ERC-721
Overview
Max Total Supply
23,625 BDOGE
Holders
1,609
Market
Volume (24H)
N/A
Min Price (24H)
N/A
Max Price (24H)
N/A
Other Info
Token Contract
Balance
1 BDOGELoading...
Loading
Loading...
Loading
Loading...
Loading
Minimal Proxy Contract for 0x70c78d5814bd76d3947feb223a66e0f039b8aee7
Contract Name:
Crate721NTLC
Compiler Version
v0.8.23+commit.f704f362
Optimization Enabled:
Yes with 200 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
/* * SPDX-License-Identifier: UNLICENSED * * SPDX-FileType: SOURCE * * SPDX-FileCopyrightText: 2024 Johannes Krauser III <[email protected]>, JeffX <[email protected]> * * SPDX-FileContributor: Johannes Krauser III <[email protected]> */ pragma solidity 0.8.23; import {ICrate721NTLC} from "./interface/ICrate721NTLC.sol"; import {NftConfig, NftPolicy, THIS_LAUNCHPAD, THIS_PARENT, THIS_TREASURY} from "./interface/ILaunchpad.sol"; import {ERC721Crate} from "@common-resources/crate/contracts/ERC721Crate.sol"; import {TransferFailed} from "@common-resources/crate/contracts/ICore.sol"; import {MintList} from "@common-resources/crate/contracts/extensions/lists/IMintlistExt.sol"; import {ERC20 as tERC20} from "@common-resources/crate/contracts/types/tERC20.sol"; import {FixedPointMathLib as FPML} from "solady/src/utils/FixedPointMathLib.sol"; /** * @title Crate721NTLC * @author Johannes Krauser III <[email protected]> * @notice ERC721 Crate with treasury allocation */ contract Crate721NTLC is ERC721Crate, ICrate721NTLC { address public launchpad; uint16[2] public royaltyRange; address[] public feeRecipients; uint16 public numerator; uint16[] public fees; uint16[2][] public feeRanges; address public treasury; address public splitter; address public parent; uint16 public minAllocation; uint16 public maxAllocation; modifier onlyFactory() { if (msg.sender != launchpad) revert NotFactory(); _; } constructor() payable { _disableInitializers(); } function initialize( address owner_, address launchpad_, NftPolicy calldata policy_, NftConfig calldata config_ ) external payable virtual initializer { _initialize(config_.name, config_.symbol, config_.maxSupply, config_.royalty, owner_, config_.price); launchpad = launchpad_; parent = config_.parent; royaltyRange = policy_.royalty; feeRecipients = policy_.feeRecipients; feeRanges = policy_.feeRanges; _setFees(config_.fees, 0, 0); // emit ParentUpdate(parent); emit LaunchpadUpdate(launchpad_); } // >>>>>>>>>>>> [ INTERNAL FUNCTIONS ] <<<<<<<<<<<< function _validateRange(uint16 min_, uint16 max_, uint16 value_) internal pure { if (value_ < min_ || value_ > max_) revert InvalidRange(); } // >>>>>>>>>>>> [ MINT LOGIC ] <<<<<<<<<<<< function _handlePayments(uint256 allocation_) internal virtual { uint256 value = msg.value; if (value == 0) return; uint256 length = feeRecipients.length; uint256 fee; for (uint256 i = 0; i < length; ++i) { address recipient = feeRecipients[i]; if (recipient == THIS_TREASURY) { recipient = treasury; fee = FPML.fullMulDivUp(allocation_, value, _DENOMINATOR_BPS); } else { if (recipient == THIS_LAUNCHPAD) { recipient = launchpad; } else if (recipient == THIS_PARENT) { recipient = parent; } fee = FPML.fullMulDivUp(fees[i], value, _DENOMINATOR_BPS); } if (recipient != address(0) && fee > 0) recipient.call{value: fee}(""); } } function _handleMint(address recipient_, uint256 amount_, address referral_) internal virtual override { _handlePayments(minAllocation); ERC721Crate._handleMint(recipient_, amount_, referral_); } function _handleMintWithList( bytes32[] calldata proof_, uint8 listId_, address recipient_, uint32 amount_, address referral_ ) internal virtual override { _handlePayments(minAllocation); ERC721Crate._handleMintWithList(proof_, listId_, recipient_, amount_, referral_); } // Standard mint function that supports batch minting and custom allocation function mint(address recipient_, uint256 amount_, uint16 allocation_) public payable virtual { if (allocation_ < minAllocation || allocation_ > maxAllocation) revert AllocationOutOfBounds(); _handlePayments(allocation_); ERC721Crate._handleMint(recipient_, amount_, address(0)); } // Standard batch mint with custom allocation support and referral fee support function mint(address recipient_, uint256 amount_, address referral_, uint16 allocation_) public payable virtual { if (allocation_ < minAllocation || allocation_ > maxAllocation) revert AllocationOutOfBounds(); _handlePayments(allocation_); ERC721Crate._handleMint(recipient_, amount_, referral_); } // >>>>>>>>>>>> [ PERMISSIONED / OWNER FUNCTIONS ] <<<<<<<<<<<< function updateSetup(address treasury_, address splitter_) public payable onlyFactory { treasury = treasury_; splitter = splitter_; _setRoyalties(splitter, defaultRoyalty); } /** * @inheritdoc ERC721Crate * @notice Override to account for allocation when setting a percentage going to the referral */ function setReferralFee(uint16 bps_) external virtual override onlyOwner { if (bps_ > (_DENOMINATOR_BPS - numerator)) revert MaxReferral(); _setReferralFee(bps_); } function _setFees(uint16[] calldata fees_, uint16 minAllocation_, uint16 maxAllocation_) internal virtual { uint16 _numerator; uint256 length = feeRecipients.length; for (uint256 i = 0; i < length; ++i) { uint16 fee = fees_[i]; uint16 min = feeRanges[i][0]; uint16 max = feeRanges[i][1]; address recipient = feeRecipients[i]; if (recipient == THIS_TREASURY) { minAllocation_ = minAllocation_ > 0 ? minAllocation_ : fee; maxAllocation_ = maxAllocation_ > 0 ? maxAllocation_ : fee; _validateRange(min, max, minAllocation_); _validateRange(min, max, maxAllocation_); _numerator += maxAllocation_; } else { _validateRange(min, max, fee); _numerator += fee; } } if (_numerator > _DENOMINATOR_BPS - referralFee) revert InvalidRange(); minAllocation = minAllocation_; maxAllocation = maxAllocation_; numerator = _numerator; fees = fees_; emit TreasuryUpdate(minAllocation_, maxAllocation_); emit FeeUpdate(feeRecipients, fees_); } function setFees( uint16[] calldata fees_, uint16 minAllocation_, uint16 maxAllocation_ ) external virtual onlyOwner { if (minAllocation_ > maxAllocation_) revert InvalidRange(); if (minAllocation_ < minAllocation && _totalSupply > 0) revert AllocationOutOfBounds(); _setFees(fees_, minAllocation_, maxAllocation_); } function setRoyalties(address recipient_, uint96 bps_) external virtual override onlyOwner { _validateRange(royaltyRange[0], royaltyRange[1], uint16(bps_)); recipient_ = splitter == address(0) ? recipient_ : splitter; _setRoyalties(recipient_, bps_); } function setTokenRoyalties(uint256 tokenId_, address recipient_, uint96 bps_) external virtual override onlyOwner { _validateRange(royaltyRange[0], royaltyRange[1], uint16(bps_)); recipient_ = splitter == address(0) ? recipient_ : splitter; _setTokenRoyalties(tokenId_, recipient_, bps_); } function setLists(uint8[] calldata ids_, MintList[] calldata lists_) external virtual onlyOwner { uint256 length = ids_.length; if (length != lists_.length) revert InvalidLength(); for (uint256 i = 0; i < length; ++i) { _setList( ids_[i], lists_[i].root, lists_[i].price, lists_[i].userSupply, lists_[i].maxSupply, lists_[i].start, lists_[i].end, lists_[i].unit, lists_[i].reserved, lists_[i].paused, maxSupply ); } } // Withdraw non-allocated mint funds function withdraw(address recipient, uint256 amount) public virtual override nonReentrant { _withdraw(owner() == address(0) && treasury != address(0) ? treasury : recipient, amount); } // >>>>>>>>>>>> [ ASSET HANDLING ] <<<<<<<<<<<< // Internal handling for receive() and fallback() to reduce code length function _processPayment() internal virtual override { bool mintedOut = (_totalSupply + _reservedSupply) == maxSupply; if (mintedOut) { uint256 value = msg.value; if (value != 0 && treasury != address(0)) { // Calculate allocation and split payment accordingly if (owner() != address(0)) value = FPML.fullMulDivUp(minAllocation, value, _DENOMINATOR_BPS); payable(treasury).call{value: value}(""); } return; } if (paused()) revert EnforcedPause(); mint(msg.sender, (msg.value / price)); } /* maybe useless override since any nft that uses this contract uses onERC721Received by default */ function rescueERC721(address token_, address recipient_, uint256 tokenId_) public virtual override onlyOwner { if (token_ == address(this) && treasury != address(0)) recipient_ = treasury; _sendERC721(token_, recipient_, tokenId_); } function onERC721Received(address, address, uint256 tokenId, bytes calldata) external virtual returns (bytes4) { if (msg.sender != address(this) || treasury == address(0)) revert NoTreasury(); _sendERC721(address(this), treasury, tokenId); return Crate721NTLC.onERC721Received.selector; } }
/* * SPDX-License-Identifier: NOASSERTION * * SPDX-FileType: SOURCE * * SPDX-FileCopyrightText: 2024 Johannes Krauser III <[email protected]> * * SPDX-FileContributor: Johannes Krauser III <[email protected]> */ pragma solidity 0.8.23; import {NftConfig, NftPolicy} from "./ILaunchpad.sol"; interface ICrate721NTLC { // >>>>>>>>>>>> [ ERRORS ] <<<<<<<<<<<< error AllocationOutOfBounds(); error AllocationOverflow(); error NotFactory(); error NoTreasury(); error TreasuryExists(); error InvalidRange(); error InvalidLength(); // >>>>>>>>>>>> [ EVENTS ] <<<<<<<<<<<< event LaunchpadUpdate(address indexed launchpad_); event TreasuryUpdate(uint256 minAllocation, uint256 maxAllocation); event FeeUpdate(address[] feeRecipients_, uint16[] fees_); function initialize( address owner_, address launchpad_, NftPolicy calldata policy_, NftConfig calldata config_ ) external payable; function updateSetup(address treasury_, address splitter_) external payable; function parent() external view returns (address); }
/* * SPDX-License-Identifier: NOASSERTION * * SPDX-FileType: SOURCE * * SPDX-FileCopyrightText: 2024 Johannes Krauser III <[email protected]> * * SPDX-FileContributor: Johannes Krauser III <[email protected]> */ pragma solidity 0.8.23; address constant THIS_LAUNCHPAD = 0x0000000000000000000000000000000000000001; address constant THIS_CONTRACT = 0x0000000000000000000000000000000000000002; address constant THIS_TREASURY = 0x0000000000000000000000000000000000000003; address constant THIS_PARENT = 0x0000000000000000000000000000000000000004; address constant FEE_SPLITTER = 0x0000000000000000000000000000000000000005; struct NftPolicy { address masterCopy; bytes32 merkleRoot; address[] feeRecipients; uint16[2][] feeRanges; // [launchpadFee, treasuryFee, (optional: parentFee), ...others] uint16[2] royalty; uint256 deployFee; bool paused; } struct NftConfig { uint64 policyId; bytes32 salt; string name; string symbol; uint16[] fees; uint16 royalty; uint32 maxSupply; uint256 price; bytes32[] proof; address parent; } struct TreasuryPolicy { address masterCopy; bytes32 merkleRoot; address[] feeRecipients; uint16[2][] feeRanges; uint16[2] royalty; uint16[2] interestRate; uint256[2] termLimit; bool paused; } struct TreasuryConfig { uint64 policyId; address collection; address parent; address collateral; uint16 royalty; uint16 interestRate; uint256 termLimit; uint16[] fees; } struct SplitterPolicy { address masterCopy; bytes32 merkleRoot; address[] feeRecipients; uint16[2][] feeRanges; bool paused; } struct SplitterConfig { uint64 policyId; address collection; address parent; address collateral; address treasury; uint16[] fees; } interface ILaunchpad { event NftPolicyUpdate(uint64 indexed id_, NftPolicy config_); event TreasuryPolicyUpdate(uint64 indexed id_, TreasuryPolicy config_); event SplitterPolicyUpdate(uint64 indexed id_, SplitterPolicy config_); /// EVENTS /// /// @notice Emitted after collection has been created /// @param creator_ Address of creator of contract /// @param collection_ Address of the collection contract /// @param salt_ Salt used to create the collection contract event CollectionCreated( address indexed creator_, address indexed collection_, bytes32 indexed salt_, uint64 policyId_ ); /// @notice Emitted after Treasury has been created /// @param creator_ Address of creator of contract /// @param collection_ Address of the collection contract /// @param treasury_ Address of the treasury contract event TreasuryCreated( address indexed creator_, address indexed collection_, address indexed treasury_, uint64 policyId_ ); /// @notice Emitted after Splitter has been created /// @param creator_ Address of creator of contract /// @param collection_ Address of the collection contract /// @param splitter_ Address of the splitter contract event SplitterCreated( address indexed creator_, address indexed collection_, address indexed splitter_, uint64 policyId_ ); event ApprovedCreatorUpdate(address indexed wallet_, bool status_); /// ERRORS /// error ConfigUnknown(uint256 id_); /// @notice Error for if address is zero error AddressIsZero(); /// @notice Error for if address is already set error AddressAlreadySet(); error InsufficientFunds(); /// @notice Error for if invalid fee percent error InvalidFeePercent(); /// @notice Trying to set treasury for an invalid collection error InvalidCollection(); error InvalidPolicy(); error InvalidConfig(); error TreasuryExists(); error SplitterExists(); function getNftPolicy(uint64 id_) external view returns (NftPolicy memory policy_); function getTreasuryPolicy(uint64 id_) external view returns (TreasuryPolicy memory policy_); function getSplitterPolicy(uint64 id_) external view returns (SplitterPolicy memory policy_); /* function getNftConfig(uint256 id_) external view returns (NftPolicy memory policy_, NftConfig memory config_); function getTreasuryConfig(uint256 id_) external view returns (TreasuryPolicy memory policy_, TreasuryConfig memory config_); function getSplitterConfig(uint256 id_) external view returns (SplitterPolicy memory policy_, SplitterConfig memory config_); */ }
/* * SPDX-License-Identifier: NOASSERTION * * SPDX-FileType: SOURCE * * SPDX-FileCopyrightText: 2024 Johannes Krauser III <[email protected]>, Zodomo <[email protected]> * * SPDX-FileContributor: Zodomo <[email protected]> * SPDX-FileContributor: Johannes Krauser III <[email protected]> */ pragma solidity 0.8.23; // >>>>>>>>>>>> [ IMPORTS ] <<<<<<<<<<<< import {Core} from "./Core.sol"; import {CoreMetadata721} from "./metadata/CoreMetadata721.sol"; import {BlacklistExt} from "./extensions/blacklist/BlacklistExt.sol"; import {MintlistExt} from "./extensions/lists/MintlistExt.sol"; import {LockableExt} from "./extensions/lockable/LockableExt.sol"; import {ReferralExt} from "./extensions/referral/ReferralExt.sol"; import {RoyaltyExt} from "./extensions/royalty/RoyaltyExt.sol"; import {NotZero} from "./ICore.sol"; import {Initializable} from "solady/src/utils/Initializable.sol"; /** * @title ERC721Crate * @author Zodomo.eth (Farcaster/Telegram/Discord/Github: @zodomo, X: @0xZodomo, Email: [email protected]) * @author 0xKrauser (Discord/Github/X: @0xKrauser, Email: [email protected]) * @notice ERC721 template that contains launchpad friendly features to be inherited by other contracts * @custom:github https://github.com/common-resources/crate */ contract ERC721Crate is Initializable, CoreMetadata721, BlacklistExt, MintlistExt, ReferralExt, RoyaltyExt { function _canMint(uint256 amount_) internal view override returns (uint32 tokenAmount_) { tokenAmount_ = Core._canMint(amount_); _requireUnreservedSupply(tokenAmount_, _totalSupply, maxSupply); } // >>>>>>>>>>>> [ CONSTRUCTION / INITIALIZATION ] <<<<<<<<<<<< /// @dev Constructor is kept empty in order to make the template compatible with ERC-1167 proxy factories constructor() payable { _disableInitializers(); } /** * @dev Initializes the contract with the given basic parameters. * should be called immediately after deployment, ideally by factory * @param name_ The name of the collection. e.g. "Milady Maker" * @param symbol_ The symbol of the collection. e.g. "MILADY" * @param maxSupply_ The maximum supply of tokens that can be minted. (~1.099T max) * @param royalty_ The percentage of the mint value that is paid to the referrer. (420 == 420 / 10000 == 4.20%) * @param owner_ The owner of the collection contract. * @param price_ The price of minting a single token. (~1.2M ETH max) */ function initialize( string memory name_, string memory symbol_, uint32 maxSupply_, uint16 royalty_, address owner_, uint256 price_ ) external payable virtual initializer { _initialize(name_, symbol_, maxSupply_, royalty_, owner_, price_); } /** * @dev Internal function to initialize the contract with the given parameters. * allows contracts that inherit ERC721Core to call this function in their own initializers * @param name_ The name of the collection. e.g. "Milady Maker" * @param symbol_ The symbol of the collection. e.g. "MILADY" * @param maxSupply_ The maximum supply of tokens that can be minted. (~1.099T max) * @param royalty_ The percentage of the mint value that is paid to the referrer. * e.g. (420 == 420 / 10000 == 4.20%) Max 10.00% * @param owner_ The owner of the collection contract. * @param price_ The price of minting a single token. (~1.2M ETH max) */ function _initialize( string memory name_, // Collection name ("Milady") string memory symbol_, // Collection symbol ("MIL") uint32 maxSupply_, // Max supply (~1.099T max) uint16 royalty_, // Percentage in basis points (420 == 4.20%) address owner_, // Collection contract owner uint256 price_ // Price (~1.2M ETH max) ) internal virtual onlyInitializing { _pause(); _claimed[address(0)] = 0; _initializeOwner(owner_); _setRoyalties(owner_, royalty_); _name = name_; _symbol = symbol_; price = price_; maxSupply = maxSupply_; userSupply = maxSupply_; unit = 1; start = 0; end = 0; emit ContractCreated(name_, symbol_); emit SupplyUpdate(maxSupply_); emit PriceUpdate(price_); } // >>>>>>>>>>>> [ VIEW / METADATA FUNCTIONS ] <<<<<<<<<<<< function supportsInterface(bytes4 interfaceId_) public view virtual override(RoyaltyExt, CoreMetadata721) returns (bool supported_) { return RoyaltyExt.supportsInterface(interfaceId_) || CoreMetadata721.supportsInterface(interfaceId_); } // >>>>>>>>>>>> [ INTERNAL FUNCTIONS ] <<<<<<<<<<<< /** * @notice Internal mint function for mints that do not require list logic. * @dev Implements referral logic. * @param recipient_ The address of the recipient. * @param amount_ The amount_ of tokens to mint. * @param referral_ The address of the referrer. */ function _handleMint(address recipient_, uint256 amount_, address referral_) internal virtual nonReentrant { uint32 tokenAmount = _canMint(amount_); _handleReferral(referral_, recipient_); unchecked { _claimed[msg.sender] += tokenAmount; } // Process ERC721 mints emit Minted(msg.sender, tokenAmount); _mintBatch(recipient_, tokenAmount); } /** * @notice Internal mint function that supports batch minting. * @dev Implements blacklist logic. * @param recipient_ The address of the recipient. * @param amount_ The amount of tokens to mint. */ function _mintBatch(address recipient_, uint32 amount_) internal virtual { //@TODO if recipient is not msg.sender consider emitting another event // Prevent bad inputs if (recipient_ == address(0) || amount_ == 0) revert NotZero(); // Ensure minter and recipient don't hold blacklisted assets _enforceBlacklist(recipient_); unchecked { uint256 supply = _totalSupply; for (uint256 i; i < amount_;) { super._mint(recipient_, ++supply); ++i; } _totalSupply += amount_; } } /** * @notice Internal mint function for mints that do not require list logic. * @dev Implements referral fee logic. * @param recipient_ The address of the recipient. * @param amount_ The amount_ of tokens to mint. * @param referral_ The address of the referrer. */ function _handleMintWithList( bytes32[] calldata proof_, uint8 listId_, address recipient_, uint32 amount_, address referral_ ) internal virtual nonReentrant { _handleReferral(referral_, recipient_); (uint256 cost, uint32 tokenAmount, bool reserved) = _canMintList(proof_, listId_, amount_); if (msg.value < cost) revert InsufficientPayment(); if (_totalSupply + tokenAmount > maxSupply) revert MaxSupply(); // If the reserved supply of the list is deactivated if (!reserved) _requireUnreservedSupply(tokenAmount, _totalSupply, maxSupply); emit ListMinted(msg.sender, listId_, tokenAmount); _mintBatch(recipient_, tokenAmount); } /** * @notice Mint a single token to the sender. * @dev Standard single-unit mint to msg.sender (implemented for max scannner compatibility) */ function mint() public payable virtual { _handleMint(msg.sender, 1, address(0)); } /** * @notice Mint the amount of tokens to the sender, provided that enough ether is sent. * @dev Standard multi-unit mint to msg.sender (implemented for max scanner compatibility) * @param amount_ The amount of tokens to mint. */ function mint(uint256 amount_) public payable virtual { _handleMint(msg.sender, amount_, address(0)); } /** * @notice Mint the amount of tokens to the recipient, provided that enough ether is sent. * @dev Standard mint function with recipient that supports batch minting * @param recipient_ The address of the recipient. * @param amount_ The amount of tokens to mint. */ function mint(address recipient_, uint256 amount_) public payable virtual { _handleMint(recipient_, amount_, address(0)); } /** * @notice Mint the amount of tokens to the recipient, provided that enough ether is sent * Sends a percentage of the mint value to the referrer. * @dev Standard batch mint with referral fee support * @param recipient_ The address of the recipient. * @param amount_ The amount of tokens to mint. * @param referral_ The address of the referrer. */ function mint(address recipient_, uint256 amount_, address referral_) public payable virtual { _handleMint(recipient_, amount_, referral_); } /** * @notice Mint the amount of tokens to the recipient, provided that enough ether is sent * @dev Batch mint with referral fee support and list merkle proof support. * @param proof_ List of ordered hashes used to reconstruct the Merkel root. * @param listId_ The id of the list to check against for permission. * @param recipient_ The address of the recipient. * @param amount_ The amount of tokens to mint. * @param referral_ The address of the referrer. */ function mint( bytes32[] calldata proof_, uint8 listId_, address recipient_, uint32 amount_, address referral_ ) public payable virtual { _handleMintWithList(proof_, listId_, recipient_, amount_, referral_); } // >>>>>>>>>>>> [ PERMISSIONED / OWNER FUNCTIONS ] <<<<<<<<<<<< /// @dev Override to account for the lists reserved supply. function setSupply(uint32 maxSupply_) external virtual override onlyOwner { if (maxSupply_ < _totalSupply + _reservedSupply) revert UnderSupply(); _setMaxSupply(maxSupply_); } // >>>>>>>>>>>> [ Lists ] <<<<<<<<<<<< function setList( uint256 price_, uint8 listId_, bytes32 root_, uint32 userSupply_, uint32 maxSupply_, uint32 start_, uint32 end_, uint32 unit_, bool reserved_, bool paused_ ) external virtual onlyOwner { _setList(listId_, root_, price_, userSupply_, maxSupply_, start_, end_, unit_, reserved_, paused_, maxSupply); } function pauseList(uint8 listId_) external virtual onlyOwner { _pauseList(listId_, true); } function unpauseList(uint8 listId_) external virtual onlyOwner { _pauseList(listId_, false); } function deleteList(uint8 listId_) external virtual onlyOwner { _deleteList(listId_); } // >>>>>>>>>>>> [ Royalty ] <<<<<<<<<<<< function setRoyalties(address recipient_, uint96 bps_) external virtual onlyOwner { _requireRoyaltiesUnfrozen(); _requireRoyaltiesEnabled(); _setRoyalties(recipient_, bps_); } function setTokenRoyalties(uint256 tokenId_, address recipient_, uint96 bps_) external virtual onlyOwner { _requireRoyaltiesUnfrozen(); _requireRoyaltiesEnabled(); _setTokenRoyalties(tokenId_, recipient_, bps_); } function freezeRoyalties() external virtual onlyOwner { _freezeRoyalties(); } function disableRoyalties() external virtual onlyOwner { _disableRoyalties(); } // >>>>>>>>>>>> [ Blacklist ] <<<<<<<<<<<< function setBlacklist(address[] calldata assets_, bool status_) external virtual onlyOwner { _setBlacklist(assets_, status_); } // >>>>>>>>>>>> [ Referral ] <<<<<<<<<<<< function setReferralFee(uint16 bps_) external virtual onlyOwner { _setReferralFee(bps_); } // >>>>>>>>>>>> [ ASSET HANDLING ] <<<<<<<<<<<< function _processPayment() internal virtual override { bool mintedOut = (_totalSupply + _reservedSupply) == maxSupply; if (mintedOut) { return Core._processPayment(); } if (paused()) revert EnforcedPause(); mint(msg.sender, (msg.value / price)); } }
/* * SPDX-License-Identifier: NOASSERTION * * SPDX-FileType: SOURCE * * SPDX-FileCopyrightText: 2024 Johannes Krauser III <[email protected]>, Zodomo <[email protected]> * * SPDX-FileContributor: Zodomo <[email protected]> * SPDX-FileContributor: Johannes Krauser III <[email protected]> */ pragma solidity 0.8.23; /// @dev Input alue is zero, not allowed error NotZero(); /// @dev Transfer of ether to an address failed. error TransferFailed(); /** * @title ICore * @author 0xKrauser (Discord/Github/X: @0xKrauser, Email: [email protected]) * @notice Interface to integrate Core and require missing external functions. * @custom:github https://github.com/common-resources/crate */ interface ICore { // >>>>>>>>>>>> [ ERRORS ] <<<<<<<<<<<< /// @dev End timestamp needs to be either 0 (no end) or greater than start time error TimestampEnd(); /// @dev Cannot mint with a msg.value lower than the price for one token. error InsufficientPayment(); /// @dev The minting process for this list has ended or not started yet. error TimeOutOfBounds(); /// @dev maxSupply has been reached. error MaxSupply(); /// @dev msg.sender/recipient has already claimed the maximum allocation. error ClaimSupply(); /// @dev After at least one mint, the maxSupply cannot be increased. error CannotIncreaseMaxSupplyAfterMint(); /// @dev Cannot set maxSupply to a value lower than the current supply. error UnderSupply(); // >>>>>>>>>>>> [ EVENTS ] <<<<<<<<<<<< /** * @dev Emitted when a new nft contract is created. * @param name_ The name of the nft contract. * @param symbol_ The symbol of the nft contract. * @custom:unique */ event ContractCreated(string name_, string symbol_); /** * @dev Emitted when the price is updated. * @param price_ The new price. */ event PriceUpdate(uint256 price_); /** * @dev Emitted when the supply is updated. * @param supply_ The new supply. */ event SupplyUpdate(uint256 supply_); /** * @notice Emitted when the minting range is updated. * @param start_ The new start timestamp of the public mint. * @param end_ The new end timestamp of the public mint. */ event MintPeriodUpdate(uint256 start_, uint256 end_); /** * @dev Emitted when the maximum claimable tokens per user are updated. * @param claimable_ The new amount of tokens available per address. */ event UserClaimableUpdate(uint256 claimable_); /** * @dev Emitted when the default unit amount of tokens minted is updated. * @param unit_ The new amount of tokens you mint for every 1 you pay. */ event UnitUpdate(uint256 unit_); /** * @dev Emitted when the contract owner withdraws funds. * * @param to_ The address to which the funds are withdrawn. * @param amount_ The amount of funds withdrawn. */ event Withdraw(address indexed to_, uint256 amount_); /** * @notice Emitted when a new mint is executed. * @dev Needed in order to keep track of the claimed supply. * @param minter_ The address of the minter. * @param amount_ The amount of tokens minted. */ event Minted(address indexed minter_, uint256 amount_); }
/* * SPDX-License-Identifier: NOASSERTION * * SPDX-FileType: SOURCE * * SPDX-FileCopyrightText: 2024 Johannes Krauser III <[email protected]>, Zodomo <[email protected]> * * SPDX-FileContributor: Johannes Krauser III <[email protected]> */ pragma solidity 0.8.23; /** * @dev Represents a custom mint configuration. * * This struct contains the following fields: * @param root The root hash of the merkle tree. * @param price The price of each token. * @param unit The number of units that makeup a token. * @param userSupply The max number of tokens that each user can mint. * @param maxSupply The max number of tokens that can be minted by the lsit. * @param start The Unix epoch at which the list starts. * @param end The Unix epoch at which the list ends. * @param reserved Set aside the maxSupply of this token as reserved. * @param paused Pause the list and prevent new mints. */ struct MintList { bytes32 root; uint256 price; uint32 unit; uint32 userSupply; uint32 maxSupply; uint32 start; uint32 end; bool reserved; bool paused; } /** * @title IMintlistExt * @author 0xKrauser (Discord/Github/X: @0xKrauser, Email: [email protected]) * @notice Interface to integrate MintlistExt and require missing external functions. * @custom:github https://github.com/common-resources/crate */ interface IMintlistExt { /** * @dev Emitted when someone mints from a list. * @param minter_ The address of the minter. * @param listId_ The ID of the custom mint list. * @param amount_ The amount of tokens minted. */ event ListMinted(address indexed minter_, uint8 indexed listId_, uint256 amount_); /** * @dev Emitted when a custom mint list is disabled. * @param listId_ The ID of the custom mint list. * @param paused_ The paused status of the custom mint list. */ event MintListStatus(uint8 indexed listId_, bool paused_); /** * @dev Emitted when a custom mint list is deleted. * @param listId_ The ID of the custom mint list. * @custom:unique */ event MintListDeleted(uint8 indexed listId_); /** * @dev Emitted when a custom mint list is configured. * @param listId_ The ID of the custom mint list. * @param list_ The configuration of the list. */ event MintListUpdate(uint8 indexed listId_, MintList list_); /// @dev List is deleted error ListDeleted(); /// @dev List does not exist error ListUnknown(); /// @dev Max supply is less than the current minted supply of the list error SupplyUnderflow(); // @TODO error ReservedMaxSupply(); /// @dev End timestamp needs to be either 0 (no end) or greater than start time error ListTimestampEnd(); /// @dev List is paused error ListPaused(); /// @dev Can not mint if the amount trespasses the supply reserved for the list(s) error ReservedSupply(); /// @dev List max supply has been reached error ListMaxSupply(); /// @dev msg.sender/recipient has already claimed the maximum allocation for a given MintList. error ListClaimSupply(); /// @dev msg.sender/recipient is not eligible to claim any mint allocation for a given MintList. error NotEligible(); /// @dev The minting process for this list has ended or not started yet. error ListTimeOutOfBounds(); function listIndex() external view returns (uint8); function getList(uint8 listId_) external view returns (MintList memory); function listClaimedOf(uint8 listId_, address wallet_) external view returns (uint256 claimed_); function listSupply(uint8 listId_) external view returns (uint32); /** * @notice Configures a custom mint list with a merkle root, mintable amount, and price. * @dev If list already exists, adjusts the configuration to the new values. * @param listId_ The ID of the custom mint list. */ function setList( uint256 price_, uint8 listId_, bytes32 root_, uint32 userSupply_, uint32 maxSupply_, uint32 start_, uint32 end_, uint32 unit_, bool reserved_, bool paused_ ) external; /** * @notice Pauses a custom mint list. * @param listId_ The ID of the custom mint list. */ function pauseList(uint8 listId_) external; /** * @notice Unpauses a custom mint list. * @param listId_ The ID of the custom mint list. */ function unpauseList(uint8 listId_) external; /** * @notice Deletes a custom mint list. * @param listId_ The ID of the custom mint list. */ function deleteList(uint8 listId_) external; }
/*
* SPDX-License-Identifier: AGPL-3.0-only
*
* SPDX-FileType: SOURCE
*
* SPDX-FileCopyrightText: 2024 Riley <https://github.com/jtriley-eth>
*
* SPDX-FileContributor: Riley <https://github.com/jtriley-eth>
*/
pragma solidity 0.8.23;
// ## ERC20 Type Wrapper
//
// This type wraps the address primitive type and contains functions to call the core ERC20 interface
// without allocating new memory. Functions perform returndata validation.
//
// All external calls that fail will revert internally. This is to simplify the API.
type ERC20 is address;
using {
totalSupply,
balanceOf,
allowance,
transfer,
transferFrom,
approve,
// -- operators
eq as ==,
neq as !=,
gt as >,
gte as >=,
lt as <,
lte as <=,
add as +,
sub as -,
mul as *,
div as /,
mod as %,
and as &,
or as |,
xor as ^,
not as ~
} for ERC20 global;
// -------------------------------------------------------------------------------------------------
// Query ERC20.totalSupply without allocating new memory.
//
// Procedures:
// 01. store totalSupply selector in memory
// 02. staticcall totalSupply; cache as ok
// 03. check that the return value is 32 bytes; compose with ok
// 04. if ok is false, revert
// 05. assign returned value to output
function totalSupply(ERC20 erc20) view returns (uint256 output) {
assembly ("memory-safe") {
mstore(0x00, 0x18160ddd00000000000000000000000000000000000000000000000000000000)
let ok := staticcall(gas(), erc20, 0x00, 0x04, 0x00, 0x20)
ok := and(ok, eq(returndatasize(), 0x20))
if iszero(ok) { revert(0x00, 0x00) }
output := mload(0x00)
}
}
// -------------------------------------------------------------------------------------------------
// Query ERC20.balanceOf without allocating new memory.
//
// Procedures:
// 01. store balanceOf selector in memory
// 02. store owner address in memory
// 03. staticcall balanceOf; cache as ok
// 04. check that the return value is 32 bytes; compose with ok
// 05. if ok is false, revert
// 06. assign returned value to output
function balanceOf(ERC20 erc20, address owner) view returns (uint256 output) {
assembly ("memory-safe") {
mstore(0x00, 0x70a0823100000000000000000000000000000000000000000000000000000000)
mstore(0x04, owner)
let ok := staticcall(gas(), erc20, 0x00, 0x24, 0x00, 0x20)
ok := and(ok, eq(returndatasize(), 0x20))
if iszero(ok) { revert(0x00, 0x00) }
output := mload(0x00)
}
}
// -------------------------------------------------------------------------------------------------
// Query ERC20.allowance without allocating new memory.
//
// Procedures:
// 01. store allowance selector in memory
// 02. store owner address in memory
// 03. store spender address in memory
// 04. staticcall allowance; cache as ok
// 05. check that the return value is 32 bytes; compose with ok
// 06. if ok is false, revert
// 07. restore the upper bits of the free memory pointer to zero
function allowance(ERC20 erc20, address owner, address spender) view returns (uint256 output) {
assembly {
mstore(0x00, 0xdd62ed3e00000000000000000000000000000000000000000000000000000000)
mstore(0x04, owner)
mstore(0x24, spender)
let ok := staticcall(gas(), erc20, 0x00, 0x44, 0x00, 0x20)
ok := and(ok, eq(returndatasize(), 0x20))
if iszero(ok) { revert(0x00, 0x00) }
mstore(0x24, 0x00)
output := mload(0x00)
}
}
// -------------------------------------------------------------------------------------------------
// Call ERC20.transfer without allocating new memory.
//
// Procedures:
// 01. store transfer selector in memory
// 02. store recipient address in memory
// 03. store amount in memory
// 04. call transfer; cache as ok
// 05. check that either no data was returned or the data is 32 bytes and true; compose with ok
// 06. revert if ok is false
// 07. restore the upper bits of the free memory pointer to zero
function transfer(ERC20 erc20, address receiver, uint256 amount) {
assembly {
mstore(0x00, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(0x04, receiver)
mstore(0x24, amount)
let ok := call(gas(), erc20, 0x00, 0x00, 0x44, 0x00, 0x20)
ok := and(ok, or(iszero(returndatasize()), and(eq(returndatasize(), 0x20), mload(0x00))))
if iszero(ok) { revert(0x00, 0x00) }
mstore(0x24, 0x00)
}
}
// -------------------------------------------------------------------------------------------------
// Call ERC20.transferFrom without allocating new memory.
//
// Procedures:
// 01. load the free memory pointer; cache as fmp
// 02. store transferFrom selector in memory
// 03. store sender address in memory
// 04. store recipient address in memory
// 05. store amount in memory
// 06. call transferFrom; cache as ok
// 07. check that either no data was returned or the data is 32 bytes and true; compose with ok
// 08. revert if ok is false
// 09. restore the free memory pointer to fmp
// 10. restore the zero slot to zero
function transferFrom(ERC20 erc20, address sender, address receiver, uint256 amount) {
assembly {
let fmp := mload(0x40)
mstore(0x00, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(0x04, sender)
mstore(0x24, receiver)
mstore(0x44, amount)
let ok := call(gas(), erc20, 0x00, 0x00, 0x64, 0x00, 0x20)
ok := and(ok, or(iszero(returndatasize()), and(eq(returndatasize(), 0x20), mload(0x00))))
if iszero(ok) { revert(0x00, 0x00) }
mstore(0x40, fmp)
mstore(0x60, 0x00)
}
}
// -------------------------------------------------------------------------------------------------
// Call ERC20.approve without allocating new memory.
//
// Procedures:
// 01. store approve selector in memory
// 02. store spender address in memory
// 03. store amount in memory
// 04. call approve; cache as ok
// 05. check that either no data was returned or the data is 32 bytes and true; compose with ok
// 06. revert if ok is false
// 07. restore the upper bits of the free memory pointer to zero
function approve(ERC20 erc20, address spender, uint256 amount) {
assembly {
mstore(0x00, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(0x04, spender)
mstore(0x24, amount)
let ok := call(gas(), erc20, 0x00, 0x00, 0x44, 0x00, 0x20)
ok := and(ok, or(iszero(returndatasize()), and(eq(returndatasize(), 0x20), mload(0x00))))
if iszero(ok) { revert(0x00, 0x00) }
mstore(0x24, 0x00)
}
}
// -------------------------------------------------------------------------------------------------
// Returns `true` if the two ERC20 instances are equal, `false` otherwise.
function eq(ERC20 lhs, ERC20 rhs) pure returns (bool output) {
assembly {
output := eq(lhs, rhs)
}
}
// -------------------------------------------------------------------------------------------------
// Returns `true` if the two ERC20 instances are not equal, `false` otherwise.
function neq(ERC20 lhs, ERC20 rhs) pure returns (bool output) {
assembly {
output := iszero(eq(lhs, rhs))
}
}
// -------------------------------------------------------------------------------------------------
// Returns `true` if `lhs` is greater than `rhs`, `false` otherwise.
function gt(ERC20 lhs, ERC20 rhs) pure returns (bool output) {
assembly {
output := gt(lhs, rhs)
}
}
// -------------------------------------------------------------------------------------------------
// Returns `true` if `lhs` is greater than or equal to `rhs`, `false` otherwise.
function gte(ERC20 lhs, ERC20 rhs) pure returns (bool output) {
assembly {
output := iszero(lt(lhs, rhs))
}
}
// -------------------------------------------------------------------------------------------------
// Returns `true` if `lhs` is less than `rhs`, `false` otherwise.
function lt(ERC20 lhs, ERC20 rhs) pure returns (bool output) {
assembly {
output := lt(lhs, rhs)
}
}
// -------------------------------------------------------------------------------------------------
// Returns `true` if `lhs` is less than or equal to `rhs`, `false` otherwise.
function lte(ERC20 lhs, ERC20 rhs) pure returns (bool output) {
assembly {
output := iszero(gt(lhs, rhs))
}
}
// -------------------------------------------------------------------------------------------------
// Returns the sum of two ERC20 instances.
function add(ERC20 lhs, ERC20 rhs) pure returns (ERC20 output) {
assembly {
output := add(lhs, rhs)
if gt(output, 0xffffffffffffffffffffffffffffffffffffffff) { revert(0x00, 0x00) }
}
}
// -------------------------------------------------------------------------------------------------
// Returns the difference of two ERC20 instances.
function sub(ERC20 lhs, ERC20 rhs) pure returns (ERC20 output) {
assembly {
output := sub(lhs, rhs)
if gt(output, 0xffffffffffffffffffffffffffffffffffffffff) { revert(0x00, 0x00) }
}
}
// -------------------------------------------------------------------------------------------------
// Returns the product of two ERC20 instances.
function mul(ERC20 lhs, ERC20 rhs) pure returns (ERC20 output) {
assembly {
if lhs {
output := and(mul(lhs, rhs), 0xffffffffffffffffffffffffffffffffffffffff)
if iszero(eq(div(output, lhs), rhs)) { revert(0x00, 0x00) }
}
}
}
// -------------------------------------------------------------------------------------------------
// Returns the division of two ERC20 instances.
function div(ERC20 lhs, ERC20 rhs) pure returns (ERC20 output) {
assembly {
if iszero(rhs) { revert(0x00, 0x00) }
output := div(lhs, rhs)
}
}
// -------------------------------------------------------------------------------------------------
// Returns the modulus of two ERC20 instances.
function mod(ERC20 lhs, ERC20 rhs) pure returns (ERC20 output) {
assembly {
if iszero(rhs) { revert(0x00, 0x00) }
output := mod(lhs, rhs)
}
}
// -------------------------------------------------------------------------------------------------
// Returns the bitwise AND of two ERC20 instances.
function and(ERC20 lhs, ERC20 rhs) pure returns (ERC20 output) {
assembly {
output := and(lhs, rhs)
}
}
// -------------------------------------------------------------------------------------------------
// Returns the bitwise OR of two ERC20 instances.
function or(ERC20 lhs, ERC20 rhs) pure returns (ERC20 output) {
assembly {
output := or(lhs, rhs)
}
}
// -------------------------------------------------------------------------------------------------
// Returns the bitwise XOR of two ERC20 instances.
function xor(ERC20 lhs, ERC20 rhs) pure returns (ERC20 output) {
assembly {
output := xor(lhs, rhs)
}
}
// -------------------------------------------------------------------------------------------------
// Returns the bitwise NOT of an ERC20 instance.
function not(ERC20 lhs) pure returns (ERC20 output) {
assembly {
output := and(not(lhs), 0xffffffffffffffffffffffffffffffffffffffff)
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
library FixedPointMathLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The operation failed, as the output exceeds the maximum value of uint256.
error ExpOverflow();
/// @dev The operation failed, as the output exceeds the maximum value of uint256.
error FactorialOverflow();
/// @dev The operation failed, due to an overflow.
error RPowOverflow();
/// @dev The mantissa is too big to fit.
error MantissaOverflow();
/// @dev The operation failed, due to an multiplication overflow.
error MulWadFailed();
/// @dev The operation failed, due to an multiplication overflow.
error SMulWadFailed();
/// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
error DivWadFailed();
/// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
error SDivWadFailed();
/// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
error MulDivFailed();
/// @dev The division failed, as the denominator is zero.
error DivFailed();
/// @dev The full precision multiply-divide operation failed, either due
/// to the result being larger than 256 bits, or a division by a zero.
error FullMulDivFailed();
/// @dev The output is undefined, as the input is less-than-or-equal to zero.
error LnWadUndefined();
/// @dev The input outside the acceptable domain.
error OutOfDomain();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The scalar of ETH and most ERC20s.
uint256 internal constant WAD = 1e18;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* SIMPLIFIED FIXED POINT OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Equivalent to `(x * y) / WAD` rounded down.
function mulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
if gt(x, div(not(0), y)) {
if y {
mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
revert(0x1c, 0x04)
}
}
z := div(mul(x, y), WAD)
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded down.
function sMulWad(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
// Equivalent to `require((x == 0 || z / x == y) && !(x == -1 && y == type(int256).min))`.
if iszero(gt(or(iszero(x), eq(sdiv(z, x), y)), lt(not(x), eq(y, shl(255, 1))))) {
mstore(0x00, 0xedcd4dd4) // `SMulWadFailed()`.
revert(0x1c, 0x04)
}
z := sdiv(z, WAD)
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks.
function rawMulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := div(mul(x, y), WAD)
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks.
function rawSMulWad(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := sdiv(mul(x, y), WAD)
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded up.
function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
// Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
if iszero(eq(div(z, y), x)) {
if y {
mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
revert(0x1c, 0x04)
}
}
z := add(iszero(iszero(mod(z, WAD))), div(z, WAD))
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded up, but without overflow checks.
function rawMulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), WAD))
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded down.
function divWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `require(y != 0 && x <= type(uint256).max / WAD)`.
if iszero(mul(y, lt(x, add(1, div(not(0), WAD))))) {
mstore(0x00, 0x7c5f487d) // `DivWadFailed()`.
revert(0x1c, 0x04)
}
z := div(mul(x, WAD), y)
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded down.
function sDivWad(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, WAD)
// Equivalent to `require(y != 0 && ((x * WAD) / WAD == x))`.
if iszero(mul(y, eq(sdiv(z, WAD), x))) {
mstore(0x00, 0x5c43740d) // `SDivWadFailed()`.
revert(0x1c, 0x04)
}
z := sdiv(z, y)
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks.
function rawDivWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := div(mul(x, WAD), y)
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks.
function rawSDivWad(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := sdiv(mul(x, WAD), y)
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded up.
function divWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `require(y != 0 && x <= type(uint256).max / WAD)`.
if iszero(mul(y, lt(x, add(1, div(not(0), WAD))))) {
mstore(0x00, 0x7c5f487d) // `DivWadFailed()`.
revert(0x1c, 0x04)
}
z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded up, but without overflow and divide by zero checks.
function rawDivWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
}
}
/// @dev Equivalent to `x` to the power of `y`.
/// because `x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)`.
/// Note: This function is an approximation.
function powWad(int256 x, int256 y) internal pure returns (int256) {
// Using `ln(x)` means `x` must be greater than 0.
return expWad((lnWad(x) * y) / int256(WAD));
}
/// @dev Returns `exp(x)`, denominated in `WAD`.
/// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
/// Note: This function is an approximation. Monotonically increasing.
function expWad(int256 x) internal pure returns (int256 r) {
unchecked {
// When the result is less than 0.5 we return zero.
// This happens when `x <= (log(1e-18) * 1e18) ~ -4.15e19`.
if (x <= -41446531673892822313) return r;
/// @solidity memory-safe-assembly
assembly {
// When the result is greater than `(2**255 - 1) / 1e18` we can not represent it as
// an int. This happens when `x >= floor(log((2**255 - 1) / 1e18) * 1e18) ≈ 135`.
if iszero(slt(x, 135305999368893231589)) {
mstore(0x00, 0xa37bfec9) // `ExpOverflow()`.
revert(0x1c, 0x04)
}
}
// `x` is now in the range `(-42, 136) * 1e18`. Convert to `(-42, 136) * 2**96`
// for more intermediate precision and a binary basis. This base conversion
// is a multiplication by 1e18 / 2**96 = 5**18 / 2**78.
x = (x << 78) / 5 ** 18;
// Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers
// of two such that exp(x) = exp(x') * 2**k, where k is an integer.
// Solving this gives k = round(x / log(2)) and x' = x - k * log(2).
int256 k = ((x << 96) / 54916777467707473351141471128 + 2 ** 95) >> 96;
x = x - k * 54916777467707473351141471128;
// `k` is in the range `[-61, 195]`.
// Evaluate using a (6, 7)-term rational approximation.
// `p` is made monic, we'll multiply by a scale factor later.
int256 y = x + 1346386616545796478920950773328;
y = ((y * x) >> 96) + 57155421227552351082224309758442;
int256 p = y + x - 94201549194550492254356042504812;
p = ((p * y) >> 96) + 28719021644029726153956944680412240;
p = p * x + (4385272521454847904659076985693276 << 96);
// We leave `p` in `2**192` basis so we don't need to scale it back up for the division.
int256 q = x - 2855989394907223263936484059900;
q = ((q * x) >> 96) + 50020603652535783019961831881945;
q = ((q * x) >> 96) - 533845033583426703283633433725380;
q = ((q * x) >> 96) + 3604857256930695427073651918091429;
q = ((q * x) >> 96) - 14423608567350463180887372962807573;
q = ((q * x) >> 96) + 26449188498355588339934803723976023;
/// @solidity memory-safe-assembly
assembly {
// Div in assembly because solidity adds a zero check despite the unchecked.
// The q polynomial won't have zeros in the domain as all its roots are complex.
// No scaling is necessary because p is already `2**96` too large.
r := sdiv(p, q)
}
// r should be in the range `(0.09, 0.25) * 2**96`.
// We now need to multiply r by:
// - The scale factor `s ≈ 6.031367120`.
// - The `2**k` factor from the range reduction.
// - The `1e18 / 2**96` factor for base conversion.
// We do this all at once, with an intermediate result in `2**213`
// basis, so the final right shift is always by a positive amount.
r = int256(
(uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k)
);
}
}
/// @dev Returns `ln(x)`, denominated in `WAD`.
/// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
/// Note: This function is an approximation. Monotonically increasing.
function lnWad(int256 x) internal pure returns (int256 r) {
/// @solidity memory-safe-assembly
assembly {
// We want to convert `x` from `10**18` fixed point to `2**96` fixed point.
// We do this by multiplying by `2**96 / 10**18`. But since
// `ln(x * C) = ln(x) + ln(C)`, we can simply do nothing here
// and add `ln(2**96 / 10**18)` at the end.
// Compute `k = log2(x) - 96`, `r = 159 - k = 255 - log2(x) = 255 ^ log2(x)`.
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, 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))))
// We place the check here for more optimal stack operations.
if iszero(sgt(x, 0)) {
mstore(0x00, 0x1615e638) // `LnWadUndefined()`.
revert(0x1c, 0x04)
}
// forgefmt: disable-next-item
r := xor(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff))
// Reduce range of x to (1, 2) * 2**96
// ln(2^k * x) = k * ln(2) + ln(x)
x := shr(159, shl(r, x))
// Evaluate using a (8, 8)-term rational approximation.
// `p` is made monic, we will multiply by a scale factor later.
// forgefmt: disable-next-item
let p := sub( // This heavily nested expression is to avoid stack-too-deep for via-ir.
sar(96, mul(add(43456485725739037958740375743393,
sar(96, mul(add(24828157081833163892658089445524,
sar(96, mul(add(3273285459638523848632254066296,
x), x))), x))), x)), 11111509109440967052023855526967)
p := sub(sar(96, mul(p, x)), 45023709667254063763336534515857)
p := sub(sar(96, mul(p, x)), 14706773417378608786704636184526)
p := sub(mul(p, x), shl(96, 795164235651350426258249787498))
// We leave `p` in `2**192` basis so we don't need to scale it back up for the division.
// `q` is monic by convention.
let q := add(5573035233440673466300451813936, x)
q := add(71694874799317883764090561454958, sar(96, mul(x, q)))
q := add(283447036172924575727196451306956, sar(96, mul(x, q)))
q := add(401686690394027663651624208769553, sar(96, mul(x, q)))
q := add(204048457590392012362485061816622, sar(96, mul(x, q)))
q := add(31853899698501571402653359427138, sar(96, mul(x, q)))
q := add(909429971244387300277376558375, sar(96, mul(x, q)))
// `p / q` is in the range `(0, 0.125) * 2**96`.
// Finalization, we need to:
// - Multiply by the scale factor `s = 5.549…`.
// - Add `ln(2**96 / 10**18)`.
// - Add `k * ln(2)`.
// - Multiply by `10**18 / 2**96 = 5**18 >> 78`.
// The q polynomial is known not to have zeros in the domain.
// No scaling required because p is already `2**96` too large.
p := sdiv(p, q)
// Multiply by the scaling factor: `s * 5**18 * 2**96`, base is now `5**18 * 2**192`.
p := mul(1677202110996718588342820967067443963516166, p)
// Add `ln(2) * k * 5**18 * 2**192`.
// forgefmt: disable-next-item
p := add(mul(16597577552685614221487285958193947469193820559219878177908093499208371, sub(159, r)), p)
// Add `ln(2**96 / 10**18) * 5**18 * 2**192`.
p := add(600920179829731861736702779321621459595472258049074101567377883020018308, p)
// Base conversion: mul `2**18 / 2**192`.
r := sar(174, p)
}
}
/// @dev Returns `W_0(x)`, denominated in `WAD`.
/// See: https://en.wikipedia.org/wiki/Lambert_W_function
/// a.k.a. Product log function. This is an approximation of the principal branch.
/// Note: This function is an approximation. Monotonically increasing.
function lambertW0Wad(int256 x) internal pure returns (int256 w) {
// forgefmt: disable-next-item
unchecked {
if ((w = x) <= -367879441171442322) revert OutOfDomain(); // `x` less than `-1/e`.
(int256 wad, int256 p) = (int256(WAD), x);
uint256 c; // Whether we need to avoid catastrophic cancellation.
uint256 i = 4; // Number of iterations.
if (w <= 0x1ffffffffffff) {
if (-0x4000000000000 <= w) {
i = 1; // Inputs near zero only take one step to converge.
} else if (w <= -0x3ffffffffffffff) {
i = 32; // Inputs near `-1/e` take very long to converge.
}
} else if (uint256(w >> 63) == uint256(0)) {
/// @solidity memory-safe-assembly
assembly {
// Inline log2 for more performance, since the range is small.
let v := shr(49, w)
let l := shl(3, lt(0xff, v))
l := add(or(l, byte(and(0x1f, shr(shr(l, v), 0x8421084210842108cc6318c6db6d54be)),
0x0706060506020504060203020504030106050205030304010505030400000000)), 49)
w := sdiv(shl(l, 7), byte(sub(l, 31), 0x0303030303030303040506080c13))
c := gt(l, 60)
i := add(2, add(gt(l, 53), c))
}
} else {
int256 ll = lnWad(w = lnWad(w));
/// @solidity memory-safe-assembly
assembly {
// `w = ln(x) - ln(ln(x)) + b * ln(ln(x)) / ln(x)`.
w := add(sdiv(mul(ll, 1023715080943847266), w), sub(w, ll))
i := add(3, iszero(shr(68, x)))
c := iszero(shr(143, x))
}
if (c == uint256(0)) {
do { // If `x` is big, use Newton's so that intermediate values won't overflow.
int256 e = expWad(w);
/// @solidity memory-safe-assembly
assembly {
let t := mul(w, div(e, wad))
w := sub(w, sdiv(sub(t, x), div(add(e, t), wad)))
}
if (p <= w) break;
p = w;
} while (--i != uint256(0));
/// @solidity memory-safe-assembly
assembly {
w := sub(w, sgt(w, 2))
}
return w;
}
}
do { // Otherwise, use Halley's for faster convergence.
int256 e = expWad(w);
/// @solidity memory-safe-assembly
assembly {
let t := add(w, wad)
let s := sub(mul(w, e), mul(x, wad))
w := sub(w, sdiv(mul(s, wad), sub(mul(e, t), sdiv(mul(add(t, wad), s), add(t, t)))))
}
if (p <= w) break;
p = w;
} while (--i != c);
/// @solidity memory-safe-assembly
assembly {
w := sub(w, sgt(w, 2))
}
// For certain ranges of `x`, we'll use the quadratic-rate recursive formula of
// R. Iacono and J.P. Boyd for the last iteration, to avoid catastrophic cancellation.
if (c == uint256(0)) return w;
int256 t = w | 1;
/// @solidity memory-safe-assembly
assembly {
x := sdiv(mul(x, wad), t)
}
x = (t * (wad + lnWad(x)));
/// @solidity memory-safe-assembly
assembly {
w := sdiv(x, add(wad, t))
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* GENERAL NUMBER UTILITIES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Calculates `floor(x * y / d)` with full precision.
/// Throws if result overflows a uint256 or when `d` is zero.
/// Credit to Remco Bloemen under MIT license: https://2π.com/21/muldiv
function fullMulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
// 512-bit multiply `[p1 p0] = x * y`.
// Compute the product mod `2**256` and mod `2**256 - 1`
// then use the Chinese Remainder Theorem to reconstruct
// the 512 bit result. The result is stored in two 256
// variables such that `product = p1 * 2**256 + p0`.
// Temporarily use `result` as `p0` to save gas.
result := mul(x, y) // Lower 256 bits of `x * y`.
for {} 1 {} {
// If overflows.
if iszero(mul(or(iszero(x), eq(div(result, x), y)), d)) {
let mm := mulmod(x, y, not(0))
let p1 := sub(mm, add(result, lt(mm, result))) // Upper 256 bits of `x * y`.
/*------------------- 512 by 256 division --------------------*/
// Make division exact by subtracting the remainder from `[p1 p0]`.
let r := mulmod(x, y, d) // Compute remainder using mulmod.
let t := and(d, sub(0, d)) // The least significant bit of `d`. `t >= 1`.
// Make sure the result is less than `2**256`. Also prevents `d == 0`.
// Placing the check here seems to give more optimal stack operations.
if iszero(gt(d, p1)) {
mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
revert(0x1c, 0x04)
}
d := div(d, t) // Divide `d` by `t`, which is a power of two.
// Invert `d mod 2**256`
// Now that `d` is an odd number, it has an inverse
// modulo `2**256` such that `d * inv = 1 mod 2**256`.
// Compute the inverse by starting with a seed that is correct
// correct for four bits. That is, `d * inv = 1 mod 2**4`.
let inv := xor(2, mul(3, d))
// Now use Newton-Raphson iteration to improve the precision.
// Thanks to Hensel's lifting lemma, this also works in modular
// arithmetic, doubling the correct bits in each step.
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**8
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**16
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**32
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**64
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**128
result :=
mul(
// Divide [p1 p0] by the factors of two.
// Shift in bits from `p1` into `p0`. For this we need
// to flip `t` such that it is `2**256 / t`.
or(
mul(sub(p1, gt(r, result)), add(div(sub(0, t), t), 1)),
div(sub(result, r), t)
),
mul(sub(2, mul(d, inv)), inv) // inverse mod 2**256
)
break
}
result := div(result, d)
break
}
}
}
/// @dev Calculates `floor(x * y / d)` with full precision.
/// Behavior is undefined if `d` is zero or the final result cannot fit in 256 bits.
/// Performs the full 512 bit calculation regardless.
function fullMulDivUnchecked(uint256 x, uint256 y, uint256 d)
internal
pure
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
result := mul(x, y)
let mm := mulmod(x, y, not(0))
let p1 := sub(mm, add(result, lt(mm, result)))
let t := and(d, sub(0, d))
let r := mulmod(x, y, d)
d := div(d, t)
let inv := xor(2, mul(3, d))
inv := mul(inv, sub(2, mul(d, inv)))
inv := mul(inv, sub(2, mul(d, inv)))
inv := mul(inv, sub(2, mul(d, inv)))
inv := mul(inv, sub(2, mul(d, inv)))
inv := mul(inv, sub(2, mul(d, inv)))
result :=
mul(
or(mul(sub(p1, gt(r, result)), add(div(sub(0, t), t), 1)), div(sub(result, r), t)),
mul(sub(2, mul(d, inv)), inv)
)
}
}
/// @dev Calculates `floor(x * y / d)` with full precision, rounded up.
/// Throws if result overflows a uint256 or when `d` is zero.
/// Credit to Uniswap-v3-core under MIT license:
/// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FullMath.sol
function fullMulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 result) {
result = fullMulDiv(x, y, d);
/// @solidity memory-safe-assembly
assembly {
if mulmod(x, y, d) {
result := add(result, 1)
if iszero(result) {
mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
revert(0x1c, 0x04)
}
}
}
}
/// @dev Returns `floor(x * y / d)`.
/// Reverts if `x * y` overflows, or `d` is zero.
function mulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
// Equivalent to `require(d != 0 && (y == 0 || x <= type(uint256).max / y))`.
if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
mstore(0x00, 0xad251c27) // `MulDivFailed()`.
revert(0x1c, 0x04)
}
z := div(z, d)
}
}
/// @dev Returns `ceil(x * y / d)`.
/// Reverts if `x * y` overflows, or `d` is zero.
function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(x, y)
// Equivalent to `require(d != 0 && (y == 0 || x <= type(uint256).max / y))`.
if iszero(mul(or(iszero(x), eq(div(z, x), y)), d)) {
mstore(0x00, 0xad251c27) // `MulDivFailed()`.
revert(0x1c, 0x04)
}
z := add(iszero(iszero(mod(z, d))), div(z, d))
}
}
/// @dev Returns `ceil(x / d)`.
/// Reverts if `d` is zero.
function divUp(uint256 x, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
if iszero(d) {
mstore(0x00, 0x65244e4e) // `DivFailed()`.
revert(0x1c, 0x04)
}
z := add(iszero(iszero(mod(x, d))), div(x, d))
}
}
/// @dev Returns `max(0, x - y)`.
function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(gt(x, y), sub(x, y))
}
}
/// @dev Returns `condition ? x : y`, without branching.
function ternary(bool condition, uint256 x, uint256 y) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
result := xor(x, mul(xor(x, y), iszero(condition)))
}
}
/// @dev Exponentiate `x` to `y` by squaring, denominated in base `b`.
/// Reverts if the computation overflows.
function rpow(uint256 x, uint256 y, uint256 b) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(b, iszero(y)) // `0 ** 0 = 1`. Otherwise, `0 ** n = 0`.
if x {
z := xor(b, mul(xor(b, x), and(y, 1))) // `z = isEven(y) ? scale : x`
let half := shr(1, b) // Divide `b` by 2.
// Divide `y` by 2 every iteration.
for { y := shr(1, y) } y { y := shr(1, y) } {
let xx := mul(x, x) // Store x squared.
let xxRound := add(xx, half) // Round to the nearest number.
// Revert if `xx + half` overflowed, or if `x ** 2` overflows.
if or(lt(xxRound, xx), shr(128, x)) {
mstore(0x00, 0x49f7642b) // `RPowOverflow()`.
revert(0x1c, 0x04)
}
x := div(xxRound, b) // Set `x` to scaled `xxRound`.
// If `y` is odd:
if and(y, 1) {
let zx := mul(z, x) // Compute `z * x`.
let zxRound := add(zx, half) // Round to the nearest number.
// If `z * x` overflowed or `zx + half` overflowed:
if or(xor(div(zx, x), z), lt(zxRound, zx)) {
// Revert if `x` is non-zero.
if x {
mstore(0x00, 0x49f7642b) // `RPowOverflow()`.
revert(0x1c, 0x04)
}
}
z := div(zxRound, b) // Return properly scaled `zxRound`.
}
}
}
}
}
/// @dev Returns the square root of `x`, rounded down.
function sqrt(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// `floor(sqrt(2**15)) = 181`. `sqrt(2**15) - 181 = 2.84`.
z := 181 // The "correct" value is 1, but this saves a multiplication later.
// This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
// start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
// Let `y = x / 2**r`. We check `y >= 2**(k + 8)`
// but shift right by `k` bits to ensure that if `x >= 256`, then `y >= 256`.
let r := shl(7, lt(0xffffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffffff, shr(r, x))))
z := shl(shr(1, r), z)
// Goal was to get `z*z*y` within a small factor of `x`. More iterations could
// get y in a tighter range. Currently, we will have y in `[256, 256*(2**16))`.
// We ensured `y >= 256` so that the relative difference between `y` and `y+1` is small.
// That's not possible if `x < 256` but we can just verify those cases exhaustively.
// Now, `z*z*y <= x < z*z*(y+1)`, and `y <= 2**(16+8)`, and either `y >= 256`, or `x < 256`.
// Correctness can be checked exhaustively for `x < 256`, so we assume `y >= 256`.
// Then `z*sqrt(y)` is within `sqrt(257)/sqrt(256)` of `sqrt(x)`, or about 20bps.
// For `s` in the range `[1/256, 256]`, the estimate `f(s) = (181/1024) * (s+1)`
// is in the range `(1/2.84 * sqrt(s), 2.84 * sqrt(s))`,
// with largest error when `s = 1` and when `s = 256` or `1/256`.
// Since `y` is in `[256, 256*(2**16))`, let `a = y/65536`, so that `a` is in `[1/256, 256)`.
// Then we can estimate `sqrt(y)` using
// `sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2**18`.
// There is no overflow risk here since `y < 2**136` after the first branch above.
z := shr(18, mul(z, add(shr(r, x), 65536))) // A `mul()` is saved from starting `z` at 181.
// Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
// If `x+1` is a perfect square, the Babylonian method cycles between
// `floor(sqrt(x))` and `ceil(sqrt(x))`. This statement ensures we return floor.
// See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
z := sub(z, lt(div(x, z), z))
}
}
/// @dev Returns the cube root of `x`, rounded down.
/// Credit to bout3fiddy and pcaversaccio under AGPLv3 license:
/// https://github.com/pcaversaccio/snekmate/blob/main/src/utils/Math.vy
/// Formally verified by xuwinnie:
/// https://github.com/vectorized/solady/blob/main/audits/xuwinnie-solady-cbrt-proof.pdf
function cbrt(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
let r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, 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))))
// Makeshift lookup table to nudge the approximate log2 result.
z := div(shl(div(r, 3), shl(lt(0xf, shr(r, x)), 0xf)), xor(7, mod(r, 3)))
// Newton-Raphson's.
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
// Round down.
z := sub(z, lt(div(x, mul(z, z)), z))
}
}
/// @dev Returns the square root of `x`, denominated in `WAD`, rounded down.
function sqrtWad(uint256 x) internal pure returns (uint256 z) {
unchecked {
if (x <= type(uint256).max / 10 ** 18) return sqrt(x * 10 ** 18);
z = (1 + sqrt(x)) * 10 ** 9;
z = (fullMulDivUnchecked(x, 10 ** 18, z) + z) >> 1;
}
/// @solidity memory-safe-assembly
assembly {
z := sub(z, gt(999999999999999999, sub(mulmod(z, z, x), 1))) // Round down.
}
}
/// @dev Returns the cube root of `x`, denominated in `WAD`, rounded down.
/// Formally verified by xuwinnie:
/// https://github.com/vectorized/solady/blob/main/audits/xuwinnie-solady-cbrt-proof.pdf
function cbrtWad(uint256 x) internal pure returns (uint256 z) {
unchecked {
if (x <= type(uint256).max / 10 ** 36) return cbrt(x * 10 ** 36);
z = (1 + cbrt(x)) * 10 ** 12;
z = (fullMulDivUnchecked(x, 10 ** 36, z * z) + z + z) / 3;
}
/// @solidity memory-safe-assembly
assembly {
let p := x
for {} 1 {} {
if iszero(shr(229, p)) {
if iszero(shr(199, p)) {
p := mul(p, 100000000000000000) // 10 ** 17.
break
}
p := mul(p, 100000000) // 10 ** 8.
break
}
if iszero(shr(249, p)) { p := mul(p, 100) }
break
}
let t := mulmod(mul(z, z), z, p)
z := sub(z, gt(lt(t, shr(1, p)), iszero(t))) // Round down.
}
}
/// @dev Returns the factorial of `x`.
function factorial(uint256 x) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
result := 1
if iszero(lt(x, 58)) {
mstore(0x00, 0xaba0f2a2) // `FactorialOverflow()`.
revert(0x1c, 0x04)
}
for {} x { x := sub(x, 1) } { result := mul(result, x) }
}
}
/// @dev Returns the log2 of `x`.
/// Equivalent to computing the index of the most significant bit (MSB) of `x`.
/// Returns 0 if `x` is zero.
function log2(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, 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
r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
0x0706060506020504060203020504030106050205030304010505030400000000))
}
}
/// @dev Returns the log2 of `x`, rounded up.
/// Returns 0 if `x` is zero.
function log2Up(uint256 x) internal pure returns (uint256 r) {
r = log2(x);
/// @solidity memory-safe-assembly
assembly {
r := add(r, lt(shl(r, 1), x))
}
}
/// @dev Returns the log10 of `x`.
/// Returns 0 if `x` is zero.
function log10(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
if iszero(lt(x, 100000000000000000000000000000000000000)) {
x := div(x, 100000000000000000000000000000000000000)
r := 38
}
if iszero(lt(x, 100000000000000000000)) {
x := div(x, 100000000000000000000)
r := add(r, 20)
}
if iszero(lt(x, 10000000000)) {
x := div(x, 10000000000)
r := add(r, 10)
}
if iszero(lt(x, 100000)) {
x := div(x, 100000)
r := add(r, 5)
}
r := add(r, add(gt(x, 9), add(gt(x, 99), add(gt(x, 999), gt(x, 9999)))))
}
}
/// @dev Returns the log10 of `x`, rounded up.
/// Returns 0 if `x` is zero.
function log10Up(uint256 x) internal pure returns (uint256 r) {
r = log10(x);
/// @solidity memory-safe-assembly
assembly {
r := add(r, lt(exp(10, r), x))
}
}
/// @dev Returns the log256 of `x`.
/// Returns 0 if `x` is zero.
function log256(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, 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(shr(3, r), lt(0xff, shr(r, x)))
}
}
/// @dev Returns the log256 of `x`, rounded up.
/// Returns 0 if `x` is zero.
function log256Up(uint256 x) internal pure returns (uint256 r) {
r = log256(x);
/// @solidity memory-safe-assembly
assembly {
r := add(r, lt(shl(shl(3, r), 1), x))
}
}
/// @dev Returns the scientific notation format `mantissa * 10 ** exponent` of `x`.
/// Useful for compressing prices (e.g. using 25 bit mantissa and 7 bit exponent).
function sci(uint256 x) internal pure returns (uint256 mantissa, uint256 exponent) {
/// @solidity memory-safe-assembly
assembly {
mantissa := x
if mantissa {
if iszero(mod(mantissa, 1000000000000000000000000000000000)) {
mantissa := div(mantissa, 1000000000000000000000000000000000)
exponent := 33
}
if iszero(mod(mantissa, 10000000000000000000)) {
mantissa := div(mantissa, 10000000000000000000)
exponent := add(exponent, 19)
}
if iszero(mod(mantissa, 1000000000000)) {
mantissa := div(mantissa, 1000000000000)
exponent := add(exponent, 12)
}
if iszero(mod(mantissa, 1000000)) {
mantissa := div(mantissa, 1000000)
exponent := add(exponent, 6)
}
if iszero(mod(mantissa, 10000)) {
mantissa := div(mantissa, 10000)
exponent := add(exponent, 4)
}
if iszero(mod(mantissa, 100)) {
mantissa := div(mantissa, 100)
exponent := add(exponent, 2)
}
if iszero(mod(mantissa, 10)) {
mantissa := div(mantissa, 10)
exponent := add(exponent, 1)
}
}
}
}
/// @dev Convenience function for packing `x` into a smaller number using `sci`.
/// The `mantissa` will be in bits [7..255] (the upper 249 bits).
/// The `exponent` will be in bits [0..6] (the lower 7 bits).
/// Use `SafeCastLib` to safely ensure that the `packed` number is small
/// enough to fit in the desired unsigned integer type:
/// ```
/// uint32 packed = SafeCastLib.toUint32(FixedPointMathLib.packSci(777 ether));
/// ```
function packSci(uint256 x) internal pure returns (uint256 packed) {
(x, packed) = sci(x); // Reuse for `mantissa` and `exponent`.
/// @solidity memory-safe-assembly
assembly {
if shr(249, x) {
mstore(0x00, 0xce30380c) // `MantissaOverflow()`.
revert(0x1c, 0x04)
}
packed := or(shl(7, x), packed)
}
}
/// @dev Convenience function for unpacking a packed number from `packSci`.
function unpackSci(uint256 packed) internal pure returns (uint256 unpacked) {
unchecked {
unpacked = (packed >> 7) * 10 ** (packed & 0x7f);
}
}
/// @dev Returns the average of `x` and `y`. Rounds towards zero.
function avg(uint256 x, uint256 y) internal pure returns (uint256 z) {
unchecked {
z = (x & y) + ((x ^ y) >> 1);
}
}
/// @dev Returns the average of `x` and `y`. Rounds towards negative infinity.
function avg(int256 x, int256 y) internal pure returns (int256 z) {
unchecked {
z = (x >> 1) + (y >> 1) + (x & y & 1);
}
}
/// @dev Returns the absolute value of `x`.
function abs(int256 x) internal pure returns (uint256 z) {
unchecked {
z = (uint256(x) + uint256(x >> 255)) ^ uint256(x >> 255);
}
}
/// @dev Returns the absolute distance between `x` and `y`.
function dist(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := add(xor(sub(0, gt(x, y)), sub(y, x)), gt(x, y))
}
}
/// @dev Returns the absolute distance between `x` and `y`.
function dist(int256 x, int256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := add(xor(sub(0, sgt(x, y)), sub(y, x)), sgt(x, y))
}
}
/// @dev Returns the minimum of `x` and `y`.
function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), lt(y, x)))
}
}
/// @dev Returns the minimum of `x` and `y`.
function min(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), slt(y, x)))
}
}
/// @dev Returns the maximum of `x` and `y`.
function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), gt(y, x)))
}
}
/// @dev Returns the maximum of `x` and `y`.
function max(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), sgt(y, x)))
}
}
/// @dev Returns `x`, bounded to `minValue` and `maxValue`.
function clamp(uint256 x, uint256 minValue, uint256 maxValue)
internal
pure
returns (uint256 z)
{
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, minValue), gt(minValue, x)))
z := xor(z, mul(xor(z, maxValue), lt(maxValue, z)))
}
}
/// @dev Returns `x`, bounded to `minValue` and `maxValue`.
function clamp(int256 x, int256 minValue, int256 maxValue) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, minValue), sgt(minValue, x)))
z := xor(z, mul(xor(z, maxValue), slt(maxValue, z)))
}
}
/// @dev Returns greatest common divisor of `x` and `y`.
function gcd(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
for { z := x } y {} {
let t := y
y := mod(z, y)
z := t
}
}
}
/// @dev Returns `a + (b - a) * (t - begin) / (end - begin)`,
/// with `t` clamped between `begin` and `end` (inclusive).
/// Agnostic to the order of (`a`, `b`) and (`end`, `begin`).
/// If `begins == end`, returns `t <= begin ? a : b`.
function lerp(uint256 a, uint256 b, uint256 t, uint256 begin, uint256 end)
internal
pure
returns (uint256)
{
if (begin > end) (t, begin, end) = (~t, ~begin, ~end);
if (t <= begin) return a;
if (t >= end) return b;
unchecked {
if (b >= a) return a + fullMulDiv(b - a, t - begin, end - begin);
return a - fullMulDiv(a - b, t - begin, end - begin);
}
}
/// @dev Returns `a + (b - a) * (t - begin) / (end - begin)`.
/// with `t` clamped between `begin` and `end` (inclusive).
/// Agnostic to the order of (`a`, `b`) and (`end`, `begin`).
/// If `begins == end`, returns `t <= begin ? a : b`.
function lerp(int256 a, int256 b, int256 t, int256 begin, int256 end)
internal
pure
returns (int256)
{
if (begin > end) (t, begin, end) = (~t, ~begin, ~end);
if (t <= begin) return a;
if (t >= end) return b;
// forgefmt: disable-next-item
unchecked {
if (b >= a) return int256(uint256(a) + fullMulDiv(uint256(b - a),
uint256(t - begin), uint256(end - begin)));
return int256(uint256(a) - fullMulDiv(uint256(a - b),
uint256(t - begin), uint256(end - begin)));
}
}
/// @dev Returns if `x` is an even number. Some people may need this.
function isEven(uint256 x) internal pure returns (bool) {
return x & uint256(1) == uint256(0);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* RAW NUMBER OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns `x + y`, without checking for overflow.
function rawAdd(uint256 x, uint256 y) internal pure returns (uint256 z) {
unchecked {
z = x + y;
}
}
/// @dev Returns `x + y`, without checking for overflow.
function rawAdd(int256 x, int256 y) internal pure returns (int256 z) {
unchecked {
z = x + y;
}
}
/// @dev Returns `x - y`, without checking for underflow.
function rawSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
unchecked {
z = x - y;
}
}
/// @dev Returns `x - y`, without checking for underflow.
function rawSub(int256 x, int256 y) internal pure returns (int256 z) {
unchecked {
z = x - y;
}
}
/// @dev Returns `x * y`, without checking for overflow.
function rawMul(uint256 x, uint256 y) internal pure returns (uint256 z) {
unchecked {
z = x * y;
}
}
/// @dev Returns `x * y`, without checking for overflow.
function rawMul(int256 x, int256 y) internal pure returns (int256 z) {
unchecked {
z = x * y;
}
}
/// @dev Returns `x / y`, returning 0 if `y` is zero.
function rawDiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := div(x, y)
}
}
/// @dev Returns `x / y`, returning 0 if `y` is zero.
function rawSDiv(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := sdiv(x, y)
}
}
/// @dev Returns `x % y`, returning 0 if `y` is zero.
function rawMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mod(x, y)
}
}
/// @dev Returns `x % y`, returning 0 if `y` is zero.
function rawSMod(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := smod(x, y)
}
}
/// @dev Returns `(x + y) % d`, return 0 if `d` if zero.
function rawAddMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := addmod(x, y, d)
}
}
/// @dev Returns `(x * y) % d`, return 0 if `d` if zero.
function rawMulMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mulmod(x, y, d)
}
}
}/* * SPDX-License-Identifier: NOASSERTION * * SPDX-FileType: SOURCE * * SPDX-FileCopyrightText: 2024 Johannes Krauser III <[email protected]>, Zodomo <[email protected]> * * SPDX-FileContributor: Johannes Krauser III <[email protected]> */ pragma solidity 0.8.23; import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol"; import {Ownable} from "solady/src/auth/Ownable.sol"; import {ReentrancyGuard} from "solady/src/utils/ReentrancyGuard.sol"; import {ICore, NotZero, TransferFailed} from "./ICore.sol"; import {ERC20 as tERC20} from "./types/tERC20.sol"; import {ERC721 as tERC721} from "./types/tERC721.sol"; /** * @title Core * @author 0xKrauser (Discord/Github/X: @0xKrauser, Email: [email protected]) * @notice Core contract with essential internals commonly shared through the token contracts it gets inherited from. * @custom:github https://github.com/common-resources/crate */ abstract contract Core is ICore, Ownable, Pausable, ReentrancyGuard { /// @dev Price of minting a single unit. uint256 public price; // <256> {{ /// @dev Maximum supply of tokens that can be minted. uint32 public maxSupply; /// @dev Total supply of tokens minted. uint32 internal _totalSupply; /// @dev The number of tokens that can be minted by a single user. uint32 public userSupply; /// @dev The number of tokens that are minted per each mint of 1. uint24 public unit; /// @notice timestamp for the start of the minting process uint32 public start; /// @notice timestamp for the end of the minting process uint32 public end; // }} <256> mapping(address wallet_ => uint32 claimed) internal _claimed; /** * @dev Checks if mint execution respects the minting period and supply. * @param amount_ The amount to be minted. * @return tokenAmount_ The amount of tokens to minted. */ function _canMint(uint256 amount_) internal view virtual returns (uint32 tokenAmount_) { _requireNotPaused(); if (msg.value < (price * amount_)) revert InsufficientPayment(); uint256 tokenAmount = amount_ * unit; if (_totalSupply + tokenAmount > maxSupply) revert MaxSupply(); if (_claimed[msg.sender] + tokenAmount > userSupply) revert ClaimSupply(); if ((start != 0 && block.timestamp < start) || (end != 0 && block.timestamp > end)) revert TimeOutOfBounds(); // we checked against maxSupply, so we can safely cast to uint32 return uint32(tokenAmount); } /** * @dev The amount of tokens is given by unit_ * amount of mints. * @return _totalSupply The amount of tokens that have already been minted in total. */ function totalSupply() external view returns (uint256) { return _totalSupply; } /** * @dev Used to check that the userSupply limit is respected. * @param wallet_ The wallet's address to check for minted tokens. * @return tokenAmount_ The amount of tokens the user has already minted. */ function claimedOf(address wallet_) external view returns (uint256) { return _claimed[wallet_]; } /** * @notice Pause the contract from minting any more tokens. */ function pause() external onlyOwner { _pause(); } /** * @notice Unpause the contract and allow the minting of more tokens. */ function unpause() external onlyOwner { _unpause(); } /** * @notice Sets the price for minting a single token. * @param price_ The new price. */ function setPrice(uint256 price_) external virtual onlyOwner { price = price_; emit PriceUpdate(price_); } /** * @notice Sets the price for minting a single token. * @param start_ Start of the minting period in Unix time. * @param end_ End of the minting period in Unix time. */ function setMintPeriod(uint32 start_, uint32 end_) external onlyOwner { if (end_ != 0 && start_ > end_) revert TimestampEnd(); if (start == 0 && start != start_ && paused()) _unpause(); // Open minting if it wasn't already start = start_; end = end_; emit MintPeriodUpdate(start_, end_); } /** * @notice Sets the maximum amount of mints per user. * @param claimable_ New limit to the number of mints per user. */ function setClaimableUserSupply(uint32 claimable_) external virtual onlyOwner { if (claimable_ == 0) revert NotZero(); userSupply = claimable_; emit UserClaimableUpdate(claimable_); } /** * @notice Sets the number of tokens that get created per mint. * @dev The amount of tokens is given by unit_ * amount of mints. * @param unit_ Number of tokens associated with each mint. */ function setUnit(uint24 unit_) external virtual onlyOwner { if (unit_ == 0) revert NotZero(); unit = unit_; emit UnitUpdate(unit_); } /** * @notice Sets the maximum supply of tokens that can be minted. * @param maxSupply_ The new maximum supply. * If maxSupply_ is less than the current supply, the function will revert. * If minting has already started and maxSupply_ is greater than current maxSupply, the function will revert. */ function setSupply(uint32 maxSupply_) external virtual onlyOwner { if (maxSupply_ < _totalSupply) revert UnderSupply(); _setMaxSupply(maxSupply_); } function _setMaxSupply(uint32 maxSupply_) internal { if (maxSupply_ > maxSupply && _totalSupply != 0) revert MaxSupply(); maxSupply = maxSupply_; emit SupplyUpdate(maxSupply_); } function _withdraw(address recipient_, uint256 amount_) internal virtual { if (recipient_ == address(0)) revert NotZero(); // Cache owner address to save gas address owner = owner(); bool forfeit = owner == address(0); // If contract is owned and caller isn't them, revert. if (!forfeit && owner != msg.sender) revert Unauthorized(); uint256 balance = address(this).balance; // Instead of reverting for overage, simply overwrite amount with balance if (amount_ > balance || forfeit) amount_ = balance; // Process withdrawal (bool success,) = payable(recipient_).call{value: amount_}(""); if (!success) revert TransferFailed(); emit Withdraw(recipient_, amount_); } /** * @notice Withdraws funds from the contract. * @param recipient_ The address to which the funds are withdrawn. * @param amount_ The amount of funds withdrawn. */ function withdraw(address recipient_, uint256 amount_) public virtual nonReentrant { _withdraw(recipient_, amount_); } function _sendERC20(address token_, address recipient_, uint256 amount_) internal virtual { tERC20.wrap(token_).transfer(recipient_, amount_); } /** * @dev function to retrieve erc20 from the contract * @param token_ The address of the ERC20 token. * @param recipient_ The address to which the tokens are transferred. */ function rescueERC20(address token_, address recipient_) public virtual onlyOwner { uint256 balance = tERC20.wrap(token_).balanceOf(address(this)); _sendERC20(token_, recipient_, balance); } function _sendERC721(address token_, address recipient_, uint256 tokenId_) internal virtual { tERC721.wrap(token_).safeTransferFrom(address(this), recipient_, tokenId_, ""); } /** * @notice Rescue ERC721 tokens from the contract. * @param token_ The address of the ERC721 to retrieve. * @param recipient_ The address to which the token is transferred. * @param tokenId_ The ID of the token to be transferred. */ function rescueERC721(address token_, address recipient_, uint256 tokenId_) public virtual onlyOwner { _sendERC721(token_, recipient_, tokenId_); } ///@dev Internal handling of ether acquired through received(). function _processPayment() internal virtual { if (msg.sender == address(0) || msg.value == 0) revert NotZero(); (bool success,) = payable(owner()).call{value: msg.value}(""); if (!success) revert TransferFailed(); } /// @dev Fallback function to accept ether. receive() external payable virtual { _processPayment(); } }
/* * SPDX-License-Identifier: NOASSERTION * * SPDX-FileType: SOURCE * * SPDX-FileCopyrightText: 2024 Johannes Krauser III <[email protected]>, Zodomo <[email protected]> * * SPDX-FileContributor: Johannes Krauser III <[email protected]> */ pragma solidity 0.8.23; // ICoreMetadata is imported for passing to child contracts import {CoreMetadata, ICoreMetadata} from "./CoreMetadata.sol"; import {ICoreMetadata721} from "./ICoreMetadata721.sol"; import {ERC721} from "solady/src/tokens/ERC721.sol"; /** * @title CoreMetadata721 * @author 0xKrauser (Discord/Github/X: @0xKrauser, Email: [email protected]) * @notice CoreMetadata Advancement for metadata behavior specific of ERC721. * @dev ERC721 is imported at this stage as no further forks are expected to require inheriting from a different token * contract. * @custom:github https://github.com/common-resources/crate */ abstract contract CoreMetadata721 is ERC721, CoreMetadata, ICoreMetadata721 { function name() public view virtual override returns (string memory name_) { return _name; } function symbol() public view virtual override returns (string memory symbol_) { return _symbol; } /// @inheritdoc ICoreMetadata721 function tokenURI(uint256 tokenId_) public view virtual override(ICoreMetadata721, ERC721) returns (string memory tokenURI_) { if (_exists(tokenId_)) return _tokenURI(tokenId_); revert TokenDoesNotExist(); } function setContractURI(string memory contractURI_) external virtual onlyOwner { CoreMetadata._setContractURI(contractURI_); } /** * @notice Sets the token URI for a specific token. * @param tokenId_ The ID of the token. * @param tokenURI_ The new token URI. */ function setTokenURI(uint256 tokenId_, string memory tokenURI_) external virtual onlyOwner { CoreMetadata._setTokenURI(tokenId_, tokenURI_); emit URI(tokenURI_, tokenId_); emit MetadataUpdate(tokenId_); } function setBaseURI(string memory baseURI_, string memory fileExtension_) external virtual onlyOwner { CoreMetadata._setBaseURI(baseURI_, fileExtension_); emit BatchMetadataUpdate(0, maxSupply); } function freezeURI() external virtual onlyOwner { CoreMetadata._freezeURI(); } function freezeTokenURI(uint256 tokenId_) external virtual onlyOwner { CoreMetadata._freezeTokenURI(tokenId_); } function supportsInterface(bytes4 interfaceId_) public view virtual override returns (bool) { return interfaceId_ == 0x5b5e139f // ERC721Metadata || interfaceId_ == 0x49064906 // IERC4906 || ERC721.supportsInterface(interfaceId_); } }
/* * SPDX-License-Identifier: NOASSERTION * * SPDX-FileType: SOURCE * * SPDX-FileCopyrightText: 2024 Johannes Krauser III <[email protected]>, Zodomo <[email protected]> * * SPDX-FileContributor: Zodomo <[email protected]> * SPDX-FileContributor: Johannes Krauser III <[email protected]> */ pragma solidity 0.8.23; import {Asset as tAsset} from "../blacklist/Asset.sol"; import {IBlacklistExt} from "../blacklist/IBlacklistExt.sol"; import {EnumerableSetLib} from "solady/src/utils/EnumerableSetLib.sol"; /** * @title BlacklistExt * @author Zodomo (Discord/Github: Zodomo, X: @0xZodomo, Email: [email protected]) * @author 0xKrauser (Discord/Github/X: @0xKrauser, Email: [email protected]) * @notice Core Extension for managing a blacklist of assets and permission their owners. * @custom:github https://github.com/common-resources/crate */ abstract contract BlacklistExt is IBlacklistExt { using EnumerableSetLib for EnumerableSetLib.AddressSet; /// @dev Enumerable set of blacklisted asset addresses. EnumerableSetLib.AddressSet internal _blacklist; /// @inheritdoc IBlacklistExt function getBlacklist() external view virtual returns (address[] memory blacklist_) { return _blacklist.values(); } // @TODO: shouldn't blacklisted also be enforced on transfer? /** * @notice Adds or removes assets to the blacklist. * @param assets_ The list of addresses to be blacklisted. * @param status_ The status to which they have been updated. */ function _setBlacklist(address[] calldata assets_, bool status_) internal virtual { for (uint256 i; i < assets_.length; ++i) { if (status_) _blacklist.add(assets_[i]); else _blacklist.remove(assets_[i]); } emit BlacklistUpdate(assets_, status_); } /** * @dev Blacklist function to prevent mints to and from holders of prohibited assets, * applied both on minter and recipient * @param recipient_ The address of the recipient. */ function _enforceBlacklist(address recipient_) internal virtual { address[] memory blacklist = _blacklist.values(); uint256 count; for (uint256 i = 0; i < blacklist.length;) { unchecked { count += tAsset.wrap(blacklist[i]).balanceOf(msg.sender); count += tAsset.wrap(blacklist[i]).balanceOf(recipient_); if (count > 0) revert Blacklisted(); ++i; } } } }
/* * SPDX-License-Identifier: NOASSERTION * * SPDX-FileType: SOURCE * * SPDX-FileCopyrightText: 2024 Johannes Krauser III <[email protected]>, Zodomo <[email protected]> * * SPDX-FileContributor: Johannes Krauser III <[email protected]> */ pragma solidity 0.8.23; import {NotZero} from "../../ICore.sol"; import {IMintlistExt, MintList} from "./IMintlistExt.sol"; import {MerkleProofLib} from "solady/src/utils/MerkleProofLib.sol"; /** * @title MintlistExt * @author 0xKrauser (Discord/Github/X: @0xKrauser, Email: [email protected]) * @notice Core Extension for managing mintlists, handle limited offers and reserved whitelist supplies. * @custom:github https://github.com/common-resources/crate */ abstract contract MintlistExt is IMintlistExt { /// @dev Total supply of tokens reserved for the custom lists. uint32 internal _reservedSupply; /// @dev current count of custom mint lists uint8 public listIndex; /// @dev Mapping of listId to MintList data. mapping(uint8 listId_ => MintList list) public lists; /// @dev Mapping of listId to minted list supply. mapping(uint8 listId_ => uint32 supply) public listSupply; /// @dev Mapping of user to listId to already claimed amount. mapping(uint8 listId_ => mapping(address wallet_ => uint256 claimed)) internal _claimedList; function _validate( uint8 listId_, uint32 userSupply_, uint32 maxSupply_, uint32 unit_, uint32 start_, uint32 end_ ) internal view { if (listId_ > listIndex) revert ListUnknown(); if (maxSupply_ == 0 || userSupply_ == 0 || unit_ == 0) revert NotZero(); if (maxSupply_ < listSupply[listId_]) revert SupplyUnderflow(); if (end_ != 0 && start_ > end_) revert ListTimestampEnd(); } function getList(uint8 listId_) external view virtual returns (MintList memory) { return lists[listId_]; } function listClaimedOf(uint8 listId_, address wallet_) external view returns (uint256) { return _claimedList[listId_][wallet_]; } function _updateReserved( bool oldReserved_, bool reserved_, uint32 alreadyMinted_, uint32 oldListMaxSupply_, uint32 listMaxSupply_, uint32 maxSupply_ ) internal virtual { if (!oldReserved_ && !reserved_) return; unchecked { if (oldReserved_ && reserved_) { if (listMaxSupply_ == oldListMaxSupply_) return; if (listMaxSupply_ > oldListMaxSupply_) { _reservedSupply += listMaxSupply_ - oldListMaxSupply_; if (_reservedSupply > maxSupply_) revert ReservedMaxSupply(); return; } _reservedSupply -= oldListMaxSupply_ - listMaxSupply_; return; } if (reserved_) { _reservedSupply += listMaxSupply_ - alreadyMinted_; return; } _reservedSupply -= oldListMaxSupply_ - alreadyMinted_; } } function _setList( uint8 listId_, bytes32 root_, uint256 price_, uint32 userSupply_, uint32 maxSupply_, uint32 start_, uint32 end_, uint32 unit_, bool reserved_, bool paused_, uint32 contractMaxSupply_ ) internal virtual { _validate(listId_, userSupply_, maxSupply_, unit_, start_, end_); // If listId_ is 0, increment listCount and create new list // Note that since position 0 has this role, no list should be allocated in lists[0] uint8 id = listId_ == 0 ? ++listIndex : listId_; MintList storage list = lists[id]; if (listId_ != 0 && list.userSupply == 0) revert ListDeleted(); if (userSupply_ == 0) revert NotZero(); _updateReserved(list.reserved, reserved_, listSupply[id], list.maxSupply, maxSupply_, contractMaxSupply_); list.root = root_; list.userSupply = userSupply_; list.maxSupply = maxSupply_; list.unit = unit_; list.start = start_; list.end = end_; list.price = price_; list.reserved = reserved_; list.paused = paused_; emit MintListUpdate(id, list); } function _pauseList(uint8 listId_, bool paused_) internal virtual { _requireListExists(listId_); MintList storage list = lists[listId_]; if (list.userSupply == 0) revert ListDeleted(); list.paused = paused_; emit MintListUpdate(listId_, list); } function _deleteList(uint8 listId_) internal virtual { _requireListExists(listId_); MintList storage list = lists[listId_]; if (list.userSupply == 0) revert ListDeleted(); _updateReserved(list.reserved, false, listSupply[listId_], list.maxSupply, 0, 0); list.userSupply = 0; emit MintListDeleted(listId_); } function _requireListExists(uint8 listId_) internal view { if (listId_ == 0 || listId_ > listIndex) revert ListUnknown(); } function _requireUnreservedSupply(uint256 amount_, uint32 totalSupply_, uint32 maxSupply_) internal view { if (amount_ > maxSupply_ - totalSupply_ - _reservedSupply) revert ReservedSupply(); } function _canMintList( bytes32[] calldata proof_, uint8 listId_, uint32 amount_ ) internal returns (uint256 cost_, uint32 _amount_, bool reserved_) { MintList memory list = lists[listId_]; if (list.paused) revert ListPaused(); //@TODO maybe give the choice of basing the proof verification on the sender or the recipient? bytes32 leaf = keccak256(bytes.concat(keccak256(bytes.concat(abi.encode(msg.sender))))); if (list.root != bytes32(0) && !MerkleProofLib.verifyCalldata(proof_, list.root, leaf)) revert NotEligible(); _amount_ = amount_ * list.unit; // if the list has been deleted userSupply will be 0 and this will revert unchecked { _claimedList[listId_][msg.sender] += _amount_; if (_claimedList[listId_][msg.sender] > list.userSupply) revert ListClaimSupply(); listSupply[listId_] += _amount_; if (listSupply[listId_] > list.maxSupply) revert ListMaxSupply(); if (list.reserved) { _reservedSupply -= _amount_; } } if ((list.start != 0 && block.timestamp < list.start) || (list.end != 0 && block.timestamp > list.end)) { revert ListTimeOutOfBounds(); } // price is per unit so we multiply by the amount of units and not the amount of tokens cost_ = list.price * amount_; reserved_ = list.reserved; } }
/* * SPDX-License-Identifier: NOASSERTION * * SPDX-FileType: SOURCE * * SPDX-FileCopyrightText: 2024 Johannes Krauser III <[email protected]>, Zodomo <[email protected]> * * SPDX-FileContributor: Johannes Krauser III <[email protected]> */ pragma solidity 0.8.23; import {ILockableExt} from "./ILockableExt.sol"; /** * @title LockableExt * @author 0xKrauser (Discord/Github/X: @0xKrauser, Email: [email protected]) * @notice Core Extension for managing locks (e.g. staking, freezing) and emitting the appropriate events. * @custom:github https://github.com/common-resources/crate */ abstract contract LockableExt is ILockableExt { mapping(uint256 tokenId_ => address locker_) public locked; function _lock(uint256 id_) internal virtual { if (locked[id_] != address(0)) revert AlreadyLocked(); locked[id_] = msg.sender; } function _unlock(uint256 id_) internal virtual { if (locked[id_] != msg.sender) revert NotLocker(); delete locked[id_]; } }
/* * SPDX-License-Identifier: NOASSERTION * * SPDX-FileType: SOURCE * * SPDX-FileCopyrightText: 2024 Johannes Krauser III <[email protected]>, Zodomo <[email protected]> * * SPDX-FileContributor: Zodomo <[email protected]> * SPDX-FileContributor: Johannes Krauser III <[email protected]> */ pragma solidity 0.8.23; import {TransferFailed} from "../../ICore.sol"; import {IReferralExt} from "./IReferralExt.sol"; import {FixedPointMathLib as FPML} from "solady/src/utils/FixedPointMathLib.sol"; /** * @title ReferralExt * @author 0xKrauser (Discord/Github/X: @0xKrauser, Email: [email protected]) * @notice Core Extension for allowing mint referrals and reward them with a percentage of the mint price. * @custom:github https://github.com/common-resources/crate */ abstract contract ReferralExt is IReferralExt { /// @dev Denominator for basis points. Equivalent to 100% with 2 decimal places. uint16 internal constant _DENOMINATOR_BPS = 10_000; /** * @notice Percentage of the mint value that is paid to the referrer. * @dev Referral fee value in BPS. */ uint16 public referralFee; function _handleReferral(address referral_, address recipient_) internal virtual { if (msg.value > 0 && referralFee > 0 && referral_ > address(0)) { if (referral_ == msg.sender || referral_ == recipient_) revert SelfReferral(); // If referral isn't address(0) and mint isn't free, process sending referral fee // Reentrancy is handled by applying ReentrancyGuard to referral mint function // [mint(address, uint256, address)] //@TODO stash and then withdraw might be better for gas? //@TODO referral discounts? uint256 referralAlloc = FPML.mulDivUp(referralFee, msg.value, _DENOMINATOR_BPS); (bool success,) = payable(referral_).call{value: referralAlloc}(""); if (!success) revert TransferFailed(); emit Referral(referral_, msg.sender, referralAlloc); } } function _setReferralFee(uint16 bps_) internal virtual { if (bps_ > _DENOMINATOR_BPS) revert MaxReferral(); referralFee = bps_; emit ReferralFeeUpdate(bps_); } }
/* * SPDX-License-Identifier: NOASSERTION * * SPDX-FileType: SOURCE * * SPDX-FileCopyrightText: 2024 Johannes Krauser III <[email protected]>, Zodomo <[email protected]> * * SPDX-FileContributor: Zodomo <[email protected]> * SPDX-FileContributor: Johannes Krauser III <[email protected]> */ pragma solidity 0.8.23; import {NotZero} from "../../ICore.sol"; import {IRoyaltyExt} from "./IRoyaltyExt.sol"; import {ERC2981} from "solady/src/tokens/ERC2981.sol"; /** * @title RoyaltyExt * @author Zodomo (Discord/Github: Zodomo, X: @0xZodomo, Email: [email protected]) * @author 0xKrauser (Discord/Github/X: @0xKrauser, Email: [email protected]) * @notice Core Extension for signaling marketplaces the preferred royalties on a sale transaction. * @custom:github https://github.com/common-resources/crate */ abstract contract RoyaltyExt is IRoyaltyExt, ERC2981 { uint96 public defaultRoyalty; bool public royaltiesFrozen; function _requireRoyaltiesEnabled() internal view { // Revert if royalties are disabled (address receiver,) = royaltyInfo(0, 0); if (receiver == address(0)) revert DisabledRoyalties(); } function _requireRoyaltiesUnfrozen() internal view { // Revert if royalties are frozen if (royaltiesFrozen) revert FrozenRoyalties(); } function _setRoyalties(address recipient_, uint96 bps_) internal virtual { // Royalty recipient of nonexistent tokenId 0 is used as royalty status indicator, address(0) == disabled _setTokenRoyalty(0, recipient_, bps_); _setDefaultRoyalty(recipient_, bps_); defaultRoyalty = bps_; emit RoyaltiesUpdate(0, recipient_, bps_); } function _setTokenRoyalties(uint256 tokenId_, address recipient_, uint96 bps_) internal virtual { // Revert if resetting tokenId 0 as it is utilized for royalty enablement status if (tokenId_ == 0) revert NotZero(); // Reset token royalty if fee is 0, else set it if (bps_ == 0) _resetTokenRoyalty(tokenId_); else _setTokenRoyalty(tokenId_, recipient_, bps_); emit RoyaltiesUpdate(tokenId_, recipient_, bps_); } function _freezeRoyalties() internal virtual { _requireRoyaltiesEnabled(); _requireRoyaltiesUnfrozen(); royaltiesFrozen = true; emit RoyaltiesFrozen(); } function _disableRoyalties() internal virtual { _requireRoyaltiesEnabled(); _deleteDefaultRoyalty(); _resetTokenRoyalty(0); emit RoyaltiesDisabled(); } function supportsInterface(bytes4 interfaceId_) public view virtual override returns (bool) { return ERC2981.supportsInterface(interfaceId_); } }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Initializable mixin for the upgradeable contracts.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Initializable.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/proxy/utils/Initializable.sol)
abstract contract Initializable {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The contract is already initialized.
error InvalidInitialization();
/// @dev The contract is not initializing.
error NotInitializing();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Triggered when the contract has been initialized.
event Initialized(uint64 version);
/// @dev `keccak256(bytes("Initialized(uint64)"))`.
bytes32 private constant _INTIALIZED_EVENT_SIGNATURE =
0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The default initializable slot is given by:
/// `bytes32(~uint256(uint32(bytes4(keccak256("_INITIALIZABLE_SLOT")))))`.
///
/// Bits Layout:
/// - [0] `initializing`
/// - [1..64] `initializedVersion`
bytes32 private constant _INITIALIZABLE_SLOT =
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf601132;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Override to return a custom storage slot if required.
function _initializableSlot() internal pure virtual returns (bytes32) {
return _INITIALIZABLE_SLOT;
}
/// @dev Guards an initializer function so that it can be invoked at most once.
///
/// You can guard a function with `onlyInitializing` such that it can be called
/// through a function guarded with `initializer`.
///
/// This is similar to `reinitializer(1)`, except that in the context of a constructor,
/// an `initializer` guarded function can be invoked multiple times.
/// This can be useful during testing and is not expected to be used in production.
///
/// Emits an {Initialized} event.
modifier initializer() virtual {
bytes32 s = _initializableSlot();
/// @solidity memory-safe-assembly
assembly {
let i := sload(s)
// Set `initializing` to 1, `initializedVersion` to 1.
sstore(s, 3)
// If `!(initializing == 0 && initializedVersion == 0)`.
if i {
// If `!(address(this).code.length == 0 && initializedVersion == 1)`.
if iszero(lt(extcodesize(address()), eq(shr(1, i), 1))) {
mstore(0x00, 0xf92ee8a9) // `InvalidInitialization()`.
revert(0x1c, 0x04)
}
s := shl(shl(255, i), s) // Skip initializing if `initializing == 1`.
}
}
_;
/// @solidity memory-safe-assembly
assembly {
if s {
// Set `initializing` to 0, `initializedVersion` to 1.
sstore(s, 2)
// Emit the {Initialized} event.
mstore(0x20, 1)
log1(0x20, 0x20, _INTIALIZED_EVENT_SIGNATURE)
}
}
}
/// @dev Guards an reinitialzer function so that it can be invoked at most once.
///
/// You can guard a function with `onlyInitializing` such that it can be called
/// through a function guarded with `reinitializer`.
///
/// Emits an {Initialized} event.
modifier reinitializer(uint64 version) virtual {
bytes32 s = _initializableSlot();
/// @solidity memory-safe-assembly
assembly {
version := and(version, 0xffffffffffffffff) // Clean upper bits.
let i := sload(s)
// If `initializing == 1 || initializedVersion >= version`.
if iszero(lt(and(i, 1), lt(shr(1, i), version))) {
mstore(0x00, 0xf92ee8a9) // `InvalidInitialization()`.
revert(0x1c, 0x04)
}
// Set `initializing` to 1, `initializedVersion` to `version`.
sstore(s, or(1, shl(1, version)))
}
_;
/// @solidity memory-safe-assembly
assembly {
// Set `initializing` to 0, `initializedVersion` to `version`.
sstore(s, shl(1, version))
// Emit the {Initialized} event.
mstore(0x20, version)
log1(0x20, 0x20, _INTIALIZED_EVENT_SIGNATURE)
}
}
/// @dev Guards a function such that it can only be called in the scope
/// of a function guarded with `initializer` or `reinitializer`.
modifier onlyInitializing() virtual {
_checkInitializing();
_;
}
/// @dev Reverts if the contract is not initializing.
function _checkInitializing() internal view virtual {
bytes32 s = _initializableSlot();
/// @solidity memory-safe-assembly
assembly {
if iszero(and(1, sload(s))) {
mstore(0x00, 0xd7e6bcf8) // `NotInitializing()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Locks any future initializations by setting the initialized version to `2**64 - 1`.
///
/// Calling this in the constructor will prevent the contract from being initialized
/// or reinitialized. It is recommended to use this to lock implementation contracts
/// that are designed to be called through proxies.
///
/// Emits an {Initialized} event the first time it is successfully called.
function _disableInitializers() internal virtual {
bytes32 s = _initializableSlot();
/// @solidity memory-safe-assembly
assembly {
let i := sload(s)
if and(i, 1) {
mstore(0x00, 0xf92ee8a9) // `InvalidInitialization()`.
revert(0x1c, 0x04)
}
let uint64max := shr(192, s) // Computed to save bytecode.
if iszero(eq(shr(1, i), uint64max)) {
// Set `initializing` to 0, `initializedVersion` to `2**64 - 1`.
sstore(s, shl(1, uint64max))
// Emit the {Initialized} event.
mstore(0x20, uint64max)
log1(0x20, 0x20, _INTIALIZED_EVENT_SIGNATURE)
}
}
}
/// @dev Returns the highest version that has been initialized.
function _getInitializedVersion() internal view virtual returns (uint64 version) {
bytes32 s = _initializableSlot();
/// @solidity memory-safe-assembly
assembly {
version := shr(1, sload(s))
}
}
/// @dev Returns whether the contract is currently initializing.
function _isInitializing() internal view virtual returns (bool result) {
bytes32 s = _initializableSlot();
/// @solidity memory-safe-assembly
assembly {
result := and(1, sload(s))
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
bool private _paused;
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/
error EnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/
error ExpectedPause();
/**
* @dev Initializes the contract in unpaused state.
*/
constructor() {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}// 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.4;
/// @notice Reentrancy guard mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Unauthorized reentrant call.
error Reentrancy();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Equivalent to: `uint72(bytes9(keccak256("_REENTRANCY_GUARD_SLOT")))`.
/// 9 bytes is large enough to avoid collisions with lower slots,
/// but not too large to result in excessive bytecode bloat.
uint256 private constant _REENTRANCY_GUARD_SLOT = 0x929eee149b4bd21268;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* REENTRANCY GUARD */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Guards a function from reentrancy.
modifier nonReentrant() virtual {
/// @solidity memory-safe-assembly
assembly {
if eq(sload(_REENTRANCY_GUARD_SLOT), address()) {
mstore(0x00, 0xab143c06) // `Reentrancy()`.
revert(0x1c, 0x04)
}
sstore(_REENTRANCY_GUARD_SLOT, address())
}
_;
/// @solidity memory-safe-assembly
assembly {
sstore(_REENTRANCY_GUARD_SLOT, codesize())
}
}
/// @dev Guards a view function from read-only reentrancy.
modifier nonReadReentrant() virtual {
/// @solidity memory-safe-assembly
assembly {
if eq(sload(_REENTRANCY_GUARD_SLOT), address()) {
mstore(0x00, 0xab143c06) // `Reentrancy()`.
revert(0x1c, 0x04)
}
}
_;
}
}/* * SPDX-License-Identifier: AGPL-3.0-only * * SPDX-FileType: SOURCE * * SPDX-FileCopyrightText: 2024 Riley <https://github.com/jtriley-eth> * * SPDX-FileContributor: Riley <https://github.com/jtriley-eth> * SPDX-FileContributor: Johannes Krauser III <[email protected]> */ pragma solidity 0.8.23; // ## ERC721 Type Wrapper // // This type wraps the address primitive type and contains functions to call the core ERC721 interface // without allocating new memory. Functions perform returndata validation. // // All external calls that fail will revert internally. This is to simplify the API. type ERC721 is address; using { supportsInterface, totalSupply, owner, getApproved, isApprovedForAll, balanceOf, ownerOf, safeTransferFrom, transferFrom, approve, setApprovalForAll, // -- operators eq as ==, neq as !=, gt as >, gte as >=, lt as <, lte as <=, add as +, sub as -, mul as *, div as /, mod as %, and as &, or as |, xor as ^, not as ~ } for ERC721 global; // ------------------------------------------------------------------------------------------------- // Query ERC721.supportsInterface without allocating new memory. // // Procedures: // 01. right shifts interface id by 32 bits to pack with the selector // 02. store the packed supportsInterface selector and interface id in memory // 03. staticcall supportsInterface; cache as ok // 04. check that the return value is 32 bytes; compose with ok // 05. revert if ok is false // 06. assign the return value to output function supportsInterface(ERC721 erc721, bytes4 interfaceId) view returns (bool output) { assembly { interfaceId := shr(0x20, interfaceId) mstore(0x00, or(interfaceId, 0x01ffc9a700000000000000000000000000000000000000000000000000000000)) let ok := staticcall(gas(), erc721, 0x00, 0x24, 0x00, 0x20) ok := and(ok, eq(returndatasize(), 0x20)) if iszero(ok) { revert(0x00, 0x00) } output := mload(0x00) } } // ------------------------------------------------------------------------------------------------- // Query ERC20.totalSupply without allocating new memory. // // Procedures: // 01. store totalSupply selector in memory // 02. staticcall totalSupply; cache as ok // 03. check that the return value is 32 bytes; compose with ok // 04. if ok is false, revert // 05. assign returned value to output function totalSupply(ERC721 erc721) view returns (uint256 output) { assembly ("memory-safe") { mstore(0x00, 0x18160ddd00000000000000000000000000000000000000000000000000000000) let ok := staticcall(gas(), erc721, 0x00, 0x04, 0x00, 0x20) ok := and(ok, eq(returndatasize(), 0x20)) if iszero(ok) { revert(0x00, 0x00) } output := mload(0x00) } } // ------------------------------------------------------------------------------------------------- // Query ERC721.getApproved without allocating new memory. // // Procedures: // 01. store getApproved selector in memory // 02. store tokenId in memory // 03. staticcall getApproved; cache as ok // 04. check that the return value is 32 bytes; compose with ok // 05. revert if ok is false // 06. assign the return value to output function getApproved(ERC721 erc721, uint256 tokenId) view returns (address output) { assembly ("memory-safe") { mstore(0x00, 0x081812fc00000000000000000000000000000000000000000000000000000000) mstore(0x04, tokenId) let ok := staticcall(gas(), erc721, 0x00, 0x24, 0x00, 0x20) ok := and(ok, eq(returndatasize(), 0x20)) if iszero(ok) { revert(0x00, 0x00) } output := mload(0x00) } } // ------------------------------------------------------------------------------------------------- // Query ERC721.isApprovedForAll without allocating new memory. // // Procedures: // 01. store isApprovedForAll selector in memory // 02. store owner in memory // 03. store operator in memory // 04. staticcall isApprovedForAll; cache as ok // 05. check that the return value is 32 bytes; compose with ok // 06. revert if ok is false // 07. assign the return value to output // 08. restore the upper bits of the free memory pointer to zero function isApprovedForAll(ERC721 erc721, address owner, address operator) view returns (bool output) { assembly { mstore(0x00, 0xe985e9c500000000000000000000000000000000000000000000000000000000) mstore(0x04, owner) mstore(0x24, operator) let ok := staticcall(gas(), erc721, 0x00, 0x44, 0x00, 0x20) ok := and(ok, eq(returndatasize(), 0x20)) if iszero(ok) { revert(0x00, 0x00) } output := mload(0x00) mstore(0x24, 0x00) } } // ------------------------------------------------------------------------------------------------- // Query ERC721.balanceOf without allocating new memory. // // Procedures: // 01. store balanceOf selector in memory // 02. store owner in memory // 03. staticcall balanceOf; cache as ok // 04. check that the return value is 32 bytes; compose with ok // 05. revert if ok is false // 06. assign the return value to output function balanceOf(ERC721 erc721, address owner) view returns (uint256 output) { assembly ("memory-safe") { mstore(0x00, 0x70a0823100000000000000000000000000000000000000000000000000000000) mstore(0x04, owner) let ok := staticcall(gas(), erc721, 0x00, 0x24, 0x00, 0x20) ok := and(ok, eq(returndatasize(), 0x20)) if iszero(ok) { revert(0x00, 0x00) } output := mload(0x00) } } // ------------------------------------------------------------------------------------------------- // Query ERC721.owner without allocating new memory. // // Procedures: // 01. store owner selector in memory // 03. staticcall owner; cache as ok // 04. check that the return value is 32 bytes; compose with ok // 05. revert if ok is false // 06. assign the return value to output function owner(ERC721 erc721) view returns (address output) { assembly ("memory-safe") { mstore(0x00, 0x8da5cb5b00000000000000000000000000000000000000000000000000000000) let ok := staticcall(gas(), erc721, 0x00, 0x04, 0x00, 0x20) ok := and(ok, eq(returndatasize(), 0x20)) if iszero(ok) { revert(0x00, 0x00) } output := mload(0x00) } } // ------------------------------------------------------------------------------------------------- // Query ERC721.ownerOf without allocating new memory. // // Procedures: // 01. store ownerOf selector in memory // 02. store tokenId in memory // 03. staticcall ownerOf; cache as ok // 04. check that the return value is 32 bytes; compose with ok // 05. revert if ok is false // 06. assign the return value to output function ownerOf(ERC721 erc721, uint256 tokenId) view returns (address output) { assembly ("memory-safe") { mstore(0x00, 0x6352211e00000000000000000000000000000000000000000000000000000000) mstore(0x04, tokenId) let ok := staticcall(gas(), erc721, 0x00, 0x24, 0x00, 0x20) ok := and(ok, eq(returndatasize(), 0x20)) if iszero(ok) { revert(0x00, 0x00) } output := mload(0x00) } } // ------------------------------------------------------------------------------------------------- // Call ERC721.safeTransferFrom without allocating new memory. // // Procedures: // 01. load free memory pointer from memory; cache as fmp // 02. load length of data from memory; cache as dataLength // 03. pattern match the dataLength // 04. if dataLength is zero: // a. store `safeTransferFrom(address,address,uint256)` selector in memory // b. store sender in memory // c. store receiver in memory // d. store tokenId in memory // e. call safeTransferFrom; revert if it fails // f. restore free memory pointer to fmp // g. restore zero slot to zero // 05. if dataLength is nonzero: // a. store `safeTransferFrom(address,address,uint256,bytes)` selector in memory // b. store sender in memory // c. store receiver in memory // d. store tokenId in memory // e. store data offset in memory // f. copy data, including length, to memory // g. call safeTransferFrom; revert if it fails // // Notes: // - if data is empty, `safeTransferFrom(address,address,uint256)` is called // - if data is not empty, `safeTransferFrom(address,address,uint256,bytes)` is called // - if data is not empty, the call payload is constructed at the free memory pointer, but it // does not update the free memory pointer, allowing the memory to be reused. function safeTransferFrom(ERC721 erc721, address sender, address receiver, uint256 tokenId, bytes memory data) { assembly { let fmp := mload(0x40) let dataLength := mload(data) switch dataLength case 0x00 { mstore(0x00, 0x42842e0e00000000000000000000000000000000000000000000000000000000) mstore(0x04, sender) mstore(0x24, receiver) mstore(0x44, tokenId) if iszero(call(gas(), erc721, 0x00, 0x00, 0x64, 0x00, 0x00)) { revert(0x00, 0x00) } mstore(0x40, fmp) mstore(0x60, 0x00) } default { revert(0x00, 0x00) } // remove mcopy and revert for non-empty bytes data } } // ------------------------------------------------------------------------------------------------- // Call ERC721.transferFrom without allocating new memory. // // Procedures: // 01. load free memory pointer from memory; cache as fmp // 02. store transferFrom selector in memory // 03. store sender in memory // 04. store receiver in memory // 05. store tokenId in memory // 06. call transferFrom; revert if it fails // 07. restore free memory pointer to fmp // 08. restore zero slot to zero function transferFrom(ERC721 erc721, address sender, address receiver, uint256 tokenId) { assembly { let fmp := mload(0x40) mstore(0x00, 0x23b872dd00000000000000000000000000000000000000000000000000000000) mstore(0x04, sender) mstore(0x24, receiver) mstore(0x44, tokenId) if iszero(call(gas(), erc721, 0x00, 0x00, 0x64, 0x00, 0x00)) { revert(0x00, 0x00) } mstore(0x40, fmp) mstore(0x60, 0x00) } } // ------------------------------------------------------------------------------------------------- // Call ERC721.approve without allocating new memory. // // Procedures: // 01. store approve selector in memory // 02. store operator in memory // 03. store tokenId in memory // 04. call approve; revert if it fails // 05. restore upper bits of the free memory pointer to zero function approve(ERC721 erc721, address operator, uint256 tokenId) { assembly { mstore(0x00, 0x095ea7b300000000000000000000000000000000000000000000000000000000) mstore(0x04, operator) mstore(0x24, tokenId) if iszero(call(gas(), erc721, 0x00, 0x00, 0x44, 0x00, 0x00)) { revert(0x00, 0x00) } mstore(0x24, 0x00) } } // ------------------------------------------------------------------------------------------------- // Call ERC721.setApprovalForAll without allocating new memory. // // Procedures: // 01. store setApprovalForAll selector in memory // 02. store operator in memory // 03. store approved in memory // 04. call setApprovalForAll; revert if it fails // 05. restore upper bits of the free memory pointer to zero function setApprovalForAll(ERC721 erc721, address operator, bool approved) { assembly { mstore(0x00, 0xa22cb46500000000000000000000000000000000000000000000000000000000) mstore(0x04, operator) mstore(0x24, approved) if iszero(call(gas(), erc721, 0x00, 0x00, 0x44, 0x00, 0x00)) { revert(0x00, 0x00) } mstore(0x24, 0x00) } } // ------------------------------------------------------------------------------------------------- // Returns `true` if the two ERC721 instances are equal, `false` otherwise. function eq(ERC721 lhs, ERC721 rhs) pure returns (bool output) { assembly { output := eq(lhs, rhs) } } // ------------------------------------------------------------------------------------------------- // Returns `true` if the two ERC721 instances are not equal, `false` otherwise. function neq(ERC721 lhs, ERC721 rhs) pure returns (bool output) { assembly { output := iszero(eq(lhs, rhs)) } } // ------------------------------------------------------------------------------------------------- // Returns `true` if `lhs` is greater than `rhs`, `false` otherwise. function gt(ERC721 lhs, ERC721 rhs) pure returns (bool output) { assembly { output := gt(lhs, rhs) } } // ------------------------------------------------------------------------------------------------- // Returns `true` if `lhs` is greater than or equal to `rhs`, `false` otherwise. function gte(ERC721 lhs, ERC721 rhs) pure returns (bool output) { assembly { output := iszero(lt(lhs, rhs)) } } // ------------------------------------------------------------------------------------------------- // Returns `true` if `lhs` is less than `rhs`, `false` otherwise. function lt(ERC721 lhs, ERC721 rhs) pure returns (bool output) { assembly { output := lt(lhs, rhs) } } // ------------------------------------------------------------------------------------------------- // Returns `true` if `lhs` is less than or equal to `rhs`, `false` otherwise. function lte(ERC721 lhs, ERC721 rhs) pure returns (bool output) { assembly { output := iszero(gt(lhs, rhs)) } } // ------------------------------------------------------------------------------------------------- // Returns the sum of two ERC721 instances. function add(ERC721 lhs, ERC721 rhs) pure returns (ERC721 output) { assembly { output := add(lhs, rhs) if gt(output, 0xffffffffffffffffffffffffffffffffffffffff) { revert(0x00, 0x00) } } } // ------------------------------------------------------------------------------------------------- // Returns the difference of two ERC721 instances. function sub(ERC721 lhs, ERC721 rhs) pure returns (ERC721 output) { assembly { output := sub(lhs, rhs) if gt(output, 0xffffffffffffffffffffffffffffffffffffffff) { revert(0x00, 0x00) } } } // ------------------------------------------------------------------------------------------------- // Returns the product of two ERC721 instances. function mul(ERC721 lhs, ERC721 rhs) pure returns (ERC721 output) { assembly { if lhs { output := and(mul(lhs, rhs), 0xffffffffffffffffffffffffffffffffffffffff) if iszero(eq(div(output, lhs), rhs)) { revert(0x00, 0x00) } } } } // ------------------------------------------------------------------------------------------------- // Returns the division of two ERC721 instances. function div(ERC721 lhs, ERC721 rhs) pure returns (ERC721 output) { assembly { if iszero(rhs) { revert(0x00, 0x00) } output := div(lhs, rhs) } } // ------------------------------------------------------------------------------------------------- // Returns the modulus of two ERC721 instances. function mod(ERC721 lhs, ERC721 rhs) pure returns (ERC721 output) { assembly { if iszero(rhs) { revert(0x00, 0x00) } output := mod(lhs, rhs) } } // ------------------------------------------------------------------------------------------------- // Returns the bitwise AND of two ERC721 instances. function and(ERC721 lhs, ERC721 rhs) pure returns (ERC721 output) { assembly { output := and(lhs, rhs) } } // ------------------------------------------------------------------------------------------------- // Returns the bitwise OR of two ERC721 instances. function or(ERC721 lhs, ERC721 rhs) pure returns (ERC721 output) { assembly { output := or(lhs, rhs) } } // ------------------------------------------------------------------------------------------------- // Returns the bitwise XOR of two ERC721 instances. function xor(ERC721 lhs, ERC721 rhs) pure returns (ERC721 output) { assembly { output := xor(lhs, rhs) } } // ------------------------------------------------------------------------------------------------- // Returns the bitwise NOT of an ERC721 instance. function not(ERC721 lhs) pure returns (ERC721 output) { assembly { output := and(not(lhs), 0xffffffffffffffffffffffffffffffffffffffff) } }
/* * SPDX-License-Identifier: NOASSERTION * * SPDX-FileType: SOURCE * * SPDX-FileCopyrightText: 2024 Johannes Krauser III <[email protected]>, Zodomo <[email protected]> * * SPDX-FileContributor: Johannes Krauser III <[email protected]> */ pragma solidity 0.8.23; import {Core} from "../Core.sol"; import {ICoreMetadata} from "./ICoreMetadata.sol"; import {LibString} from "solady/src/utils/LibString.sol"; /** * @title CoreMetadata * @author 0xKrauser (Discord/Github/X: @0xKrauser, Email: [email protected]) * @notice Core Advancement laying out the basics of a contract integrating token uri / metadata features. * @custom:github https://github.com/common-resources/crate */ abstract contract CoreMetadata is Core, ICoreMetadata { using LibString for uint256; // Used to convert uint256 tokenId to string for tokenURI() /** * @dev Name of the collection. * e.g. "Milady Maker" * @custom:immutable */ string internal _name; /** * @dev Symbol of the collection. * e.g. "MILADY" * @custom:immutable */ string internal _symbol; /** * @dev Base URI for the collection tokenURIs. * e.g. "https://miladymaker.net/", "ipfs://QmXyZ/", "ar://QmXyZ/" */ string internal _baseURI; /// @dev Mapping of token IDs to their respective tokenURIs, optional override. mapping(uint256 tokenId_ => string uri) internal _tokenURIs; /** * @dev File extension for the collection tokenURIs. * e.g. ".json", ".jsonc", "" (empty) */ string internal _fileExtension; /** * @dev Contract URI for the collection. * e.g. "ipfs://QmXyZ/contract.json", "ar://QmXyZ/contract.json", or a stringified JSON object. * @custom:documentation https://docs.opensea.io/docs/contract-level-metadata */ string internal _contractURI; /// @dev indicates that the metadata for the entire collection is frozen and cannot be updated anymore bool public permanentURI; /// @dev Mapping of token IDs to their respective permanentURI state. mapping(uint256 tokenId_ => bool isPermanent) internal _permanentTokenURIs; /** * @notice Returns the contract metadata URI for the collection. * @return contractURI_ a string representing the contract metadata URI or a stringified JSON object. */ function contractURI() external view virtual returns (string memory contractURI_) { return _contractURI; } /** * @inheritdoc ICoreMetadata */ function baseURI() external view virtual returns (string memory) { return _baseURI; } function _tokenURI(uint256 tokenId_) internal view virtual returns (string memory) { string memory tokenURI = _tokenURIs[tokenId_]; if (bytes(tokenURI).length > 0) { return tokenURI; } string memory newBaseURI = _baseURI; return bytes(newBaseURI).length > 0 ? string(abi.encodePacked(newBaseURI, tokenId_.toString(), _fileExtension)) : ""; } /** * @notice Sets the contract metadata URI. * @dev This URI is used to store contract-level metadata. * @param contractURI_ The new contract metadata URI. */ function _setContractURI(string memory contractURI_) internal virtual { _contractURI = contractURI_; emit ContractURIUpdated(contractURI_); } /** * @notice Sets the base URI for the collection tokenURIs. * @param baseURI_ The new base URI. * @param fileExtension_ The file extension for the collection tokenURIs. e.g. ".json" */ function _setBaseURI(string memory baseURI_, string memory fileExtension_) internal virtual { if (permanentURI) revert URIPermanent(); _baseURI = baseURI_; _fileExtension = fileExtension_; emit BaseURIUpdate(baseURI_, fileExtension_); } function _setTokenURI(uint256 tokenId_, string memory tokenURI_) internal virtual { if (permanentURI || _permanentTokenURIs[tokenId_]) revert URIPermanent(); _tokenURIs[tokenId_] = tokenURI_; } /** * @notice Freezes the metadata for the entire collection. * @dev Once the metadata is frozen, it cannot be updated anymore. */ function _freezeURI() internal virtual { permanentURI = true; emit BatchPermanentURI(0, maxSupply); } /** * @notice Freezes the metadata for a specific token. * @param tokenId_ The ID of the token. */ function _freezeTokenURI(uint256 tokenId_) internal virtual { if (permanentURI) revert URIPermanent(); _permanentTokenURIs[tokenId_] = true; string memory staticURI = _tokenURI(tokenId_); _tokenURIs[tokenId_] = staticURI; emit PermanentURI(staticURI, tokenId_); } }
/* * SPDX-License-Identifier: NOASSERTION * * SPDX-FileType: SOURCE * * SPDX-FileCopyrightText: 2024 Johannes Krauser III <[email protected]>, Zodomo <[email protected]> * * SPDX-FileContributor: Johannes Krauser III <[email protected]> */ pragma solidity 0.8.23; /** * @title ICoreMetadata721 * @author 0xKrauser (Discord/Github/X: @0xKrauser, Email: [email protected]) * @notice Interface to integrate CoreMetadata721 and require missing external functions. * @custom:github https://github.com/common-resources/crate */ interface ICoreMetadata721 { event URI(string value, uint256 indexed id); /// @dev This event emits when the metadata of a token is updated. /// So that the third-party platforms such as NFT market could /// timely update the images and related attributes of the NFT. event MetadataUpdate(uint256 indexed tokenId_); /// @dev This event emits when the metadata of a range of tokens is updated. /// So that the third-party platforms such as NFT market could /// timely update the images and related attributes of the NFTs. event BatchMetadataUpdate(uint256 indexed fromTokenId_, uint256 indexed toTokenId_); /** * @notice Returns the URI for the given token ID. * @dev if the token has a non-empty, manually set URI, it will be returned as is, * otherwise it will return the concatenation of the baseURI, the token ID and, optionally, the file extension. * @param tokenId_ the ID of the token to query. * @return tokenURI_ a string representing the token URI. */ function tokenURI(uint256 tokenId_) external view returns (string memory tokenURI_); }
// 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: AGPL-3.0-only * * SPDX-FileType: SOURCE * * SPDX-FileCopyrightText: 2024 jtriley.eth <[email protected]> * * SPDX-FileContributor: jtriley.eth <[email protected]> * SPDX-FileContributor: Modified by Johannes Krauser III <[email protected]> */ pragma solidity 0.8.23; /** * @title Asset * @author jtriley.eth (Github: jtriley-eth, Email: [email protected]) * @notice Minimal "token-types"-like interface for token assets with a balance. * @custom:github https://github.com/common-resources/crate */ type Asset is address; using { balanceOf, // -- operators eq as ==, neq as !=, gt as >, gte as >=, lt as <, lte as <=, add as +, sub as -, mul as *, div as /, mod as %, and as &, or as |, xor as ^, not as ~ } for Asset global; // ------------------------------------------------------------------------------------------------- // Query Asset.balanceOf without allocating new memory. // // Procedures: // 01. store balanceOf selector in memory // 02. store owner in memory // 03. staticcall balanceOf; cache as ok // 04. check that the return value is 32 bytes; compose with ok // 05. revert if ok is false // 06. assign the return value to output function balanceOf(Asset erc721, address owner) view returns (uint256 output) { assembly ("memory-safe") { mstore(0x00, 0x70a0823100000000000000000000000000000000000000000000000000000000) mstore(0x04, owner) let ok := staticcall(gas(), erc721, 0x00, 0x24, 0x00, 0x20) ok := and(ok, eq(returndatasize(), 0x20)) if iszero(ok) { revert(0x00, 0x00) } output := mload(0x00) } } // ------------------------------------------------------------------------------------------------- // Returns `true` if the two Asset instances are equal, `false` otherwise. function eq(Asset lhs, Asset rhs) pure returns (bool output) { assembly { output := eq(lhs, rhs) } } // ------------------------------------------------------------------------------------------------- // Returns `true` if the two Asset instances are not equal, `false` otherwise. function neq(Asset lhs, Asset rhs) pure returns (bool output) { assembly { output := iszero(eq(lhs, rhs)) } } // ------------------------------------------------------------------------------------------------- // Returns `true` if `lhs` is greater than `rhs`, `false` otherwise. function gt(Asset lhs, Asset rhs) pure returns (bool output) { assembly { output := gt(lhs, rhs) } } // ------------------------------------------------------------------------------------------------- // Returns `true` if `lhs` is greater than or equal to `rhs`, `false` otherwise. function gte(Asset lhs, Asset rhs) pure returns (bool output) { assembly { output := iszero(lt(lhs, rhs)) } } // ------------------------------------------------------------------------------------------------- // Returns `true` if `lhs` is less than `rhs`, `false` otherwise. function lt(Asset lhs, Asset rhs) pure returns (bool output) { assembly { output := lt(lhs, rhs) } } // ------------------------------------------------------------------------------------------------- // Returns `true` if `lhs` is less than or equal to `rhs`, `false` otherwise. function lte(Asset lhs, Asset rhs) pure returns (bool output) { assembly { output := iszero(gt(lhs, rhs)) } } // ------------------------------------------------------------------------------------------------- // Returns the sum of two Asset instances. function add(Asset lhs, Asset rhs) pure returns (Asset output) { assembly { output := add(lhs, rhs) if gt(output, 0xffffffffffffffffffffffffffffffffffffffff) { revert(0x00, 0x00) } } } // ------------------------------------------------------------------------------------------------- // Returns the difference of two Asset instances. function sub(Asset lhs, Asset rhs) pure returns (Asset output) { assembly { output := sub(lhs, rhs) if gt(output, 0xffffffffffffffffffffffffffffffffffffffff) { revert(0x00, 0x00) } } } // ------------------------------------------------------------------------------------------------- // Returns the product of two Asset instances. function mul(Asset lhs, Asset rhs) pure returns (Asset output) { assembly { if lhs { output := and(mul(lhs, rhs), 0xffffffffffffffffffffffffffffffffffffffff) if iszero(eq(div(output, lhs), rhs)) { revert(0x00, 0x00) } } } } // ------------------------------------------------------------------------------------------------- // Returns the division of two Asset instances. function div(Asset lhs, Asset rhs) pure returns (Asset output) { assembly { if iszero(rhs) { revert(0x00, 0x00) } output := div(lhs, rhs) } } // ------------------------------------------------------------------------------------------------- // Returns the modulus of two Asset instances. function mod(Asset lhs, Asset rhs) pure returns (Asset output) { assembly { if iszero(rhs) { revert(0x00, 0x00) } output := mod(lhs, rhs) } } // ------------------------------------------------------------------------------------------------- // Returns the bitwise AND of two Asset instances. function and(Asset lhs, Asset rhs) pure returns (Asset output) { assembly { output := and(lhs, rhs) } } // ------------------------------------------------------------------------------------------------- // Returns the bitwise OR of two Asset instances. function or(Asset lhs, Asset rhs) pure returns (Asset output) { assembly { output := or(lhs, rhs) } } // ------------------------------------------------------------------------------------------------- // Returns the bitwise XOR of two Asset instances. function xor(Asset lhs, Asset rhs) pure returns (Asset output) { assembly { output := xor(lhs, rhs) } } // ------------------------------------------------------------------------------------------------- // Returns the bitwise NOT of an Asset instance. function not(Asset lhs) pure returns (Asset output) { assembly { output := and(not(lhs), 0xffffffffffffffffffffffffffffffffffffffff) } }
/* * SPDX-License-Identifier: NOASSERTION * * SPDX-FileType: SOURCE * * SPDX-FileCopyrightText: 2024 Johannes Krauser III <[email protected]>, Zodomo <[email protected]> * * SPDX-FileContributor: Johannes Krauser III <[email protected]> */ pragma solidity 0.8.23; /** * @title IBlacklistExt * @author 0xKrauser (Discord/Github/X: @0xKrauser, Email: [email protected]) * @notice Interface to integrate BlacklistExt and require missing external functions. * @custom:github https://github.com/common-resources/crate */ interface IBlacklistExt { /** * @dev Emitted when the blacklist is updated. * @param blacklistedAssets The list of addresses marked for blacklist addition/removal. * @param status The status to which they have been updated. */ event BlacklistUpdate(address[] blacklistedAssets, bool indexed status); /// @dev Cannot execute an operation from a address than owns blacklisted assets. error Blacklisted(); /** * @dev list of blacklisted assets, used to prevent mints to and from holders of prohibited assets * @return blacklist_ an array of blacklisted asset addresses. */ function getBlacklist() external view returns (address[] memory blacklist_); /** * @notice Adds or removes assets to the blacklist. * @param assets_ The list of addresses to be blacklisted. * @param status_ The status to which they have been updated. */ function setBlacklist(address[] calldata assets_, bool status_) external; }
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Library for managing enumerable sets in storage.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/EnumerableSetLib.sol)
///
/// @dev Note:
/// In many applications, the number of elements in an enumerable set is small.
/// This enumerable set implementation avoids storing the length and indices
/// for up to 3 elements. Once the length exceeds 3 for the first time, the length
/// and indices will be initialized. The amortized cost of adding elements is O(1).
///
/// The AddressSet implementation packs the length with the 0th entry.
library EnumerableSetLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The index must be less than the length.
error IndexOutOfBounds();
/// @dev The value cannot be the zero sentinel.
error ValueIsZeroSentinel();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev A sentinel value to denote the zero value in storage.
/// No elements can be equal to this value.
/// `uint72(bytes9(keccak256(bytes("_ZERO_SENTINEL"))))`.
uint256 private constant _ZERO_SENTINEL = 0xfbb67fda52d4bfb8bf;
/// @dev The storage layout is given by:
/// ```
/// mstore(0x04, _ENUMERABLE_ADDRESS_SET_SLOT_SEED)
/// mstore(0x00, set.slot)
/// let rootSlot := keccak256(0x00, 0x24)
/// mstore(0x20, rootSlot)
/// mstore(0x00, shr(96, shl(96, value)))
/// let positionSlot := keccak256(0x00, 0x40)
/// let valueSlot := add(rootSlot, sload(positionSlot))
/// let valueInStorage := shr(96, sload(valueSlot))
/// let lazyLength := shr(160, shl(160, sload(rootSlot)))
/// ```
uint256 private constant _ENUMERABLE_ADDRESS_SET_SLOT_SEED = 0x978aab92;
/// @dev The storage layout is given by:
/// ```
/// mstore(0x04, _ENUMERABLE_WORD_SET_SLOT_SEED)
/// mstore(0x00, set.slot)
/// let rootSlot := keccak256(0x00, 0x24)
/// mstore(0x20, rootSlot)
/// mstore(0x00, value)
/// let positionSlot := keccak256(0x00, 0x40)
/// let valueSlot := add(rootSlot, sload(positionSlot))
/// let valueInStorage := sload(valueSlot)
/// let lazyLength := sload(not(rootSlot))
/// ```
uint256 private constant _ENUMERABLE_WORD_SET_SLOT_SEED = 0x18fb5864;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STRUCTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev An enumerable address set in storage.
struct AddressSet {
uint256 _spacer;
}
/// @dev An enumerable bytes32 set in storage.
struct Bytes32Set {
uint256 _spacer;
}
/// @dev An enumerable uint256 set in storage.
struct Uint256Set {
uint256 _spacer;
}
/// @dev An enumerable int256 set in storage.
struct Int256Set {
uint256 _spacer;
}
/// @dev An enumerable uint8 set in storage. Useful for enums.
struct Uint8Set {
uint256 data;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* GETTERS / SETTERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the number of elements in the set.
function length(AddressSet storage set) internal view returns (uint256 result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
let rootPacked := sload(rootSlot)
let n := shr(160, shl(160, rootPacked))
result := shr(1, n)
for {} iszero(or(iszero(shr(96, rootPacked)), n)) {} {
result := 1
if iszero(sload(add(rootSlot, result))) { break }
result := 2
if iszero(sload(add(rootSlot, result))) { break }
result := 3
break
}
}
}
/// @dev Returns the number of elements in the set.
function length(Bytes32Set storage set) internal view returns (uint256 result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
let n := sload(not(rootSlot))
result := shr(1, n)
for {} iszero(n) {} {
result := 0
if iszero(sload(add(rootSlot, result))) { break }
result := 1
if iszero(sload(add(rootSlot, result))) { break }
result := 2
if iszero(sload(add(rootSlot, result))) { break }
result := 3
break
}
}
}
/// @dev Returns the number of elements in the set.
function length(Uint256Set storage set) internal view returns (uint256 result) {
result = length(_toBytes32Set(set));
}
/// @dev Returns the number of elements in the set.
function length(Int256Set storage set) internal view returns (uint256 result) {
result = length(_toBytes32Set(set));
}
/// @dev Returns the number of elements in the set.
function length(Uint8Set storage set) internal view returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
for { let packed := sload(set.slot) } packed { result := add(1, result) } {
packed := xor(packed, and(packed, add(1, not(packed))))
}
}
}
/// @dev Returns whether `value` is in the set.
function contains(AddressSet storage set, address value) internal view returns (bool result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
value := shr(96, shl(96, value))
if eq(value, _ZERO_SENTINEL) {
mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
revert(0x1c, 0x04)
}
if iszero(value) { value := _ZERO_SENTINEL }
let rootPacked := sload(rootSlot)
for {} 1 {} {
if iszero(shr(160, shl(160, rootPacked))) {
result := 1
if eq(shr(96, rootPacked), value) { break }
if eq(shr(96, sload(add(rootSlot, 1))), value) { break }
if eq(shr(96, sload(add(rootSlot, 2))), value) { break }
result := 0
break
}
mstore(0x20, rootSlot)
mstore(0x00, value)
result := iszero(iszero(sload(keccak256(0x00, 0x40))))
break
}
}
}
/// @dev Returns whether `value` is in the set.
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
if eq(value, _ZERO_SENTINEL) {
mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
revert(0x1c, 0x04)
}
if iszero(value) { value := _ZERO_SENTINEL }
for {} 1 {} {
if iszero(sload(not(rootSlot))) {
result := 1
if eq(sload(rootSlot), value) { break }
if eq(sload(add(rootSlot, 1)), value) { break }
if eq(sload(add(rootSlot, 2)), value) { break }
result := 0
break
}
mstore(0x20, rootSlot)
mstore(0x00, value)
result := iszero(iszero(sload(keccak256(0x00, 0x40))))
break
}
}
}
/// @dev Returns whether `value` is in the set.
function contains(Uint256Set storage set, uint256 value) internal view returns (bool result) {
result = contains(_toBytes32Set(set), bytes32(value));
}
/// @dev Returns whether `value` is in the set.
function contains(Int256Set storage set, int256 value) internal view returns (bool result) {
result = contains(_toBytes32Set(set), bytes32(uint256(value)));
}
/// @dev Returns whether `value` is in the set.
function contains(Uint8Set storage set, uint8 value) internal view returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := and(1, shr(and(0xff, value), sload(set.slot)))
}
}
/// @dev Adds `value` to the set. Returns whether `value` was not in the set.
function add(AddressSet storage set, address value) internal returns (bool result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
value := shr(96, shl(96, value))
if eq(value, _ZERO_SENTINEL) {
mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
revert(0x1c, 0x04)
}
if iszero(value) { value := _ZERO_SENTINEL }
let rootPacked := sload(rootSlot)
for { let n := shr(160, shl(160, rootPacked)) } 1 {} {
mstore(0x20, rootSlot)
if iszero(n) {
let v0 := shr(96, rootPacked)
if iszero(v0) {
sstore(rootSlot, shl(96, value))
result := 1
break
}
if eq(v0, value) { break }
let v1 := shr(96, sload(add(rootSlot, 1)))
if iszero(v1) {
sstore(add(rootSlot, 1), shl(96, value))
result := 1
break
}
if eq(v1, value) { break }
let v2 := shr(96, sload(add(rootSlot, 2)))
if iszero(v2) {
sstore(add(rootSlot, 2), shl(96, value))
result := 1
break
}
if eq(v2, value) { break }
mstore(0x00, v0)
sstore(keccak256(0x00, 0x40), 1)
mstore(0x00, v1)
sstore(keccak256(0x00, 0x40), 2)
mstore(0x00, v2)
sstore(keccak256(0x00, 0x40), 3)
rootPacked := or(rootPacked, 7)
n := 7
}
mstore(0x00, value)
let p := keccak256(0x00, 0x40)
if iszero(sload(p)) {
n := shr(1, n)
sstore(add(rootSlot, n), shl(96, value))
sstore(p, add(1, n))
sstore(rootSlot, add(2, rootPacked))
result := 1
break
}
break
}
}
}
/// @dev Adds `value` to the set. Returns whether `value` was not in the set.
function add(Bytes32Set storage set, bytes32 value) internal returns (bool result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
if eq(value, _ZERO_SENTINEL) {
mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
revert(0x1c, 0x04)
}
if iszero(value) { value := _ZERO_SENTINEL }
for { let n := sload(not(rootSlot)) } 1 {} {
mstore(0x20, rootSlot)
if iszero(n) {
let v0 := sload(rootSlot)
if iszero(v0) {
sstore(rootSlot, value)
result := 1
break
}
if eq(v0, value) { break }
let v1 := sload(add(rootSlot, 1))
if iszero(v1) {
sstore(add(rootSlot, 1), value)
result := 1
break
}
if eq(v1, value) { break }
let v2 := sload(add(rootSlot, 2))
if iszero(v2) {
sstore(add(rootSlot, 2), value)
result := 1
break
}
if eq(v2, value) { break }
mstore(0x00, v0)
sstore(keccak256(0x00, 0x40), 1)
mstore(0x00, v1)
sstore(keccak256(0x00, 0x40), 2)
mstore(0x00, v2)
sstore(keccak256(0x00, 0x40), 3)
n := 7
}
mstore(0x00, value)
let p := keccak256(0x00, 0x40)
if iszero(sload(p)) {
n := shr(1, n)
sstore(add(rootSlot, n), value)
sstore(p, add(1, n))
sstore(not(rootSlot), or(1, shl(1, add(1, n))))
result := 1
break
}
break
}
}
}
/// @dev Adds `value` to the set. Returns whether `value` was not in the set.
function add(Uint256Set storage set, uint256 value) internal returns (bool result) {
result = add(_toBytes32Set(set), bytes32(value));
}
/// @dev Adds `value` to the set. Returns whether `value` was not in the set.
function add(Int256Set storage set, int256 value) internal returns (bool result) {
result = add(_toBytes32Set(set), bytes32(uint256(value)));
}
/// @dev Adds `value` to the set. Returns whether `value` was not in the set.
function add(Uint8Set storage set, uint8 value) internal returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := sload(set.slot)
let mask := shl(and(0xff, value), 1)
sstore(set.slot, or(result, mask))
result := iszero(and(result, mask))
}
}
/// @dev Removes `value` from the set. Returns whether `value` was in the set.
function remove(AddressSet storage set, address value) internal returns (bool result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
value := shr(96, shl(96, value))
if eq(value, _ZERO_SENTINEL) {
mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
revert(0x1c, 0x04)
}
if iszero(value) { value := _ZERO_SENTINEL }
let rootPacked := sload(rootSlot)
for { let n := shr(160, shl(160, rootPacked)) } 1 {} {
if iszero(n) {
result := 1
if eq(shr(96, rootPacked), value) {
sstore(rootSlot, sload(add(rootSlot, 1)))
sstore(add(rootSlot, 1), sload(add(rootSlot, 2)))
sstore(add(rootSlot, 2), 0)
break
}
if eq(shr(96, sload(add(rootSlot, 1))), value) {
sstore(add(rootSlot, 1), sload(add(rootSlot, 2)))
sstore(add(rootSlot, 2), 0)
break
}
if eq(shr(96, sload(add(rootSlot, 2))), value) {
sstore(add(rootSlot, 2), 0)
break
}
result := 0
break
}
mstore(0x20, rootSlot)
mstore(0x00, value)
let p := keccak256(0x00, 0x40)
let position := sload(p)
if iszero(position) { break }
n := sub(shr(1, n), 1)
if iszero(eq(sub(position, 1), n)) {
let lastValue := shr(96, sload(add(rootSlot, n)))
sstore(add(rootSlot, sub(position, 1)), shl(96, lastValue))
sstore(add(rootSlot, n), 0)
mstore(0x00, lastValue)
sstore(keccak256(0x00, 0x40), position)
}
sstore(rootSlot, or(shl(96, shr(96, sload(rootSlot))), or(shl(1, n), 1)))
sstore(p, 0)
result := 1
break
}
}
}
/// @dev Removes `value` from the set. Returns whether `value` was in the set.
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
if eq(value, _ZERO_SENTINEL) {
mstore(0x00, 0xf5a267f1) // `ValueIsZeroSentinel()`.
revert(0x1c, 0x04)
}
if iszero(value) { value := _ZERO_SENTINEL }
for { let n := sload(not(rootSlot)) } 1 {} {
if iszero(n) {
result := 1
if eq(sload(rootSlot), value) {
sstore(rootSlot, sload(add(rootSlot, 1)))
sstore(add(rootSlot, 1), sload(add(rootSlot, 2)))
sstore(add(rootSlot, 2), 0)
break
}
if eq(sload(add(rootSlot, 1)), value) {
sstore(add(rootSlot, 1), sload(add(rootSlot, 2)))
sstore(add(rootSlot, 2), 0)
break
}
if eq(sload(add(rootSlot, 2)), value) {
sstore(add(rootSlot, 2), 0)
break
}
result := 0
break
}
mstore(0x20, rootSlot)
mstore(0x00, value)
let p := keccak256(0x00, 0x40)
let position := sload(p)
if iszero(position) { break }
n := sub(shr(1, n), 1)
if iszero(eq(sub(position, 1), n)) {
let lastValue := sload(add(rootSlot, n))
sstore(add(rootSlot, sub(position, 1)), lastValue)
sstore(add(rootSlot, n), 0)
mstore(0x00, lastValue)
sstore(keccak256(0x00, 0x40), position)
}
sstore(not(rootSlot), or(shl(1, n), 1))
sstore(p, 0)
result := 1
break
}
}
}
/// @dev Removes `value` from the set. Returns whether `value` was in the set.
function remove(Uint256Set storage set, uint256 value) internal returns (bool result) {
result = remove(_toBytes32Set(set), bytes32(value));
}
/// @dev Removes `value` from the set. Returns whether `value` was in the set.
function remove(Int256Set storage set, int256 value) internal returns (bool result) {
result = remove(_toBytes32Set(set), bytes32(uint256(value)));
}
/// @dev Removes `value` from the set. Returns whether `value` was in the set.
function remove(Uint8Set storage set, uint8 value) internal returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := sload(set.slot)
let mask := shl(and(0xff, value), 1)
sstore(set.slot, and(result, not(mask)))
result := iszero(iszero(and(result, mask)))
}
}
/// @dev Returns all of the values in the set.
/// Note: This can consume more gas than the block gas limit for large sets.
function values(AddressSet storage set) internal view returns (address[] memory result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
let zs := _ZERO_SENTINEL
let rootPacked := sload(rootSlot)
let n := shr(160, shl(160, rootPacked))
result := mload(0x40)
let o := add(0x20, result)
let v := shr(96, rootPacked)
mstore(o, mul(v, iszero(eq(v, zs))))
for {} 1 {} {
if iszero(n) {
if v {
n := 1
v := shr(96, sload(add(rootSlot, n)))
if v {
n := 2
mstore(add(o, 0x20), mul(v, iszero(eq(v, zs))))
v := shr(96, sload(add(rootSlot, n)))
if v {
n := 3
mstore(add(o, 0x40), mul(v, iszero(eq(v, zs))))
}
}
}
break
}
n := shr(1, n)
for { let i := 1 } lt(i, n) { i := add(i, 1) } {
v := shr(96, sload(add(rootSlot, i)))
mstore(add(o, shl(5, i)), mul(v, iszero(eq(v, zs))))
}
break
}
mstore(result, n)
mstore(0x40, add(o, shl(5, n)))
}
}
/// @dev Returns all of the values in the set.
/// Note: This can consume more gas than the block gas limit for large sets.
function values(Bytes32Set storage set) internal view returns (bytes32[] memory result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
let zs := _ZERO_SENTINEL
let n := sload(not(rootSlot))
result := mload(0x40)
let o := add(0x20, result)
for {} 1 {} {
if iszero(n) {
let v := sload(rootSlot)
if v {
n := 1
mstore(o, mul(v, iszero(eq(v, zs))))
v := sload(add(rootSlot, n))
if v {
n := 2
mstore(add(o, 0x20), mul(v, iszero(eq(v, zs))))
v := sload(add(rootSlot, n))
if v {
n := 3
mstore(add(o, 0x40), mul(v, iszero(eq(v, zs))))
}
}
}
break
}
n := shr(1, n)
for { let i := 0 } lt(i, n) { i := add(i, 1) } {
let v := sload(add(rootSlot, i))
mstore(add(o, shl(5, i)), mul(v, iszero(eq(v, zs))))
}
break
}
mstore(result, n)
mstore(0x40, add(o, shl(5, n)))
}
}
/// @dev Returns all of the values in the set.
/// Note: This can consume more gas than the block gas limit for large sets.
function values(Uint256Set storage set) internal view returns (uint256[] memory result) {
result = _toUints(values(_toBytes32Set(set)));
}
/// @dev Returns all of the values in the set.
/// Note: This can consume more gas than the block gas limit for large sets.
function values(Int256Set storage set) internal view returns (int256[] memory result) {
result = _toInts(values(_toBytes32Set(set)));
}
/// @dev Returns all of the values in the set.
function values(Uint8Set storage set) internal view returns (uint8[] memory result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(0x40)
let ptr := add(result, 0x20)
let o := 0
for { let packed := sload(set.slot) } packed {} {
if iszero(and(packed, 0xffff)) {
o := add(o, 16)
packed := shr(16, packed)
continue
}
mstore(ptr, o)
ptr := add(ptr, shl(5, and(packed, 1)))
o := add(o, 1)
packed := shr(1, packed)
}
mstore(result, shr(5, sub(ptr, add(result, 0x20))))
mstore(0x40, ptr)
}
}
/// @dev Returns the element at index `i` in the set.
function at(AddressSet storage set, uint256 i) internal view returns (address result) {
bytes32 rootSlot = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
result := shr(96, sload(add(rootSlot, i)))
result := mul(result, iszero(eq(result, _ZERO_SENTINEL)))
}
if (i >= length(set)) revert IndexOutOfBounds();
}
/// @dev Returns the element at index `i` in the set.
function at(Bytes32Set storage set, uint256 i) internal view returns (bytes32 result) {
result = _rootSlot(set);
/// @solidity memory-safe-assembly
assembly {
result := sload(add(result, i))
result := mul(result, iszero(eq(result, _ZERO_SENTINEL)))
}
if (i >= length(set)) revert IndexOutOfBounds();
}
/// @dev Returns the element at index `i` in the set.
function at(Uint256Set storage set, uint256 i) internal view returns (uint256 result) {
result = uint256(at(_toBytes32Set(set), i));
}
/// @dev Returns the element at index `i` in the set.
function at(Int256Set storage set, uint256 i) internal view returns (int256 result) {
result = int256(uint256(at(_toBytes32Set(set), i)));
}
/// @dev Returns the element at index `i` in the set.
function at(Uint8Set storage set, uint256 i) internal view returns (uint8 result) {
/// @solidity memory-safe-assembly
assembly {
let packed := sload(set.slot)
for {} 1 {
mstore(0x00, 0x4e23d035) // `IndexOutOfBounds()`.
revert(0x1c, 0x04)
} {
if iszero(lt(i, 256)) { continue }
for { let j := 0 } iszero(eq(i, j)) {} {
packed := xor(packed, and(packed, add(1, not(packed))))
j := add(j, 1)
}
if iszero(packed) { continue }
break
}
// Find first set subroutine, optimized for smaller bytecode size.
let x := and(packed, add(1, not(packed)))
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))))
// For the lower 5 bits of the result, use a De Bruijn lookup.
// forgefmt: disable-next-item
result := or(r, byte(and(div(0xd76453e0, shr(r, x)), 0x1f),
0x001f0d1e100c1d070f090b19131c1706010e11080a1a141802121b1503160405))
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PRIVATE HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the root slot.
function _rootSlot(AddressSet storage s) private pure returns (bytes32 r) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x04, _ENUMERABLE_ADDRESS_SET_SLOT_SEED)
mstore(0x00, s.slot)
r := keccak256(0x00, 0x24)
}
}
/// @dev Returns the root slot.
function _rootSlot(Bytes32Set storage s) private pure returns (bytes32 r) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x04, _ENUMERABLE_WORD_SET_SLOT_SEED)
mstore(0x00, s.slot)
r := keccak256(0x00, 0x24)
}
}
/// @dev Casts to a Bytes32Set.
function _toBytes32Set(Uint256Set storage s) private pure returns (Bytes32Set storage c) {
/// @solidity memory-safe-assembly
assembly {
c.slot := s.slot
}
}
/// @dev Casts to a Bytes32Set.
function _toBytes32Set(Int256Set storage s) private pure returns (Bytes32Set storage c) {
/// @solidity memory-safe-assembly
assembly {
c.slot := s.slot
}
}
/// @dev Casts to a uint256 array.
function _toUints(bytes32[] memory a) private pure returns (uint256[] memory c) {
/// @solidity memory-safe-assembly
assembly {
c := a
}
}
/// @dev Casts to a int256 array.
function _toInts(bytes32[] memory a) private pure returns (int256[] memory c) {
/// @solidity memory-safe-assembly
assembly {
c := a
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Gas optimized verification of proof of inclusion for a leaf in a Merkle tree.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/MerkleProofLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/MerkleProofLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/MerkleProof.sol)
library MerkleProofLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MERKLE PROOF VERIFICATION OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf)
internal
pure
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
if mload(proof) {
// Initialize `offset` to the offset of `proof` elements in memory.
let offset := add(proof, 0x20)
// Left shift by 5 is equivalent to multiplying by 0x20.
let end := add(offset, shl(5, mload(proof)))
// Iterate over proof elements to compute root hash.
for {} 1 {} {
// Slot of `leaf` in scratch space.
// If the condition is true: 0x20, otherwise: 0x00.
let scratch := shl(5, gt(leaf, mload(offset)))
// Store elements to hash contiguously in scratch space.
// Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
mstore(scratch, leaf)
mstore(xor(scratch, 0x20), mload(offset))
// Reuse `leaf` to store the hash to reduce stack operations.
leaf := keccak256(0x00, 0x40)
offset := add(offset, 0x20)
if iszero(lt(offset, end)) { break }
}
}
isValid := eq(leaf, root)
}
}
/// @dev Returns whether `leaf` exists in the Merkle tree with `root`, given `proof`.
function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf)
internal
pure
returns (bool isValid)
{
/// @solidity memory-safe-assembly
assembly {
if proof.length {
// Left shift by 5 is equivalent to multiplying by 0x20.
let end := add(proof.offset, shl(5, proof.length))
// Initialize `offset` to the offset of `proof` in the calldata.
let offset := proof.offset
// Iterate over proof elements to compute root hash.
for {} 1 {} {
// Slot of `leaf` in scratch space.
// If the condition is true: 0x20, otherwise: 0x00.
let scratch := shl(5, gt(leaf, calldataload(offset)))
// Store elements to hash contiguously in scratch space.
// Scratch space is 64 bytes (0x00 - 0x3f) and both elements are 32 bytes.
mstore(scratch, leaf)
mstore(xor(scratch, 0x20), calldataload(offset))
// Reuse `leaf` to store the hash to reduce stack operations.
leaf := keccak256(0x00, 0x40)
offset := add(offset, 0x20)
if iszero(lt(offset, end)) { break }
}
}
isValid := eq(leaf, root)
}
}
/// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
/// given `proof` and `flags`.
///
/// Note:
/// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
/// will always return false.
/// - The sum of the lengths of `proof` and `leaves` must never overflow.
/// - Any non-zero word in the `flags` array is treated as true.
/// - The memory offset of `proof` must be non-zero
/// (i.e. `proof` is not pointing to the scratch space).
function verifyMultiProof(
bytes32[] memory proof,
bytes32 root,
bytes32[] memory leaves,
bool[] memory flags
) internal pure returns (bool isValid) {
// Rebuilds the root by consuming and producing values on a queue.
// The queue starts with the `leaves` array, and goes into a `hashes` array.
// After the process, the last element on the queue is verified
// to be equal to the `root`.
//
// The `flags` array denotes whether the sibling
// should be popped from the queue (`flag == true`), or
// should be popped from the `proof` (`flag == false`).
/// @solidity memory-safe-assembly
assembly {
// Cache the lengths of the arrays.
let leavesLength := mload(leaves)
let proofLength := mload(proof)
let flagsLength := mload(flags)
// Advance the pointers of the arrays to point to the data.
leaves := add(0x20, leaves)
proof := add(0x20, proof)
flags := add(0x20, flags)
// If the number of flags is correct.
for {} eq(add(leavesLength, proofLength), add(flagsLength, 1)) {} {
// For the case where `proof.length + leaves.length == 1`.
if iszero(flagsLength) {
// `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
isValid := eq(mload(xor(leaves, mul(xor(proof, leaves), proofLength))), root)
break
}
// The required final proof offset if `flagsLength` is not zero, otherwise zero.
let proofEnd := add(proof, shl(5, proofLength))
// We can use the free memory space for the queue.
// We don't need to allocate, since the queue is temporary.
let hashesFront := mload(0x40)
// Copy the leaves into the hashes.
// Sometimes, a little memory expansion costs less than branching.
// Should cost less, even with a high free memory offset of 0x7d00.
leavesLength := shl(5, leavesLength)
for { let i := 0 } iszero(eq(i, leavesLength)) { i := add(i, 0x20) } {
mstore(add(hashesFront, i), mload(add(leaves, i)))
}
// Compute the back of the hashes.
let hashesBack := add(hashesFront, leavesLength)
// This is the end of the memory for the queue.
// We recycle `flagsLength` to save on stack variables (sometimes save gas).
flagsLength := add(hashesBack, shl(5, flagsLength))
for {} 1 {} {
// Pop from `hashes`.
let a := mload(hashesFront)
// Pop from `hashes`.
let b := mload(add(hashesFront, 0x20))
hashesFront := add(hashesFront, 0x40)
// If the flag is false, load the next proof,
// else, pops from the queue.
if iszero(mload(flags)) {
// Loads the next proof.
b := mload(proof)
proof := add(proof, 0x20)
// Unpop from `hashes`.
hashesFront := sub(hashesFront, 0x20)
}
// Advance to the next flag.
flags := add(flags, 0x20)
// Slot of `a` in scratch space.
// If the condition is true: 0x20, otherwise: 0x00.
let scratch := shl(5, gt(a, b))
// Hash the scratch space and push the result onto the queue.
mstore(scratch, a)
mstore(xor(scratch, 0x20), b)
mstore(hashesBack, keccak256(0x00, 0x40))
hashesBack := add(hashesBack, 0x20)
if iszero(lt(hashesBack, flagsLength)) { break }
}
isValid :=
and(
// Checks if the last value in the queue is same as the root.
eq(mload(sub(hashesBack, 0x20)), root),
// And whether all the proofs are used, if required.
eq(proofEnd, proof)
)
break
}
}
}
/// @dev Returns whether all `leaves` exist in the Merkle tree with `root`,
/// given `proof` and `flags`.
///
/// Note:
/// - Breaking the invariant `flags.length == (leaves.length - 1) + proof.length`
/// will always return false.
/// - Any non-zero word in the `flags` array is treated as true.
/// - The calldata offset of `proof` must be non-zero
/// (i.e. `proof` is from a regular Solidity function with a 4-byte selector).
function verifyMultiProofCalldata(
bytes32[] calldata proof,
bytes32 root,
bytes32[] calldata leaves,
bool[] calldata flags
) internal pure returns (bool isValid) {
// Rebuilds the root by consuming and producing values on a queue.
// The queue starts with the `leaves` array, and goes into a `hashes` array.
// After the process, the last element on the queue is verified
// to be equal to the `root`.
//
// The `flags` array denotes whether the sibling
// should be popped from the queue (`flag == true`), or
// should be popped from the `proof` (`flag == false`).
/// @solidity memory-safe-assembly
assembly {
// If the number of flags is correct.
for {} eq(add(leaves.length, proof.length), add(flags.length, 1)) {} {
// For the case where `proof.length + leaves.length == 1`.
if iszero(flags.length) {
// `isValid = (proof.length == 1 ? proof[0] : leaves[0]) == root`.
// forgefmt: disable-next-item
isValid := eq(
calldataload(
xor(leaves.offset, mul(xor(proof.offset, leaves.offset), proof.length))
),
root
)
break
}
// The required final proof offset if `flagsLength` is not zero, otherwise zero.
let proofEnd := add(proof.offset, shl(5, proof.length))
// We can use the free memory space for the queue.
// We don't need to allocate, since the queue is temporary.
let hashesFront := mload(0x40)
// Copy the leaves into the hashes.
// Sometimes, a little memory expansion costs less than branching.
// Should cost less, even with a high free memory offset of 0x7d00.
calldatacopy(hashesFront, leaves.offset, shl(5, leaves.length))
// Compute the back of the hashes.
let hashesBack := add(hashesFront, shl(5, leaves.length))
// This is the end of the memory for the queue.
// We recycle `flagsLength` to save on stack variables (sometimes save gas).
flags.length := add(hashesBack, shl(5, flags.length))
// We don't need to make a copy of `proof.offset` or `flags.offset`,
// as they are pass-by-value (this trick may not always save gas).
for {} 1 {} {
// Pop from `hashes`.
let a := mload(hashesFront)
// Pop from `hashes`.
let b := mload(add(hashesFront, 0x20))
hashesFront := add(hashesFront, 0x40)
// If the flag is false, load the next proof,
// else, pops from the queue.
if iszero(calldataload(flags.offset)) {
// Loads the next proof.
b := calldataload(proof.offset)
proof.offset := add(proof.offset, 0x20)
// Unpop from `hashes`.
hashesFront := sub(hashesFront, 0x20)
}
// Advance to the next flag offset.
flags.offset := add(flags.offset, 0x20)
// Slot of `a` in scratch space.
// If the condition is true: 0x20, otherwise: 0x00.
let scratch := shl(5, gt(a, b))
// Hash the scratch space and push the result onto the queue.
mstore(scratch, a)
mstore(xor(scratch, 0x20), b)
mstore(hashesBack, keccak256(0x00, 0x40))
hashesBack := add(hashesBack, 0x20)
if iszero(lt(hashesBack, flags.length)) { break }
}
isValid :=
and(
// Checks if the last value in the queue is same as the root.
eq(mload(sub(hashesBack, 0x20)), root),
// And whether all the proofs are used, if required.
eq(proofEnd, proof.offset)
)
break
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EMPTY CALLDATA HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns an empty calldata bytes32 array.
function emptyProof() internal pure returns (bytes32[] calldata proof) {
/// @solidity memory-safe-assembly
assembly {
proof.length := 0
}
}
/// @dev Returns an empty calldata bytes32 array.
function emptyLeaves() internal pure returns (bytes32[] calldata leaves) {
/// @solidity memory-safe-assembly
assembly {
leaves.length := 0
}
}
/// @dev Returns an empty calldata bool array.
function emptyFlags() internal pure returns (bool[] calldata flags) {
/// @solidity memory-safe-assembly
assembly {
flags.length := 0
}
}
}/* * SPDX-License-Identifier: NOASSERTION * * SPDX-FileType: SOURCE * * SPDX-FileCopyrightText: 2024 Johannes Krauser III <[email protected]>, Zodomo <[email protected]> * * SPDX-FileContributor: Johannes Krauser III <[email protected]> */ pragma solidity 0.8.23; import {IERC721Lockable} from "erc721-lockable/contracts/IERC721Lockable.sol"; /** * @title ILockableExt * @author 0xKrauser (Discord/Github/X: @0xKrauser, Email: [email protected]) * @notice Interface to integrate LockableExt and require missing external functions. * @custom:github https://github.com/common-resources/crate */ interface ILockableExt is IERC721Lockable { error AlreadyLocked(); error NotLocker(); function transferFrom(address from_, address to_, uint256 tokenId_) external; function safeTransferFrom(address from_, address to_, uint256 tokenId_) external; }
/* * SPDX-License-Identifier: NOASSERTION * * SPDX-FileType: SOURCE * * SPDX-FileCopyrightText: 2024 Johannes Krauser III <[email protected]>, Zodomo <[email protected]> * * SPDX-FileContributor: Johannes Krauser III <[email protected]> */ pragma solidity 0.8.23; /** * @title IReferralExt * @author 0xKrauser (Discord/Github/X: @0xKrauser, Email: [email protected]) * @notice Interface to integrate ReferralExt and require missing external functions. * @custom:github https://github.com/common-resources/crate */ interface IReferralExt { /** * @dev Emitted when the referral fee is updated. * @param referralFee_ The new referral fee. */ event ReferralFeeUpdate(uint16 referralFee_); /** * @dev Emitted when a referral fee is paid. * @param referral_ The address of the referrer. * @param value_ The value of the referral fee. */ //@TODO * @param _referred The address of the referred account. //@TODO * @param _amount The amount of tokens minted. event Referral(address indexed referral_, address indexed referred_, uint256 value_); /// @dev Self-referral to either msg.sender or recipient is not allowed. error SelfReferral(); /// @dev The referral fee is above the maximum value. error MaxReferral(); /** * @notice Sets the referral fee for minting. * @dev The referral fee is a percentage of the mint value that is paid to the referrer. * @param bps_ The new referral fee, must be < (_DENOMINATOR_BPS - allocation). */ function setReferralFee(uint16 bps_) external; }
/* * SPDX-License-Identifier: NOASSERTION * * SPDX-FileType: SOURCE * * SPDX-FileCopyrightText: 2024 Johannes Krauser III <[email protected]>, Zodomo <[email protected]> * * SPDX-FileContributor: Johannes Krauser III <[email protected]> */ pragma solidity 0.8.23; /** * @title IRoyaltyExt * @author 0xKrauser (Discord/Github/X: @0xKrauser, Email: [email protected]) * @notice Interface to integrate RoyaltyExt and require missing external functions. * @custom:github https://github.com/common-resources/crate */ interface IRoyaltyExt { /** * @dev Emitted when the royalty fee for a specific token is updated. * @param tokenId_ The ID of the token for which the royalty fee is updated. * @param receiver_ The address of the receiver of the royalty fee. * @param bps_ The updated royalty fee, represented as a 96-bit fixed-point number. */ event RoyaltiesUpdate(uint256 indexed tokenId_, address indexed receiver_, uint96 bps_); /** * @dev Emitted when the royalty feature is disabled by setting address(0) as receiver. * @custom:unique */ event RoyaltiesDisabled(); /** * @dev Emitted when the royalty feature is frozen. */ event RoyaltiesFrozen(); /// @dev Cannot set royalties when they have previously been disabled. error DisabledRoyalties(); /// @dev Cannot set royalties when they have been frozen. error FrozenRoyalties(); /** * @dev Sets the default royalty receiver and fee for the contract. * @param recipient_ The address of the recipient_. * @param bps_ The royalty fee, represented as a 96-bit fixed-point number. */ function setRoyalties(address recipient_, uint96 bps_) external; /** * @dev Sets the royalty receiver and fee for a specific token ID. * @param tokenId_ The ID of the token. * @param recipient_ The address of the recipient. * @param bps_ The royalty fee, represented as a 96-bit fixed-point number. */ function setTokenRoyalties(uint256 tokenId_, address recipient_, uint96 bps_) external; /** * @notice Freezes royalties for the contract. * @dev Prevents further changes to royalties by setting the frozen flag to true. */ function freezeRoyalties() external; /** * @notice Disables royalties for the contract. * @dev Irreversibly disable royalties by resetting tokenId 0 royalty to (address(0), 0) * and deleting default royalty info */ function disableRoyalties() external; }
// 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: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}/* * SPDX-License-Identifier: NOASSERTION * * SPDX-FileType: SOURCE * * SPDX-FileCopyrightText: 2024 Johannes Krauser III <[email protected]>, Zodomo <[email protected]> * * SPDX-FileContributor: Johannes Krauser III <[email protected]> */ pragma solidity 0.8.23; /** * @title ICoreMetadata * @author 0xKrauser (Discord/Github/X: @0xKrauser, Email: [email protected]) * @notice Interface to integrate CoreMetadata and require missing external functions. * @custom:github https://github.com/common-resources/crate */ interface ICoreMetadata { /** * @dev Emitted when the contract metadata is updated. * @param uri_ The new contract metadata URI. */ event ContractURIUpdated(string uri_); /** * @dev Emitted when the contract metadata is updated. * @param baseURI_ The new base metadata URI. * @param fileExtension_ The new file extension. */ event BaseURIUpdate(string baseURI_, string fileExtension_); /** * @dev Emitted when a permanent URI is set for a specific token ID. * * @param uri_ The permanent URI value. * @param tokenId_ The token ID for which the permanent URI is set. */ event PermanentURI(string uri_, uint256 indexed tokenId_); /** * @dev Emitted when a batch of tokens have their permanent URI set. * This event is emitted when a range of token IDs have their permanent URI set. * * @param fromTokenId_ The starting token ID of the range. * @param toTokenId_ The ending token ID of the range. * @custom:unique */ event BatchPermanentURI(uint256 indexed fromTokenId_, uint256 indexed toTokenId_); /// @dev Cannot set tokenURIs or baseURI when metadata has been frozen. error URIPermanent(); /** * @notice Returns the contract metadata URI for the collection. * @return contractURI_ a string representing the contract metadata URI or a stringified JSON object. */ function contractURI() external view returns (string memory contractURI_); /** * @notice Returns the base URI for the collection tokenURIs. * @return baseURI a string representing the base URI. */ function baseURI() external view returns (string memory); /** * @notice Sets the contract metadata URI. * @dev This URI is used to store contract-level metadata. * @param contractURI_ The new contract metadata URI. */ function setContractURI(string memory contractURI_) external; /** * @notice Sets the base URI for the collection tokenURIs. * @param baseURI_ The new base URI. * @param fileExtension_ The file extension for the collection tokenURIs. e.g. ".json" */ function setBaseURI(string memory baseURI_, string memory fileExtension_) external; /** * @notice Freezes the metadata for a specific token. * @param tokenId_ The ID of the token. */ function freezeTokenURI(uint256 tokenId_) external; }
// 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 str) {
/// @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.
str := add(mload(0x40), 0x80)
mstore(0x40, add(str, 0x20)) // Allocate the memory.
mstore(str, 0) // Zeroize the slot after the string.
let end := str // 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 {} {
str := add(str, w) // `sub(str, 1)`.
// Store the character to the pointer.
// The ASCII index of the '0' character is 48.
mstore8(str, add(48, mod(temp, 10)))
temp := div(temp, 10) // Keep dividing `temp` until zero.
if iszero(temp) { break }
}
let length := sub(end, str)
str := sub(str, 0x20) // Move the pointer 32 bytes back to make room for the length.
mstore(str, length) // Store the length.
}
}
/// @dev Returns the base 10 decimal representation of `value`.
function toString(int256 value) internal pure returns (string memory str) {
if (value >= 0) return toString(uint256(value));
unchecked {
str = 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 length := mload(str) // Load the string length.
mstore(str, 0x2d) // Store the '-' character.
str := sub(str, 1) // Move back the string pointer by a byte.
mstore(str, add(length, 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 str) {
str = toHexStringNoPrefix(value, length);
/// @solidity memory-safe-assembly
assembly {
let strLength := add(mload(str), 2) // Compute the length.
mstore(str, 0x3078) // Store the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Store the length.
}
}
/// @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` 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 str)
{
/// @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.
str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f)))
mstore(0x40, add(str, 0x20)) // Allocate the memory.
mstore(str, 0) // Zeroize the slot after the string.
let end := str // Cache the end to calculate the length later.
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
let start := sub(str, 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 {} {
str := add(str, w) // `sub(str, 2)`.
mstore8(add(str, 1), mload(and(temp, 15)))
mstore8(str, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
if iszero(xor(str, start)) { break }
}
if temp {
mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`.
revert(0x1c, 0x04)
}
let strLength := sub(end, str)
str := sub(str, 0x20)
mstore(str, strLength) // 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 str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let strLength := add(mload(str), 2) // Compute the length.
mstore(str, 0x3078) // Store the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // 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 str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.
let strLength := add(mload(str), 2) // Compute the length.
mstore(add(str, o), 0x3078) // Store the "0x" prefix, accounting for leading zero.
str := sub(add(str, o), 2) // Move the pointer, accounting for leading zero.
mstore(str, sub(strLength, 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 str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present.
let strLength := mload(str) // Get the length.
str := add(str, o) // Move the pointer, accounting for leading zero.
mstore(str, sub(strLength, 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 str) {
/// @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.
str := add(mload(0x40), 0x80)
mstore(0x40, add(str, 0x20)) // Allocate the memory.
mstore(str, 0) // Zeroize the slot after the string.
let end := str // 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 {} {
str := add(str, w) // `sub(str, 2)`.
mstore8(add(str, 1), mload(and(temp, 15)))
mstore8(str, mload(and(shr(4, temp), 15)))
temp := shr(8, temp)
if iszero(temp) { break }
}
let strLength := sub(end, str)
str := sub(str, 0x20)
mstore(str, strLength) // 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 str) {
str = toHexString(value);
/// @solidity memory-safe-assembly
assembly {
let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...`
let o := add(str, 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 str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assembly
assembly {
let strLength := add(mload(str), 2) // Compute the length.
mstore(str, 0x3078) // Store the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // 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 str) {
/// @solidity memory-safe-assembly
assembly {
str := mload(0x40)
// Allocate the 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(str, 0x80))
mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.
str := add(str, 2)
mstore(str, 40) // Store the length.
let o := add(str, 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 str) {
str = toHexStringNoPrefix(raw);
/// @solidity memory-safe-assembly
assembly {
let strLength := add(mload(str), 2) // Compute the length.
mstore(str, 0x3078) // Store the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // 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 str) {
/// @solidity memory-safe-assembly
assembly {
let length := mload(raw)
str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.
mstore(str, add(length, length)) // Store the length of the output.
mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup.
let o := add(str, 0x20)
let end := add(raw, length)
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 the 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 {
let mask := shl(7, div(not(0), 255))
result := 1
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)
let end := add(o, mload(s))
for {} 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)
let end := add(o, mload(s))
for {} 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 `search` replaced with `replacement`.
function replace(string memory subject, string memory search, string memory replacement)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let subjectLength := mload(subject)
let searchLength := mload(search)
let replacementLength := mload(replacement)
subject := add(subject, 0x20)
search := add(search, 0x20)
replacement := add(replacement, 0x20)
result := add(mload(0x40), 0x20)
let subjectEnd := add(subject, subjectLength)
if iszero(gt(searchLength, subjectLength)) {
let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1)
let h := 0
if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
let s := mload(search)
for {} 1 {} {
let t := mload(subject)
// Whether the first `searchLength % 32` bytes of
// `subject` and `search` matches.
if iszero(shr(m, xor(t, s))) {
if h {
if iszero(eq(keccak256(subject, searchLength), h)) {
mstore(result, t)
result := add(result, 1)
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
// Copy the `replacement` one word at a time.
for { let o := 0 } 1 {} {
mstore(add(result, o), mload(add(replacement, o)))
o := add(o, 0x20)
if iszero(lt(o, replacementLength)) { break }
}
result := add(result, replacementLength)
subject := add(subject, searchLength)
if searchLength {
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
mstore(result, t)
result := add(result, 1)
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
}
}
let resultRemainder := result
result := add(mload(0x40), 0x20)
let k := add(sub(resultRemainder, result), sub(subjectEnd, subject))
// Copy the rest of the string one word at a time.
for {} lt(subject, subjectEnd) {} {
mstore(resultRemainder, mload(subject))
resultRemainder := add(resultRemainder, 0x20)
subject := add(subject, 0x20)
}
result := sub(result, 0x20)
let last := add(add(result, 0x20), k) // Zeroize the slot after the string.
mstore(last, 0)
mstore(0x40, add(last, 0x20)) // Allocate the memory.
mstore(result, k) // Store the length.
}
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from left to right, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function indexOf(string memory subject, string memory search, uint256 from)
internal
pure
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
for { let subjectLength := mload(subject) } 1 {} {
if iszero(mload(search)) {
if iszero(gt(from, subjectLength)) {
result := from
break
}
result := subjectLength
break
}
let searchLength := mload(search)
let subjectStart := add(subject, 0x20)
result := not(0) // Initialize to `NOT_FOUND`.
subject := add(subjectStart, from)
let end := add(sub(add(subjectStart, subjectLength), searchLength), 1)
let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
let s := mload(add(search, 0x20))
if iszero(and(lt(subject, end), lt(from, subjectLength))) { break }
if iszero(lt(searchLength, 0x20)) {
for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} {
if iszero(shr(m, xor(mload(subject), s))) {
if eq(keccak256(subject, searchLength), 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 `search` in `subject`,
/// searching from left to right.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function indexOf(string memory subject, string memory search)
internal
pure
returns (uint256 result)
{
result = indexOf(subject, search, 0);
}
/// @dev Returns the byte index of the first location of `search` in `subject`,
/// searching from right to left, starting from `from`.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function lastIndexOf(string memory subject, string memory search, uint256 from)
internal
pure
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
for {} 1 {} {
result := not(0) // Initialize to `NOT_FOUND`.
let searchLength := mload(search)
if gt(searchLength, mload(subject)) { break }
let w := result
let fromMax := sub(mload(subject), searchLength)
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(search, 0x20), searchLength) } 1 {} {
if eq(keccak256(subject, searchLength), 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 `search` in `subject`,
/// searching from right to left.
/// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found.
function lastIndexOf(string memory subject, string memory search)
internal
pure
returns (uint256 result)
{
result = lastIndexOf(subject, search, uint256(int256(-1)));
}
/// @dev Returns true if `search` is found in `subject`, false otherwise.
function contains(string memory subject, string memory search) internal pure returns (bool) {
return indexOf(subject, search) != NOT_FOUND;
}
/// @dev Returns whether `subject` starts with `search`.
function startsWith(string memory subject, string memory search)
internal
pure
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
let searchLength := mload(search)
// Just using keccak256 directly is actually cheaper.
// forgefmt: disable-next-item
result := and(
iszero(gt(searchLength, mload(subject))),
eq(
keccak256(add(subject, 0x20), searchLength),
keccak256(add(search, 0x20), searchLength)
)
)
}
}
/// @dev Returns whether `subject` ends with `search`.
function endsWith(string memory subject, string memory search)
internal
pure
returns (bool result)
{
/// @solidity memory-safe-assembly
assembly {
let searchLength := mload(search)
let subjectLength := mload(subject)
// Whether `search` is not longer than `subject`.
let withinRange := iszero(gt(searchLength, subjectLength))
// Just using keccak256 directly is actually cheaper.
// forgefmt: disable-next-item
result := and(
withinRange,
eq(
keccak256(
// `subject + 0x20 + max(subjectLength - searchLength, 0)`.
add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))),
searchLength
),
keccak256(add(search, 0x20), searchLength)
)
)
}
}
/// @dev Returns `subject` repeated `times`.
function repeat(string memory subject, uint256 times)
internal
pure
returns (string memory result)
{
/// @solidity memory-safe-assembly
assembly {
let subjectLength := mload(subject)
if iszero(or(iszero(times), iszero(subjectLength))) {
subject := add(subject, 0x20)
result := mload(0x40)
let output := add(result, 0x20)
for {} 1 {} {
// Copy the `subject` one word at a time.
for { let o := 0 } 1 {} {
mstore(add(output, o), mload(add(subject, o)))
o := add(o, 0x20)
if iszero(lt(o, subjectLength)) { break }
}
output := add(output, subjectLength)
times := sub(times, 1)
if iszero(times) { break }
}
mstore(output, 0) // Zeroize the slot after the string.
let resultLength := sub(output, add(result, 0x20))
mstore(result, resultLength) // Store the length.
mstore(0x40, add(result, add(resultLength, 0x40))) // Allocate the memory.
}
}
}
/// @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 subjectLength := mload(subject)
if iszero(gt(subjectLength, end)) { end := subjectLength }
if iszero(gt(subjectLength, start)) { start := subjectLength }
if lt(start, end) {
result := mload(0x40)
let resultLength := sub(end, start)
mstore(result, resultLength)
subject := add(subject, start)
let w := not(0x1f)
// Copy the `subject` one word at a time, backwards.
for { let o := and(add(resultLength, 0x1f), w) } 1 {} {
mstore(add(result, o), mload(add(subject, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
// Zeroize the slot after the string.
mstore(add(add(result, 0x20), resultLength), 0)
mstore(0x40, add(result, add(resultLength, 0x40))) // Allocate the memory.
}
}
}
/// @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, uint256(int256(-1)));
}
/// @dev Returns all the indices of `search` in `subject`.
/// The indices are byte offsets.
function indicesOf(string memory subject, string memory search)
internal
pure
returns (uint256[] memory result)
{
/// @solidity memory-safe-assembly
assembly {
let subjectLength := mload(subject)
let searchLength := mload(search)
if iszero(gt(searchLength, subjectLength)) {
subject := add(subject, 0x20)
search := add(search, 0x20)
result := add(mload(0x40), 0x20)
let subjectStart := subject
let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1)
let h := 0
if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) }
let m := shl(3, sub(0x20, and(searchLength, 0x1f)))
let s := mload(search)
for {} 1 {} {
let t := mload(subject)
// Whether the first `searchLength % 32` bytes of
// `subject` and `search` matches.
if iszero(shr(m, xor(t, s))) {
if h {
if iszero(eq(keccak256(subject, searchLength), h)) {
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
// Append to `result`.
mstore(result, sub(subject, subjectStart))
result := add(result, 0x20)
// Advance `subject` by `searchLength`.
subject := add(subject, searchLength)
if searchLength {
if iszero(lt(subject, subjectSearchEnd)) { break }
continue
}
}
subject := add(subject, 1)
if iszero(lt(subject, subjectSearchEnd)) { break }
}
let resultEnd := result
// Assign `result` to the free memory pointer.
result := mload(0x40)
// Store the length of `result`.
mstore(result, shr(5, sub(resultEnd, add(result, 0x20))))
// Allocate memory for result.
// We allocate one more word, so this array can be recycled for {split}.
mstore(0x40, add(resultEnd, 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))
let prevIndex := 0
for {} 1 {} {
let index := mload(indexPtr)
mstore(indexPtr, 0x60)
if iszero(eq(index, prevIndex)) {
let element := mload(0x40)
let elementLength := sub(index, prevIndex)
mstore(element, elementLength)
// Copy the `subject` one word at a time, backwards.
for { let o := and(add(elementLength, 0x1f), w) } 1 {} {
mstore(add(element, o), mload(add(add(subject, prevIndex), o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
// Zeroize the slot after the string.
mstore(add(add(element, 0x20), elementLength), 0)
// Allocate memory for the length and the bytes,
// rounded up to a multiple of 32.
mstore(0x40, add(element, and(add(elementLength, 0x3f), w)))
// Store the `element` into the array.
mstore(indexPtr, element)
}
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 {
let w := not(0x1f)
result := mload(0x40)
let aLength := mload(a)
// Copy `a` one word at a time, backwards.
for { let o := and(add(aLength, 0x20), w) } 1 {} {
mstore(add(result, o), mload(add(a, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
let bLength := mload(b)
let output := add(result, aLength)
// Copy `b` one word at a time, backwards.
for { let o := and(add(bLength, 0x20), w) } 1 {} {
mstore(add(output, o), mload(add(b, o)))
o := add(o, w) // `sub(o, 0x20)`.
if iszero(o) { break }
}
let totalLength := add(aLength, bLength)
let last := add(add(result, 0x20), totalLength)
mstore(last, 0) // Zeroize the slot after the string.
mstore(result, totalLength) // Store the length.
mstore(0x40, add(last, 0x20)) // Allocate the 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 length := mload(subject)
if length {
result := add(mload(0x40), 0x20)
subject := add(subject, 1)
let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff)
let w := not(0)
for { let o := length } 1 {} {
o := add(o, w)
let b := and(0xff, mload(add(subject, o)))
mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20)))
if iszero(o) { break }
}
result := mload(0x40)
mstore(result, length) // Store the length.
let last := add(add(result, 0x20), length)
mstore(last, 0) // Zeroize the slot after the string.
mstore(0x40, add(last, 0x20)) // Allocate the 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 the 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 {
let end := add(s, mload(s))
result := add(mload(0x40), 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(result, c)
result := add(result, 1)
continue
}
let t := shr(248, mload(c))
mstore(result, mload(and(t, 0x1f)))
result := add(result, shr(5, t))
}
let last := result
mstore(last, 0) // Zeroize the slot after the string.
result := mload(0x40)
mstore(result, sub(last, add(result, 0x20))) // Store the length.
mstore(0x40, add(last, 0x20)) // Allocate the 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 {
let end := add(s, mload(s))
result := add(mload(0x40), 0x20)
if addDoubleQuotes {
mstore8(result, 34)
result := add(1, result)
}
// 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 {} 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(result, c)
result := add(result, 1)
continue
}
mstore8(result, 0x5c) // "\\".
mstore8(add(result, 1), c)
result := add(result, 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(result, mload(0x19)) // "\\u00XX".
result := add(result, 6)
continue
}
mstore8(result, 0x5c) // "\\".
mstore8(add(result, 1), mload(add(c, 8)))
result := add(result, 2)
}
if addDoubleQuotes {
mstore8(result, 34)
result := add(1, result)
}
let last := result
mstore(last, 0) // Zeroize the slot after the string.
result := mload(0x40)
mstore(result, sub(last, add(result, 0x20))) // Store the length.
mstore(0x40, add(last, 0x20)) // Allocate the 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 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 aLength := 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, aLength)), mload(add(a, aLength))),
mload(sub(add(b, 0x1e), aLength))
),
// `totalLength != 0 && totalLength < 31`. Abuses underflow.
// Assumes that the lengths are valid and within the block gas limit.
lt(sub(add(aLength, 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.0;
/// @title ILockable
/// @dev Interface for the Lockable extension
/// @author filio.eth
interface IERC721Lockable {
/**
* @dev Emitted when `id` token is locked, and `unlocker` is stated as unlocking wallet.
*/
event Lock (address indexed unlocker, uint256 indexed id);
/**
* @dev Emitted when `id` token is unlocked.
*/
event Unlock (uint256 indexed id);
/**
* @dev Locks the `id` token and states `unlocker` wallet as unlocker.
*/
function lock(address unlocker, uint256 id) external;
/**
* @dev Unlocks the `id` token.
*/
function unlock(uint256 id) external;
/**
* @dev Returns the wallet, that is stated as unlocking wallet for the `tokenId` token.
* If address(0) returned, that means token is not locked. Any other result means token is locked.
*/
function getLocked(uint256 tokenId) external view returns (address);
}{
"remappings": [
"@common-resources/crate/=node_modules/@common-resources/crate/src/",
"@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/",
"@prb/test/=node_modules/@prb/test/",
"forge-std/=node_modules/forge-std/",
"solady/=node_modules/solady/",
"token-types/=node_modules/token-types/",
"4naly3er/=node_modules/4naly3er/",
"@layerzerolabs/=node_modules/@layerzerolabs/",
"@rari-capital/=node_modules/@rari-capital/",
"diamond3/=node_modules/diamond3/",
"erc721-lockable/=node_modules/erc721-lockable/",
"erc721s/=node_modules/erc721s/",
"solady/=node_modules/solady/",
"token-types/=node_modules/token-types/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "none",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "shanghai",
"viaIR": false,
"libraries": {}
}Contract ABI
API[{"inputs":[],"stateMutability":"payable","type":"constructor"},{"inputs":[],"name":"AccountBalanceOverflow","type":"error"},{"inputs":[],"name":"AllocationOutOfBounds","type":"error"},{"inputs":[],"name":"AllocationOverflow","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[],"name":"Blacklisted","type":"error"},{"inputs":[],"name":"CannotIncreaseMaxSupplyAfterMint","type":"error"},{"inputs":[],"name":"ClaimSupply","type":"error"},{"inputs":[],"name":"DisabledRoyalties","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"FrozenRoyalties","type":"error"},{"inputs":[],"name":"InsufficientPayment","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"InvalidLength","type":"error"},{"inputs":[],"name":"InvalidRange","type":"error"},{"inputs":[],"name":"ListClaimSupply","type":"error"},{"inputs":[],"name":"ListDeleted","type":"error"},{"inputs":[],"name":"ListMaxSupply","type":"error"},{"inputs":[],"name":"ListPaused","type":"error"},{"inputs":[],"name":"ListTimeOutOfBounds","type":"error"},{"inputs":[],"name":"ListTimestampEnd","type":"error"},{"inputs":[],"name":"ListUnknown","type":"error"},{"inputs":[],"name":"MaxReferral","type":"error"},{"inputs":[],"name":"MaxSupply","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"NoTreasury","type":"error"},{"inputs":[],"name":"NotEligible","type":"error"},{"inputs":[],"name":"NotFactory","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"NotOwnerNorApproved","type":"error"},{"inputs":[],"name":"NotZero","type":"error"},{"inputs":[],"name":"Reentrancy","type":"error"},{"inputs":[],"name":"ReservedMaxSupply","type":"error"},{"inputs":[],"name":"ReservedSupply","type":"error"},{"inputs":[],"name":"RoyaltyOverflow","type":"error"},{"inputs":[],"name":"RoyaltyReceiverIsZeroAddress","type":"error"},{"inputs":[],"name":"SelfReferral","type":"error"},{"inputs":[],"name":"SupplyUnderflow","type":"error"},{"inputs":[],"name":"TimeOutOfBounds","type":"error"},{"inputs":[],"name":"TimestampEnd","type":"error"},{"inputs":[],"name":"TokenAlreadyExists","type":"error"},{"inputs":[],"name":"TokenDoesNotExist","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"TreasuryExists","type":"error"},{"inputs":[],"name":"URIPermanent","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"UnderSupply","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"isApproved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"baseURI_","type":"string"},{"indexed":false,"internalType":"string","name":"fileExtension_","type":"string"}],"name":"BaseURIUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"fromTokenId_","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"toTokenId_","type":"uint256"}],"name":"BatchMetadataUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"fromTokenId_","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"toTokenId_","type":"uint256"}],"name":"BatchPermanentURI","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"blacklistedAssets","type":"address[]"},{"indexed":true,"internalType":"bool","name":"status","type":"bool"}],"name":"BlacklistUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"name_","type":"string"},{"indexed":false,"internalType":"string","name":"symbol_","type":"string"}],"name":"ContractCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"uri_","type":"string"}],"name":"ContractURIUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"feeRecipients_","type":"address[]"},{"indexed":false,"internalType":"uint16[]","name":"fees_","type":"uint16[]"}],"name":"FeeUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"launchpad_","type":"address"}],"name":"LaunchpadUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"minter_","type":"address"},{"indexed":true,"internalType":"uint8","name":"listId_","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"ListMinted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"MetadataUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"listId_","type":"uint8"}],"name":"MintListDeleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"listId_","type":"uint8"},{"indexed":false,"internalType":"bool","name":"paused_","type":"bool"}],"name":"MintListStatus","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint8","name":"listId_","type":"uint8"},{"components":[{"internalType":"bytes32","name":"root","type":"bytes32"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint32","name":"unit","type":"uint32"},{"internalType":"uint32","name":"userSupply","type":"uint32"},{"internalType":"uint32","name":"maxSupply","type":"uint32"},{"internalType":"uint32","name":"start","type":"uint32"},{"internalType":"uint32","name":"end","type":"uint32"},{"internalType":"bool","name":"reserved","type":"bool"},{"internalType":"bool","name":"paused","type":"bool"}],"indexed":false,"internalType":"struct MintList","name":"list_","type":"tuple"}],"name":"MintListUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"start_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"end_","type":"uint256"}],"name":"MintPeriodUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"minter_","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"Minted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"uri_","type":"string"},{"indexed":true,"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"PermanentURI","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"price_","type":"uint256"}],"name":"PriceUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"referral_","type":"address"},{"indexed":true,"internalType":"address","name":"referred_","type":"address"},{"indexed":false,"internalType":"uint256","name":"value_","type":"uint256"}],"name":"Referral","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"referralFee_","type":"uint16"}],"name":"ReferralFeeUpdate","type":"event"},{"anonymous":false,"inputs":[],"name":"RoyaltiesDisabled","type":"event"},{"anonymous":false,"inputs":[],"name":"RoyaltiesFrozen","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"tokenId_","type":"uint256"},{"indexed":true,"internalType":"address","name":"receiver_","type":"address"},{"indexed":false,"internalType":"uint96","name":"bps_","type":"uint96"}],"name":"RoyaltiesUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"supply_","type":"uint256"}],"name":"SupplyUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"minAllocation","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maxAllocation","type":"uint256"}],"name":"TreasuryUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"value","type":"string"},{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"URI","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"unit_","type":"uint256"}],"name":"UnitUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"claimable_","type":"uint256"}],"name":"UserClaimableUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"to_","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"wallet_","type":"address"}],"name":"claimedOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"contractURI","outputs":[{"internalType":"string","name":"contractURI_","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultRoyalty","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"listId_","type":"uint8"}],"name":"deleteList","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"disableRoyalties","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"end","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"feeRanges","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"feeRecipients","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"fees","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"freezeRoyalties","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"freezeTokenURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"freezeURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBlacklist","outputs":[{"internalType":"address[]","name":"blacklist_","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"listId_","type":"uint8"}],"name":"getList","outputs":[{"components":[{"internalType":"bytes32","name":"root","type":"bytes32"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint32","name":"unit","type":"uint32"},{"internalType":"uint32","name":"userSupply","type":"uint32"},{"internalType":"uint32","name":"maxSupply","type":"uint32"},{"internalType":"uint32","name":"start","type":"uint32"},{"internalType":"uint32","name":"end","type":"uint32"},{"internalType":"bool","name":"reserved","type":"bool"},{"internalType":"bool","name":"paused","type":"bool"}],"internalType":"struct MintList","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"launchpad_","type":"address"},{"components":[{"internalType":"address","name":"masterCopy","type":"address"},{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"},{"internalType":"address[]","name":"feeRecipients","type":"address[]"},{"internalType":"uint16[2][]","name":"feeRanges","type":"uint16[2][]"},{"internalType":"uint16[2]","name":"royalty","type":"uint16[2]"},{"internalType":"uint256","name":"deployFee","type":"uint256"},{"internalType":"bool","name":"paused","type":"bool"}],"internalType":"struct NftPolicy","name":"policy_","type":"tuple"},{"components":[{"internalType":"uint64","name":"policyId","type":"uint64"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint16[]","name":"fees","type":"uint16[]"},{"internalType":"uint16","name":"royalty","type":"uint16"},{"internalType":"uint32","name":"maxSupply","type":"uint32"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"},{"internalType":"address","name":"parent","type":"address"}],"internalType":"struct NftConfig","name":"config_","type":"tuple"}],"name":"initialize","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint32","name":"maxSupply_","type":"uint32"},{"internalType":"uint16","name":"royalty_","type":"uint16"},{"internalType":"address","name":"owner_","type":"address"},{"internalType":"uint256","name":"price_","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"launchpad","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"listId_","type":"uint8"},{"internalType":"address","name":"wallet_","type":"address"}],"name":"listClaimedOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"listIndex","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"listId_","type":"uint8"}],"name":"listSupply","outputs":[{"internalType":"uint32","name":"supply","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"listId_","type":"uint8"}],"name":"lists","outputs":[{"internalType":"bytes32","name":"root","type":"bytes32"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint32","name":"unit","type":"uint32"},{"internalType":"uint32","name":"userSupply","type":"uint32"},{"internalType":"uint32","name":"maxSupply","type":"uint32"},{"internalType":"uint32","name":"start","type":"uint32"},{"internalType":"uint32","name":"end","type":"uint32"},{"internalType":"bool","name":"reserved","type":"bool"},{"internalType":"bool","name":"paused","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxAllocation","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSupply","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minAllocation","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"address","name":"referral_","type":"address"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"address","name":"referral_","type":"address"},{"internalType":"uint16","name":"allocation_","type":"uint16"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"uint16","name":"allocation_","type":"uint16"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"proof_","type":"bytes32[]"},{"internalType":"uint8","name":"listId_","type":"uint8"},{"internalType":"address","name":"recipient_","type":"address"},{"internalType":"uint32","name":"amount_","type":"uint32"},{"internalType":"address","name":"referral_","type":"address"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"name_","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numerator","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"parent","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"listId_","type":"uint8"}],"name":"pauseList","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"permanentURI","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"price","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"referralFee","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token_","type":"address"},{"internalType":"address","name":"recipient_","type":"address"}],"name":"rescueERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token_","type":"address"},{"internalType":"address","name":"recipient_","type":"address"},{"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"rescueERC721","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"royaltiesFrozen","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"salePrice","type":"uint256"}],"name":"royaltyInfo","outputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"royaltyAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"royaltyRange","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"isApproved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"baseURI_","type":"string"},{"internalType":"string","name":"fileExtension_","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets_","type":"address[]"},{"internalType":"bool","name":"status_","type":"bool"}],"name":"setBlacklist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"claimable_","type":"uint32"}],"name":"setClaimableUserSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"contractURI_","type":"string"}],"name":"setContractURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16[]","name":"fees_","type":"uint16[]"},{"internalType":"uint16","name":"minAllocation_","type":"uint16"},{"internalType":"uint16","name":"maxAllocation_","type":"uint16"}],"name":"setFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"price_","type":"uint256"},{"internalType":"uint8","name":"listId_","type":"uint8"},{"internalType":"bytes32","name":"root_","type":"bytes32"},{"internalType":"uint32","name":"userSupply_","type":"uint32"},{"internalType":"uint32","name":"maxSupply_","type":"uint32"},{"internalType":"uint32","name":"start_","type":"uint32"},{"internalType":"uint32","name":"end_","type":"uint32"},{"internalType":"uint32","name":"unit_","type":"uint32"},{"internalType":"bool","name":"reserved_","type":"bool"},{"internalType":"bool","name":"paused_","type":"bool"}],"name":"setList","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8[]","name":"ids_","type":"uint8[]"},{"components":[{"internalType":"bytes32","name":"root","type":"bytes32"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint32","name":"unit","type":"uint32"},{"internalType":"uint32","name":"userSupply","type":"uint32"},{"internalType":"uint32","name":"maxSupply","type":"uint32"},{"internalType":"uint32","name":"start","type":"uint32"},{"internalType":"uint32","name":"end","type":"uint32"},{"internalType":"bool","name":"reserved","type":"bool"},{"internalType":"bool","name":"paused","type":"bool"}],"internalType":"struct MintList[]","name":"lists_","type":"tuple[]"}],"name":"setLists","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"start_","type":"uint32"},{"internalType":"uint32","name":"end_","type":"uint32"}],"name":"setMintPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"price_","type":"uint256"}],"name":"setPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"bps_","type":"uint16"}],"name":"setReferralFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient_","type":"address"},{"internalType":"uint96","name":"bps_","type":"uint96"}],"name":"setRoyalties","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"maxSupply_","type":"uint32"}],"name":"setSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId_","type":"uint256"},{"internalType":"address","name":"recipient_","type":"address"},{"internalType":"uint96","name":"bps_","type":"uint96"}],"name":"setTokenRoyalties","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId_","type":"uint256"},{"internalType":"string","name":"tokenURI_","type":"string"}],"name":"setTokenURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint24","name":"unit_","type":"uint24"}],"name":"setUnit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"splitter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"start","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId_","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"supported_","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"symbol_","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId_","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"tokenURI_","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"treasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unit","outputs":[{"internalType":"uint24","name":"","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"listId_","type":"uint8"}],"name":"unpauseList","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"treasury_","type":"address"},{"internalType":"address","name":"splitter_","type":"address"}],"name":"updateSetup","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"userSupply","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.