Overview
ETH Balance
ETH Value
$0.00Latest 25 from a total of 5,733 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Whitelist Mint B... | 3289276 | 624 days ago | IN | 0.00067 ETH | 0.00003714 | ||||
| Whitelist Mint B... | 3288982 | 624 days ago | IN | 0.00067 ETH | 0.00003816 | ||||
| Whitelist Mint B... | 3288982 | 624 days ago | IN | 0.00067 ETH | 0.00004006 | ||||
| Whitelist Mint B... | 3288975 | 624 days ago | IN | 0.00067 ETH | 0.00004487 | ||||
| Whitelist Mint B... | 3288962 | 624 days ago | IN | 0.00067 ETH | 0.0000411 | ||||
| Whitelist Mint B... | 3288958 | 624 days ago | IN | 0.00067 ETH | 0.00004405 | ||||
| Whitelist Mint B... | 3288958 | 624 days ago | IN | 0.00067 ETH | 0.00004409 | ||||
| Whitelist Mint B... | 3288958 | 624 days ago | IN | 0.00067 ETH | 0.00004409 | ||||
| Whitelist Mint B... | 3288958 | 624 days ago | IN | 0.00067 ETH | 0.00004405 | ||||
| Whitelist Mint B... | 3288958 | 624 days ago | IN | 0.00067 ETH | 0.00004223 | ||||
| Whitelist Mint B... | 3288958 | 624 days ago | IN | 0.00067 ETH | 0.00004223 | ||||
| Whitelist Mint B... | 3288957 | 624 days ago | IN | 0.00067 ETH | 0.00004223 | ||||
| Whitelist Mint B... | 3288957 | 624 days ago | IN | 0.00067 ETH | 0.00004218 | ||||
| Whitelist Mint B... | 3288956 | 624 days ago | IN | 0.00067 ETH | 0.00004223 | ||||
| Whitelist Mint B... | 3288954 | 624 days ago | IN | 0.00067 ETH | 0.00004209 | ||||
| Whitelist Mint B... | 3288954 | 624 days ago | IN | 0.00067 ETH | 0.00004209 | ||||
| Whitelist Mint B... | 3288954 | 624 days ago | IN | 0.00067 ETH | 0.00004209 | ||||
| Whitelist Mint B... | 3288954 | 624 days ago | IN | 0.00067 ETH | 0.00004209 | ||||
| Whitelist Mint B... | 3288953 | 624 days ago | IN | 0.00067 ETH | 0.00004218 | ||||
| Whitelist Mint B... | 3288953 | 624 days ago | IN | 0.00067 ETH | 0.00004218 | ||||
| Whitelist Mint B... | 3288953 | 624 days ago | IN | 0.00067 ETH | 0.00004218 | ||||
| Whitelist Mint B... | 3288953 | 624 days ago | IN | 0.00067 ETH | 0.00004218 | ||||
| Whitelist Mint B... | 3288927 | 624 days ago | IN | 0.00067 ETH | 0.00004081 | ||||
| Whitelist Mint B... | 3288927 | 624 days ago | IN | 0.00067 ETH | 0.00004081 | ||||
| Whitelist Mint B... | 3288927 | 624 days ago | IN | 0.00067 ETH | 0.00004081 |
Latest 25 internal transactions (View All)
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 3289276 | 624 days ago | 0.00067 ETH | ||||
| 3288982 | 624 days ago | 0.00067 ETH | ||||
| 3288982 | 624 days ago | 0.00067 ETH | ||||
| 3288975 | 624 days ago | 0.00067 ETH | ||||
| 3288962 | 624 days ago | 0.00067 ETH | ||||
| 3288958 | 624 days ago | 0.00067 ETH | ||||
| 3288958 | 624 days ago | 0.00067 ETH | ||||
| 3288958 | 624 days ago | 0.00067 ETH | ||||
| 3288958 | 624 days ago | 0.00067 ETH | ||||
| 3288958 | 624 days ago | 0.00067 ETH | ||||
| 3288958 | 624 days ago | 0.00067 ETH | ||||
| 3288957 | 624 days ago | 0.00067 ETH | ||||
| 3288957 | 624 days ago | 0.00067 ETH | ||||
| 3288956 | 624 days ago | 0.00067 ETH | ||||
| 3288954 | 624 days ago | 0.00067 ETH | ||||
| 3288954 | 624 days ago | 0.00067 ETH | ||||
| 3288954 | 624 days ago | 0.00067 ETH | ||||
| 3288954 | 624 days ago | 0.00067 ETH | ||||
| 3288953 | 624 days ago | 0.00067 ETH | ||||
| 3288953 | 624 days ago | 0.00067 ETH | ||||
| 3288953 | 624 days ago | 0.00067 ETH | ||||
| 3288953 | 624 days ago | 0.00067 ETH | ||||
| 3288927 | 624 days ago | 0.00067 ETH | ||||
| 3288927 | 624 days ago | 0.00067 ETH | ||||
| 3288927 | 624 days ago | 0.00067 ETH |
Cross-Chain Transactions
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
// forgefmt: disable-start
//
//
//
// ░▒▓███████▓▒░░▒▓█▓▒░ ░▒▓██████▓▒░░▒▓████████▓▒░▒▓████████▓▒░ ░▒▓██████▓▒░░▒▓███████▓▒░ ░▒▓██████▓▒░
// ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░
// ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓██▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░
// ░▒▓███████▓▒░░▒▓█▓▒░ ░▒▓████████▓▒░ ░▒▓██▓▒░ ░▒▓██████▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒▒▓███▓▒░
// ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░░▒▓██▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░
// ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓██▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░
// ░▒▓███████▓▒░░▒▓████████▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓████████▓▒░▒▓████████▓▒░▒▓██▓▒░░▒▓██████▓▒░░▒▓█▓▒░░▒▓█▓▒░░▒▓██████▓▒░
//
//
//
// forgefmt: disable-end
import { Ownable } from "@openzeppelin/access/Ownable.sol";
import { IPyth } from "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
import { PythStructs } from "@pythnetwork/pyth-sdk-solidity/PythStructs.sol";
import { IBlastPoints } from "src/lib/IBlastPoints.sol";
import {
Params,
POINTS_OPERATOR,
PROTOCOL_RECEIVER,
ETHUSD_PRICE_FEED
} from "src/lib/Structs.sol";
import { BlazeType } from "src/BlazeType.sol";
/// @title BlazeManager
/// @author www.blaze.ong
/// @notice Blaze0ng NFT Launchpad protocol manager.
contract BlazeManager_v0 is Ownable(msg.sender) {
/// @notice Amount in wei that is charged on each mint
uint256 public mintFee;
/// @notice Emitted on each new BlazeType deployment.
/// @param sender The address that deployed the BlazeType.
/// @param blazeType The address of the deployed BlazeType.
event Deployed(address indexed sender, address indexed blazeType);
constructor() {
IBlastPoints(0x2536FE9ab3F511540F2f9e2eC2A805005C3Dd800).configurePointsOperator(
POINTS_OPERATOR
);
mintFee = 670000000000000;
}
/// @notice Deploys a new BlazeType contract.
/// @param _params The BlazeType parameters.
/// @param _name The BlazeType name.
/// @param _symbol The BlazeType symbol.
/// @param _owner The BlazeType owner.
/// @return The address of the deployed BlazeType.
function deployBlazeType(
Params calldata _params,
string calldata _name,
string calldata _symbol,
address _owner
) external returns (address) {
BlazeType blazeType = new BlazeType(_params, _name, _symbol, _owner);
emit Deployed(msg.sender, address(blazeType));
return address(blazeType);
}
/// @notice Mints a BlazeType.
/// @param _blazeType The BlazeType to mint.
/// @param _amount The amount of NFTs to mint.
function mintBlazeType(
BlazeType _blazeType,
uint256 _amount
) external payable {
(bool success,) = address(PROTOCOL_RECEIVER).call{ value: mintFee }("");
require(success, "BlazeManager: fee transfer failed");
(success,) = address(_blazeType).call{ value: msg.value - mintFee }(
abi.encodeWithSignature("mint(address,uint256)", msg.sender, _amount)
);
require(success, "BlazeManager: mint failed");
}
/// @notice Mints a BlazeType.
/// @param _blazeType The BlazeType to mint.
/// @param _phase The phase of the whitelist.
/// @param _merkleProof The Merkle proof for the whitelist.
/// @param _amount The amount of NFTs to mint.
function whitelistMintBlazeType(
BlazeType _blazeType,
uint8 _phase,
bytes32[] calldata _merkleProof,
uint256 _amount
) external payable {
(bool success,) = address(PROTOCOL_RECEIVER).call{ value: mintFee }("");
require(success, "BlazeManager: fee transfer failed");
(success,) = address(_blazeType).call{ value: msg.value - mintFee }(
abi.encodeWithSignature(
"whitelistMint(uint8,bytes32[],address,uint256)",
_phase,
_merkleProof,
msg.sender,
_amount
)
);
require(success, "BlazeManager: mint failed");
}
function setMintBasis(uint256 _mintFee) external onlyOwner {
mintFee = _mintFee;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import "./PythStructs.sol";
import "./IPythEvents.sol";
/// @title Consume prices from the Pyth Network (https://pyth.network/).
/// @dev Please refer to the guidance at https://docs.pyth.network/documentation/pythnet-price-feeds/best-practices for how to consume prices safely.
/// @author Pyth Data Association
interface IPyth is IPythEvents {
/// @notice Returns the period (in seconds) that a price feed is considered valid since its publish time
function getValidTimePeriod() external view returns (uint validTimePeriod);
/// @notice Returns the price and confidence interval.
/// @dev Reverts if the price has not been updated within the last `getValidTimePeriod()` seconds.
/// @param id The Pyth Price Feed ID of which to fetch the price and confidence interval.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getPrice(
bytes32 id
) external view returns (PythStructs.Price memory price);
/// @notice Returns the exponentially-weighted moving average price and confidence interval.
/// @dev Reverts if the EMA price is not available.
/// @param id The Pyth Price Feed ID of which to fetch the EMA price and confidence interval.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getEmaPrice(
bytes32 id
) external view returns (PythStructs.Price memory price);
/// @notice Returns the price of a price feed without any sanity checks.
/// @dev This function returns the most recent price update in this contract without any recency checks.
/// This function is unsafe as the returned price update may be arbitrarily far in the past.
///
/// Users of this function should check the `publishTime` in the price to ensure that the returned price is
/// sufficiently recent for their application. If you are considering using this function, it may be
/// safer / easier to use either `getPrice` or `getPriceNoOlderThan`.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getPriceUnsafe(
bytes32 id
) external view returns (PythStructs.Price memory price);
/// @notice Returns the price that is no older than `age` seconds of the current time.
/// @dev This function is a sanity-checked version of `getPriceUnsafe` which is useful in
/// applications that require a sufficiently-recent price. Reverts if the price wasn't updated sufficiently
/// recently.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getPriceNoOlderThan(
bytes32 id,
uint age
) external view returns (PythStructs.Price memory price);
/// @notice Returns the exponentially-weighted moving average price of a price feed without any sanity checks.
/// @dev This function returns the same price as `getEmaPrice` in the case where the price is available.
/// However, if the price is not recent this function returns the latest available price.
///
/// The returned price can be from arbitrarily far in the past; this function makes no guarantees that
/// the returned price is recent or useful for any particular application.
///
/// Users of this function should check the `publishTime` in the price to ensure that the returned price is
/// sufficiently recent for their application. If you are considering using this function, it may be
/// safer / easier to use either `getEmaPrice` or `getEmaPriceNoOlderThan`.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getEmaPriceUnsafe(
bytes32 id
) external view returns (PythStructs.Price memory price);
/// @notice Returns the exponentially-weighted moving average price that is no older than `age` seconds
/// of the current time.
/// @dev This function is a sanity-checked version of `getEmaPriceUnsafe` which is useful in
/// applications that require a sufficiently-recent price. Reverts if the price wasn't updated sufficiently
/// recently.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getEmaPriceNoOlderThan(
bytes32 id,
uint age
) external view returns (PythStructs.Price memory price);
/// @notice Update price feeds with given update messages.
/// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
/// `getUpdateFee` with the length of the `updateData` array.
/// Prices will be updated if they are more recent than the current stored prices.
/// The call will succeed even if the update is not the most recent.
/// @dev Reverts if the transferred fee is not sufficient or the updateData is invalid.
/// @param updateData Array of price update data.
function updatePriceFeeds(bytes[] calldata updateData) external payable;
/// @notice Wrapper around updatePriceFeeds that rejects fast if a price update is not necessary. A price update is
/// necessary if the current on-chain publishTime is older than the given publishTime. It relies solely on the
/// given `publishTimes` for the price feeds and does not read the actual price update publish time within `updateData`.
///
/// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
/// `getUpdateFee` with the length of the `updateData` array.
///
/// `priceIds` and `publishTimes` are two arrays with the same size that correspond to senders known publishTime
/// of each priceId when calling this method. If all of price feeds within `priceIds` have updated and have
/// a newer or equal publish time than the given publish time, it will reject the transaction to save gas.
/// Otherwise, it calls updatePriceFeeds method to update the prices.
///
/// @dev Reverts if update is not needed or the transferred fee is not sufficient or the updateData is invalid.
/// @param updateData Array of price update data.
/// @param priceIds Array of price ids.
/// @param publishTimes Array of publishTimes. `publishTimes[i]` corresponds to known `publishTime` of `priceIds[i]`
function updatePriceFeedsIfNecessary(
bytes[] calldata updateData,
bytes32[] calldata priceIds,
uint64[] calldata publishTimes
) external payable;
/// @notice Returns the required fee to update an array of price updates.
/// @param updateData Array of price update data.
/// @return feeAmount The required fee in Wei.
function getUpdateFee(
bytes[] calldata updateData
) external view returns (uint feeAmount);
/// @notice Parse `updateData` and return price feeds of the given `priceIds` if they are all published
/// within `minPublishTime` and `maxPublishTime`.
///
/// You can use this method if you want to use a Pyth price at a fixed time and not the most recent price;
/// otherwise, please consider using `updatePriceFeeds`. This method may store the price updates on-chain, if they
/// are more recent than the current stored prices.
///
/// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
/// `getUpdateFee` with the length of the `updateData` array.
///
///
/// @dev Reverts if the transferred fee is not sufficient or the updateData is invalid or there is
/// no update for any of the given `priceIds` within the given time range.
/// @param updateData Array of price update data.
/// @param priceIds Array of price ids.
/// @param minPublishTime minimum acceptable publishTime for the given `priceIds`.
/// @param maxPublishTime maximum acceptable publishTime for the given `priceIds`.
/// @return priceFeeds Array of the price feeds corresponding to the given `priceIds` (with the same order).
function parsePriceFeedUpdates(
bytes[] calldata updateData,
bytes32[] calldata priceIds,
uint64 minPublishTime,
uint64 maxPublishTime
) external payable returns (PythStructs.PriceFeed[] memory priceFeeds);
/// @notice Similar to `parsePriceFeedUpdates` but ensures the updates returned are
/// the first updates published in minPublishTime. That is, if there are multiple updates for a given timestamp,
/// this method will return the first update. This method may store the price updates on-chain, if they
/// are more recent than the current stored prices.
///
///
/// @dev Reverts if the transferred fee is not sufficient or the updateData is invalid or there is
/// no update for any of the given `priceIds` within the given time range and uniqueness condition.
/// @param updateData Array of price update data.
/// @param priceIds Array of price ids.
/// @param minPublishTime minimum acceptable publishTime for the given `priceIds`.
/// @param maxPublishTime maximum acceptable publishTime for the given `priceIds`.
/// @return priceFeeds Array of the price feeds corresponding to the given `priceIds` (with the same order).
function parsePriceFeedUpdatesUnique(
bytes[] calldata updateData,
bytes32[] calldata priceIds,
uint64 minPublishTime,
uint64 maxPublishTime
) external payable returns (PythStructs.PriceFeed[] memory priceFeeds);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
contract PythStructs {
// A price with a degree of uncertainty, represented as a price +- a confidence interval.
//
// The confidence interval roughly corresponds to the standard error of a normal distribution.
// Both the price and confidence are stored in a fixed-point numeric representation,
// `x * (10^expo)`, where `expo` is the exponent.
//
// Please refer to the documentation at https://docs.pyth.network/documentation/pythnet-price-feeds/best-practices for how
// to how this price safely.
struct Price {
// Price
int64 price;
// Confidence interval around the price
uint64 conf;
// Price exponent
int32 expo;
// Unix timestamp describing when the price was published
uint publishTime;
}
// PriceFeed represents a current aggregate price from pyth publisher feeds.
struct PriceFeed {
// The price ID.
bytes32 id;
// Latest available price
Price price;
// Latest available exponentially-weighted moving average price
Price emaPrice;
}
}pragma solidity ^0.8.20;
interface IBlastPoints {
function configurePointsOperator(address operator) external;
function configurePointsOperatorOnBehalf(address contractAddress, address operator)
external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
/// @notice Struct for a single mint phase.
/// @param start Timestamp for the phase's start.
/// @param end Timestamp for the phase's end.
/// @param whitelistMintPrice Price for a whitelist mint in wei.
/// @param maxWhitelistMintsPerCall Maximum amount of whitelist mints in one call.
/// @param maxTotalWhitelistMints Maximum amount of whitelist mints per address.
/// @param merkleRoot Root hash for the merkle tree.
struct WhitelistPhase {
uint256 start;
uint256 end;
uint120 whitelistMintPrice;
uint64 maxWhitelistMintsPerCall;
uint64 maxTotalWhitelistMints;
bytes32 merkleRoot;
}
/// @notice The configuration paramater storage of the contract.
/// @param baseURI The base URI for the token URIs.
/// @param contractURI The URI for the contract's metadata.
/// @param mintsToOwner The amount the owner mints to themselves.
/// @param maxSupply The maximum amount of tokens that can be minted.
/// @param start Timestamp for the public mint's start.
/// @param end Timestamp for the public mint's end.
/// @param mintPrice Price for a mint in wei.
/// @param maxMintsPerCall Maximum amount of mints in one call.
/// @param maxTotalMints Maximum amount of mints per address.
/// @param defaultRoyalty The default royalty percentage.
/// @param royaltyReceiver The address that receives the royalties.
/// @param whitelistPhases Array of up to three WhitelistPhase's.
struct Params {
string baseURI;
string contractURI;
uint256 mintsToOwner;
uint256 maxSupply;
uint256 start;
uint256 end;
uint120 mintPrice;
uint64 maxMintsPerCall;
uint64 maxTotalMints;
uint16 defaultRoyalty;
address royaltyReceiver;
WhitelistPhase[] whitelistPhases;
}
/// @notice The locks for the contract.
/// @param baseURILocked If the `baseURI` is locked.
/// @param contractURILocked If the `contractURI` is locked.
/// @param royaltyParamsLocked If the default royalty is locked.
struct Locks {
bool baseURILocked;
bool contractURILocked;
bool royaltyParamsLocked;
}
uint16 constant MAX_BPS = 5000; // 50%
uint16 constant MAX_ROYALTY = 2000; // 20%
address constant POINTS_OPERATOR = 0xf8a82748e7DF10D0684B758d02cF6c43AD83AD25;
address constant PROTOCOL_RECEIVER = 0xa804b7d57a4A872dbfbE2987347403039aDe20dC;
address constant BLAST_PYTH = 0xA2aa501b19aff244D90cc15a4Cf739D2725B5729;
bytes32 constant ETHUSD_PRICE_FEED =
0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
// forgefmt: disable-start
//
//
//
// ░▒▓███████▓▒░░▒▓█▓▒░ ░▒▓██████▓▒░░▒▓████████▓▒░▒▓████████▓▒░ ░▒▓██████▓▒░░▒▓███████▓▒░ ░▒▓██████▓▒░
// ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░
// ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓██▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░
// ░▒▓███████▓▒░░▒▓█▓▒░ ░▒▓████████▓▒░ ░▒▓██▓▒░ ░▒▓██████▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒▒▓███▓▒░
// ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░░▒▓██▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░
// ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓██▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░
// ░▒▓███████▓▒░░▒▓████████▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓████████▓▒░▒▓████████▓▒░▒▓██▓▒░░▒▓██████▓▒░░▒▓█▓▒░░▒▓█▓▒░░▒▓██████▓▒░
//
//
//
// forgefmt: disable-end
import { ERC721A } from "ERC721A/ERC721A.sol";
import { ERC2981 } from "@solady/tokens/ERC2981.sol";
import { Ownable2Step, Ownable } from "@openzeppelin/access/Ownable2Step.sol";
import { LibString } from "@solady/utils/LibString.sol";
import { MerkleProofLib } from "@solady/utils/MerkleProofLib.sol";
import { IBlast } from "src/lib/IBlast.sol";
import { IBlastPoints } from "src/lib/IBlastPoints.sol";
import "src/lib/Errors.sol";
import "src/lib/Structs.sol";
/// @title BlazeType
/// @author www.blaze.ong
/// @notice Blaze0ng NFT Launchpad ERC721 contract.
contract BlazeType is ERC721A, ERC2981, Ownable2Step {
// -------------------------------------------------------------------------
// Storage
// -------------------------------------------------------------------------
Params public params;
Locks public locks;
/// @notice The amount of funds recived from all mints.
uint256 private _totalFundsReceived;
/// @notice Mapping for tracking whitelist claims.
mapping(uint8 phase => mapping(address listee => uint256)) whitelistClaims;
bool private _mintingClosed;
// -------------------------------------------------------------------------
// Events
// -------------------------------------------------------------------------
/// @notice Emitted when the collection owner stops the public mint.
event StopMint();
/// @notice Emitted when the `baseURI` is updated via `setBaseURI()`.
event BaseURIUpdated();
/// @notice Emitted when the `contractURI` is updated via `setContractURI()`.
event ContractURIUpdated();
/// @notice Emitted when the `maxSupply` is updated via `setMaxSupply()` or
/// `stopMint()`.
event MaxSupplyUpdated(uint256 newMaxSupply);
/// @notice Emitted when the royalty parameters are updated via `setRoyaltyParams()`.
event RoyaltyParamsUpdated(address _royaltyReceiver, uint16 _defaultRoyalty);
/// @notice BlazeType constructor
/// @param _params Initial parameters for the contract.
/// @param _name The name of the NFT.
/// @param _symbol The symbol of the NFT.
/// @param _owner The owner of the collection.
constructor(
Params memory _params,
string memory _name,
string memory _symbol,
address _owner
) ERC721A(_name, _symbol) Ownable(_owner) {
IBlast(0x4300000000000000000000000000000000000002).configureAutomaticYield();
IBlastPoints(0x2536FE9ab3F511540F2f9e2eC2A805005C3Dd800).configurePointsOperator(
POINTS_OPERATOR
);
if (
keccak256(bytes(_params.baseURI)) == keccak256(bytes(""))
|| _params.defaultRoyalty > MAX_ROYALTY
) {
revert InvalidParams();
}
_setDefaultRoyalty(_params.royaltyReceiver, _params.defaultRoyalty);
if (_params.mintsToOwner > 0) _mint(_owner, _params.mintsToOwner);
params = _params;
_validateWhitelistPhases(_params.whitelistPhases);
}
function supportsInterface(bytes4 interfaceId)
public
view
override(ERC721A, ERC2981)
returns (bool)
{
return
ERC721A.supportsInterface(interfaceId) || ERC2981.supportsInterface(interfaceId);
}
function tokenURI(uint256 _tokenId) public view override returns (string memory) {
if (!_exists(_tokenId)) revert TokenDoesNotExist();
return string(abi.encodePacked(params.baseURI, LibString.toString(_tokenId)));
}
function contractURI() external view returns (string memory) {
return params.contractURI;
}
// -------------------------------------------------------------------------
// External
// -------------------------------------------------------------------------
/// @notice Returns the whitelist phases for the NFT.
/// @return The array of whitelist phases.
function whitelistPhases() external view returns (WhitelistPhase[] memory) {
return params.whitelistPhases;
}
/// @notice Mints an NFT to `_to`.
/// @param _to Address that receives the NFT.
/// @param _amount Amount of NFT's to mint.
function mint(address _to, uint256 _amount) external payable {
if (params.start > block.timestamp || params.end < block.timestamp || _mintingClosed)
{
revert MintNotActive();
}
if (_nextTokenId() + _amount > params.maxSupply) revert MaxSupplyReached();
if (msg.value < params.mintPrice * _amount) revert InsufficientFunds();
_totalFundsReceived += msg.value;
_mint(_to, _amount);
}
/// @notice Mints an NFT for a whitelisted address
/// @param _phase The whitelist phase.
/// @param _merkleProof The merkle proof for the whitelist.
/// @param _to Address that receives the NFT.
/// @param _amount Amount of NFT's to mint.
function whitelistMint(
uint8 _phase,
bytes32[] calldata _merkleProof,
address _to,
uint256 _amount
) external payable {
WhitelistPhase memory thisPhase = params.whitelistPhases[_phase];
if (thisPhase.start > block.timestamp || thisPhase.end < block.timestamp) {
revert WhitelistPhaseNotActive();
}
if (_nextTokenId() + _amount > params.maxSupply) revert MaxSupplyReached();
if (msg.value < thisPhase.whitelistMintPrice * _amount) revert InsufficientFunds();
bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(_to, _amount))));
if (!MerkleProofLib.verifyCalldata(_merkleProof, thisPhase.merkleRoot, leaf)) {
revert InvalidMerkleProof();
}
_totalFundsReceived += msg.value;
_mint(_to, _amount);
}
// -------------------------------------------------------------------------
// OnlyOwner
// -------------------------------------------------------------------------
/// @notice Updates collection params in a batch.
function ownerBatch(bytes[] calldata _calls)
external
onlyOwner
returns (bytes[] memory results)
{
results = new bytes[](_calls.length);
for (uint256 i; i < _calls.length; i++) {
(bool success, bytes memory result) = address(this).delegatecall(_calls[i]);
if (!success) {
if (result.length < 68) revert("Transaction reverted without a reason");
bytes memory returnData;
assembly {
returnData := add(returnData, 0x04)
}
revert(string(returnData));
}
results[i] = result;
}
}
/// @notice Withdraws the mint funds to the collection owner.
function withdraw() external onlyOwner {
(bool success,) = msg.sender.call{ value: address(this).balance }("");
if(!success) {
revert WithdrawalFailed();
}
_totalFundsReceived = 0;
}
/// @notice Withdraws a specified amount of mint funds to the collection owner.
/// @param _amount The amount to withdraw.
function withdraw(uint256 _amount) external onlyOwner {
_amount > _totalFundsReceived
? _totalFundsReceived = 0
: _totalFundsReceived -= _amount;
(bool success,) = msg.sender.call{ value: _amount }("");
if(!success) {
revert WithdrawalFailed();
}
}
/// @notice Allows owner to arbitrarily end public mint capping supply at the current
/// amount.
function stopMint() external onlyOwner {
_mintingClosed = true;
uint256 newMaxSupply = _nextTokenId();
params.maxSupply = newMaxSupply;
emit StopMint();
emit MaxSupplyUpdated(newMaxSupply);
}
/// @notice Allows the owner to claim the yield generated from the mints.
function claimYield() external onlyOwner {
(bool success,) = msg.sender.call{ value: address(this).balance - _totalFundsReceived }("");
if(!success) {
revert WithdrawalFailed();
}
}
/// @notice Allows the owner to claim a specific amount of yield.
/// @param _amount The amount of yield to claim.
function claimYield(uint256 _amount) external onlyOwner {
if (address(this).balance - _totalFundsReceived < _amount) {
revert InsufficientFunds();
}
(bool success,) = msg.sender.call{ value: _amount }("");
if(!success) {
revert WithdrawalFailed();
}
}
/// @notice Setter for `baseURI`.
/// @param _baseUri The `baseURI` for the NFTs.
function setBaseURI(string calldata _baseUri) external onlyOwner {
if (locks.baseURILocked) revert BaseURILocked();
params.baseURI = _baseUri;
emit BaseURIUpdated();
}
/// @notice Permanently locks `baseURI`.
function lockBaseURI() external onlyOwner {
locks.baseURILocked = true;
}
/// @notice Setter for `contractURI`.
/// @param _contractURI The `contractURI` for the NFTs.
function setContractURI(string calldata _contractURI) external onlyOwner {
if (locks.contractURILocked) revert ContractURILocked();
params.contractURI = _contractURI;
emit ContractURIUpdated();
}
/// @notice Permanently locks `contractURI`.
function lockContractURI() external onlyOwner {
locks.contractURILocked = true;
}
/// @notice Setter for `maxSupply`.
/// @param _newMaxSupply The new maximum supply for the NFTs.
function setMaxSupply(uint256 _newMaxSupply) external onlyOwner {
_validateParamUpdate();
if (_newMaxSupply < _nextTokenId()) revert InvalidMaxSupply();
params.maxSupply = _newMaxSupply;
emit MaxSupplyUpdated(_newMaxSupply);
}
/// @notice Sets the mint price for the public mint.
/// @param _mintPrice The mint price for the public mint.
function setMintPrice(uint120 _mintPrice) external onlyOwner {
_validateParamUpdate();
params.mintPrice = _mintPrice;
}
/// @notice Sets the mint price for the given whitelist phase.
/// @param _phase The whitelist phase to set the mint price for.
/// @param _whitelistMintPrice The mint price for the whitelist phase.
function setWhitelistMintPrice(uint8 _phase, uint120 _whitelistMintPrice)
external
onlyOwner
{
_validateParamUpdate();
params.whitelistPhases[_phase].whitelistMintPrice = _whitelistMintPrice;
}
/// @notice Setter for the receiving address and bps for royalties.
/// @param _royaltyReceiver The receiving address for any royalties.
/// @param _defaultRoyalty The bps for royalties.
function setRoyaltyParams(address _royaltyReceiver, uint16 _defaultRoyalty)
external
onlyOwner
{
if (locks.royaltyParamsLocked) revert RoyaltyParamsLocked();
if (_defaultRoyalty > MAX_ROYALTY) revert InvalidDefaultRoyalty();
params.royaltyReceiver = _royaltyReceiver;
params.defaultRoyalty = _defaultRoyalty;
_setDefaultRoyalty(_royaltyReceiver, _defaultRoyalty);
emit RoyaltyParamsUpdated(_royaltyReceiver, _defaultRoyalty);
}
/// @notice Permanently locks `royaltyReceiver` and `defaultRoyalty`.
function lockRoyaltyParams() external onlyOwner {
locks.royaltyParamsLocked = true;
}
// -------------------------------------------------------------------------
// Internal
// -------------------------------------------------------------------------
/// @notice Validates whitelist phases.
/// @param _phases The whitelist phases to be validated.
function _validateWhitelistPhases(WhitelistPhase[] memory _phases) internal view {
// Check for length > 3
if (_phases.length > 3) revert InvalidWhitelistPhases();
// Check that phase start must be before phase end, phase ends must be before public
// mint start, and that phases do not overlap.
uint256 prevEnd;
for (uint256 i; i < _phases.length;) {
if (
_phases[i].start > _phases[i].end || _phases[i].end > params.start
|| (_phases[i].start != 0 && _phases[i].start < prevEnd)
) {
revert InvalidWhitelistPhases();
}
prevEnd = _phases[i].end;
unchecked {
++i;
}
}
}
/// @notice Checks that minting is active and reverts if true. Ensures parameters cannot
/// be changed during minting phases.
function _validateParamUpdate() internal view {
if (block.timestamp > params.start && block.timestamp < params.end) {
revert MintingActive();
}
for (uint256 i; i < params.whitelistPhases.length;) {
if (
block.timestamp > params.whitelistPhases[i].start
&& block.timestamp < params.whitelistPhases[i].end
) revert MintingActive();
unchecked {
++i;
}
}
}
}// 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: Apache-2.0
pragma solidity ^0.8.0;
/// @title IPythEvents contains the events that Pyth contract emits.
/// @dev This interface can be used for listening to the updates for off-chain and testing purposes.
interface IPythEvents {
/// @dev Emitted when the price feed with `id` has received a fresh update.
/// @param id The Pyth Price Feed ID.
/// @param publishTime Publish time of the given price update.
/// @param price Price of the given price update.
/// @param conf Confidence interval of the given price update.
event PriceFeedUpdate(
bytes32 indexed id,
uint64 publishTime,
int64 price,
uint64 conf
);
}// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.3.0
// Creator: Chiru Labs
pragma solidity ^0.8.4;
import './IERC721A.sol';
/**
* @dev Interface of ERC721 token receiver.
*/
interface ERC721A__IERC721Receiver {
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}
/**
* @title ERC721A
*
* @dev Implementation of the [ERC721](https://eips.ethereum.org/EIPS/eip-721)
* Non-Fungible Token Standard, including the Metadata extension.
* Optimized for lower gas during batch mints.
*
* Token IDs are minted in sequential order (e.g. 0, 1, 2, 3, ...)
* starting from `_startTokenId()`.
*
* The `_sequentialUpTo()` function can be overriden to enable spot mints
* (i.e. non-consecutive mints) for `tokenId`s greater than `_sequentialUpTo()`.
*
* Assumptions:
*
* - An owner cannot have more than 2**64 - 1 (max value of uint64) of supply.
* - The maximum token ID cannot exceed 2**256 - 1 (max value of uint256).
*/
contract ERC721A is IERC721A {
// Bypass for a `--via-ir` bug (https://github.com/chiru-labs/ERC721A/pull/364).
struct TokenApprovalRef {
address value;
}
// =============================================================
// CONSTANTS
// =============================================================
// Mask of an entry in packed address data.
uint256 private constant _BITMASK_ADDRESS_DATA_ENTRY = (1 << 64) - 1;
// The bit position of `numberMinted` in packed address data.
uint256 private constant _BITPOS_NUMBER_MINTED = 64;
// The bit position of `numberBurned` in packed address data.
uint256 private constant _BITPOS_NUMBER_BURNED = 128;
// The bit position of `aux` in packed address data.
uint256 private constant _BITPOS_AUX = 192;
// Mask of all 256 bits in packed address data except the 64 bits for `aux`.
uint256 private constant _BITMASK_AUX_COMPLEMENT = (1 << 192) - 1;
// The bit position of `startTimestamp` in packed ownership.
uint256 private constant _BITPOS_START_TIMESTAMP = 160;
// The bit mask of the `burned` bit in packed ownership.
uint256 private constant _BITMASK_BURNED = 1 << 224;
// The bit position of the `nextInitialized` bit in packed ownership.
uint256 private constant _BITPOS_NEXT_INITIALIZED = 225;
// The bit mask of the `nextInitialized` bit in packed ownership.
uint256 private constant _BITMASK_NEXT_INITIALIZED = 1 << 225;
// The bit position of `extraData` in packed ownership.
uint256 private constant _BITPOS_EXTRA_DATA = 232;
// Mask of all 256 bits in a packed ownership except the 24 bits for `extraData`.
uint256 private constant _BITMASK_EXTRA_DATA_COMPLEMENT = (1 << 232) - 1;
// The mask of the lower 160 bits for addresses.
uint256 private constant _BITMASK_ADDRESS = (1 << 160) - 1;
// The maximum `quantity` that can be minted with {_mintERC2309}.
// This limit is to prevent overflows on the address data entries.
// For a limit of 5000, a total of 3.689e15 calls to {_mintERC2309}
// is required to cause an overflow, which is unrealistic.
uint256 private constant _MAX_MINT_ERC2309_QUANTITY_LIMIT = 5000;
// The `Transfer` event signature is given by:
// `keccak256(bytes("Transfer(address,address,uint256)"))`.
bytes32 private constant _TRANSFER_EVENT_SIGNATURE =
0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;
// =============================================================
// STORAGE
// =============================================================
// The next token ID to be minted.
uint256 private _currentIndex;
// The number of tokens burned.
uint256 private _burnCounter;
// Token name
string private _name;
// Token symbol
string private _symbol;
// Mapping from token ID to ownership details
// An empty struct value does not necessarily mean the token is unowned.
// See {_packedOwnershipOf} implementation for details.
//
// Bits Layout:
// - [0..159] `addr`
// - [160..223] `startTimestamp`
// - [224] `burned`
// - [225] `nextInitialized`
// - [232..255] `extraData`
mapping(uint256 => uint256) private _packedOwnerships;
// Mapping owner address to address data.
//
// Bits Layout:
// - [0..63] `balance`
// - [64..127] `numberMinted`
// - [128..191] `numberBurned`
// - [192..255] `aux`
mapping(address => uint256) private _packedAddressData;
// Mapping from token ID to approved address.
mapping(uint256 => TokenApprovalRef) private _tokenApprovals;
// Mapping from owner to operator approvals
mapping(address => mapping(address => bool)) private _operatorApprovals;
// The amount of tokens minted above `_sequentialUpTo()`.
// We call these spot mints (i.e. non-sequential mints).
uint256 private _spotMinted;
// =============================================================
// CONSTRUCTOR
// =============================================================
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
_currentIndex = _startTokenId();
if (_sequentialUpTo() < _startTokenId()) _revert(SequentialUpToTooSmall.selector);
}
// =============================================================
// TOKEN COUNTING OPERATIONS
// =============================================================
/**
* @dev Returns the starting token ID for sequential mints.
*
* Override this function to change the starting token ID for sequential mints.
*
* Note: The value returned must never change after any tokens have been minted.
*/
function _startTokenId() internal view virtual returns (uint256) {
return 0;
}
/**
* @dev Returns the maximum token ID (inclusive) for sequential mints.
*
* Override this function to return a value less than 2**256 - 1,
* but greater than `_startTokenId()`, to enable spot (non-sequential) mints.
*
* Note: The value returned must never change after any tokens have been minted.
*/
function _sequentialUpTo() internal view virtual returns (uint256) {
return type(uint256).max;
}
/**
* @dev Returns the next token ID to be minted.
*/
function _nextTokenId() internal view virtual returns (uint256) {
return _currentIndex;
}
/**
* @dev Returns the total number of tokens in existence.
* Burned tokens will reduce the count.
* To get the total number of tokens minted, please see {_totalMinted}.
*/
function totalSupply() public view virtual override returns (uint256 result) {
// Counter underflow is impossible as `_burnCounter` cannot be incremented
// more than `_currentIndex + _spotMinted - _startTokenId()` times.
unchecked {
// With spot minting, the intermediate `result` can be temporarily negative,
// and the computation must be unchecked.
result = _currentIndex - _burnCounter - _startTokenId();
if (_sequentialUpTo() != type(uint256).max) result += _spotMinted;
}
}
/**
* @dev Returns the total amount of tokens minted in the contract.
*/
function _totalMinted() internal view virtual returns (uint256 result) {
// Counter underflow is impossible as `_currentIndex` does not decrement,
// and it is initialized to `_startTokenId()`.
unchecked {
result = _currentIndex - _startTokenId();
if (_sequentialUpTo() != type(uint256).max) result += _spotMinted;
}
}
/**
* @dev Returns the total number of tokens burned.
*/
function _totalBurned() internal view virtual returns (uint256) {
return _burnCounter;
}
/**
* @dev Returns the total number of tokens that are spot-minted.
*/
function _totalSpotMinted() internal view virtual returns (uint256) {
return _spotMinted;
}
// =============================================================
// ADDRESS DATA OPERATIONS
// =============================================================
/**
* @dev Returns the number of tokens in `owner`'s account.
*/
function balanceOf(address owner) public view virtual override returns (uint256) {
if (owner == address(0)) _revert(BalanceQueryForZeroAddress.selector);
return _packedAddressData[owner] & _BITMASK_ADDRESS_DATA_ENTRY;
}
/**
* Returns the number of tokens minted by `owner`.
*/
function _numberMinted(address owner) internal view returns (uint256) {
return (_packedAddressData[owner] >> _BITPOS_NUMBER_MINTED) & _BITMASK_ADDRESS_DATA_ENTRY;
}
/**
* Returns the number of tokens burned by or on behalf of `owner`.
*/
function _numberBurned(address owner) internal view returns (uint256) {
return (_packedAddressData[owner] >> _BITPOS_NUMBER_BURNED) & _BITMASK_ADDRESS_DATA_ENTRY;
}
/**
* Returns the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
*/
function _getAux(address owner) internal view returns (uint64) {
return uint64(_packedAddressData[owner] >> _BITPOS_AUX);
}
/**
* Sets the auxiliary data for `owner`. (e.g. number of whitelist mint slots used).
* If there are multiple variables, please pack them into a uint64.
*/
function _setAux(address owner, uint64 aux) internal virtual {
uint256 packed = _packedAddressData[owner];
uint256 auxCasted;
// Cast `aux` with assembly to avoid redundant masking.
assembly {
auxCasted := aux
}
packed = (packed & _BITMASK_AUX_COMPLEMENT) | (auxCasted << _BITPOS_AUX);
_packedAddressData[owner] = packed;
}
// =============================================================
// IERC165
// =============================================================
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
* to learn more about how these ids are created.
*
* This function call must use less than 30000 gas.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
// The interface IDs are constants representing the first 4 bytes
// of the XOR of all function selectors in the interface.
// See: [ERC165](https://eips.ethereum.org/EIPS/eip-165)
// (e.g. `bytes4(i.functionA.selector ^ i.functionB.selector ^ ...)`)
return
interfaceId == 0x01ffc9a7 || // ERC165 interface ID for ERC165.
interfaceId == 0x80ac58cd || // ERC165 interface ID for ERC721.
interfaceId == 0x5b5e139f; // ERC165 interface ID for ERC721Metadata.
}
// =============================================================
// IERC721Metadata
// =============================================================
/**
* @dev Returns the token collection name.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the token collection symbol.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
*/
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
if (!_exists(tokenId)) _revert(URIQueryForNonexistentToken.selector);
string memory baseURI = _baseURI();
return bytes(baseURI).length != 0 ? string(abi.encodePacked(baseURI, _toString(tokenId))) : '';
}
/**
* @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
* token will be the concatenation of the `baseURI` and the `tokenId`. Empty
* by default, it can be overridden in child contracts.
*/
function _baseURI() internal view virtual returns (string memory) {
return '';
}
// =============================================================
// OWNERSHIPS OPERATIONS
// =============================================================
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) public view virtual override returns (address) {
return address(uint160(_packedOwnershipOf(tokenId)));
}
/**
* @dev Gas spent here starts off proportional to the maximum mint batch size.
* It gradually moves to O(1) as tokens get transferred around over time.
*/
function _ownershipOf(uint256 tokenId) internal view virtual returns (TokenOwnership memory) {
return _unpackedOwnership(_packedOwnershipOf(tokenId));
}
/**
* @dev Returns the unpacked `TokenOwnership` struct at `index`.
*/
function _ownershipAt(uint256 index) internal view virtual returns (TokenOwnership memory) {
return _unpackedOwnership(_packedOwnerships[index]);
}
/**
* @dev Returns whether the ownership slot at `index` is initialized.
* An uninitialized slot does not necessarily mean that the slot has no owner.
*/
function _ownershipIsInitialized(uint256 index) internal view virtual returns (bool) {
return _packedOwnerships[index] != 0;
}
/**
* @dev Initializes the ownership slot minted at `index` for efficiency purposes.
*/
function _initializeOwnershipAt(uint256 index) internal virtual {
if (_packedOwnerships[index] == 0) {
_packedOwnerships[index] = _packedOwnershipOf(index);
}
}
/**
* @dev Returns the packed ownership data of `tokenId`.
*/
function _packedOwnershipOf(uint256 tokenId) private view returns (uint256 packed) {
if (_startTokenId() <= tokenId) {
packed = _packedOwnerships[tokenId];
if (tokenId > _sequentialUpTo()) {
if (_packedOwnershipExists(packed)) return packed;
_revert(OwnerQueryForNonexistentToken.selector);
}
// If the data at the starting slot does not exist, start the scan.
if (packed == 0) {
if (tokenId >= _currentIndex) _revert(OwnerQueryForNonexistentToken.selector);
// Invariant:
// There will always be an initialized ownership slot
// (i.e. `ownership.addr != address(0) && ownership.burned == false`)
// before an unintialized ownership slot
// (i.e. `ownership.addr == address(0) && ownership.burned == false`)
// Hence, `tokenId` will not underflow.
//
// We can directly compare the packed value.
// If the address is zero, packed will be zero.
for (;;) {
unchecked {
packed = _packedOwnerships[--tokenId];
}
if (packed == 0) continue;
if (packed & _BITMASK_BURNED == 0) return packed;
// Otherwise, the token is burned, and we must revert.
// This handles the case of batch burned tokens, where only the burned bit
// of the starting slot is set, and remaining slots are left uninitialized.
_revert(OwnerQueryForNonexistentToken.selector);
}
}
// Otherwise, the data exists and we can skip the scan.
// This is possible because we have already achieved the target condition.
// This saves 2143 gas on transfers of initialized tokens.
// If the token is not burned, return `packed`. Otherwise, revert.
if (packed & _BITMASK_BURNED == 0) return packed;
}
_revert(OwnerQueryForNonexistentToken.selector);
}
/**
* @dev Returns the unpacked `TokenOwnership` struct from `packed`.
*/
function _unpackedOwnership(uint256 packed) private pure returns (TokenOwnership memory ownership) {
ownership.addr = address(uint160(packed));
ownership.startTimestamp = uint64(packed >> _BITPOS_START_TIMESTAMP);
ownership.burned = packed & _BITMASK_BURNED != 0;
ownership.extraData = uint24(packed >> _BITPOS_EXTRA_DATA);
}
/**
* @dev Packs ownership data into a single uint256.
*/
function _packOwnershipData(address owner, uint256 flags) private view returns (uint256 result) {
assembly {
// Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
owner := and(owner, _BITMASK_ADDRESS)
// `owner | (block.timestamp << _BITPOS_START_TIMESTAMP) | flags`.
result := or(owner, or(shl(_BITPOS_START_TIMESTAMP, timestamp()), flags))
}
}
/**
* @dev Returns the `nextInitialized` flag set if `quantity` equals 1.
*/
function _nextInitializedFlag(uint256 quantity) private pure returns (uint256 result) {
// For branchless setting of the `nextInitialized` flag.
assembly {
// `(quantity == 1) << _BITPOS_NEXT_INITIALIZED`.
result := shl(_BITPOS_NEXT_INITIALIZED, eq(quantity, 1))
}
}
// =============================================================
// APPROVAL OPERATIONS
// =============================================================
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account. See {ERC721A-_approve}.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
*/
function approve(address to, uint256 tokenId) public payable virtual override {
_approve(to, tokenId, true);
}
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) public view virtual override returns (address) {
if (!_exists(tokenId)) _revert(ApprovalQueryForNonexistentToken.selector);
return _tokenApprovals[tokenId].value;
}
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom}
* for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) public virtual override {
_operatorApprovals[_msgSenderERC721A()][operator] = approved;
emit ApprovalForAll(_msgSenderERC721A(), operator, approved);
}
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}.
*/
function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
return _operatorApprovals[owner][operator];
}
/**
* @dev Returns whether `tokenId` exists.
*
* Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
*
* Tokens start existing when they are minted. See {_mint}.
*/
function _exists(uint256 tokenId) internal view virtual returns (bool result) {
if (_startTokenId() <= tokenId) {
if (tokenId > _sequentialUpTo()) return _packedOwnershipExists(_packedOwnerships[tokenId]);
if (tokenId < _currentIndex) {
uint256 packed;
while ((packed = _packedOwnerships[tokenId]) == 0) --tokenId;
result = packed & _BITMASK_BURNED == 0;
}
}
}
/**
* @dev Returns whether `packed` represents a token that exists.
*/
function _packedOwnershipExists(uint256 packed) private pure returns (bool result) {
assembly {
// The following is equivalent to `owner != address(0) && burned == false`.
// Symbolically tested.
result := gt(and(packed, _BITMASK_ADDRESS), and(packed, _BITMASK_BURNED))
}
}
/**
* @dev Returns whether `msgSender` is equal to `approvedAddress` or `owner`.
*/
function _isSenderApprovedOrOwner(
address approvedAddress,
address owner,
address msgSender
) private pure returns (bool result) {
assembly {
// Mask `owner` to the lower 160 bits, in case the upper bits somehow aren't clean.
owner := and(owner, _BITMASK_ADDRESS)
// Mask `msgSender` to the lower 160 bits, in case the upper bits somehow aren't clean.
msgSender := and(msgSender, _BITMASK_ADDRESS)
// `msgSender == owner || msgSender == approvedAddress`.
result := or(eq(msgSender, owner), eq(msgSender, approvedAddress))
}
}
/**
* @dev Returns the storage slot and value for the approved address of `tokenId`.
*/
function _getApprovedSlotAndAddress(uint256 tokenId)
private
view
returns (uint256 approvedAddressSlot, address approvedAddress)
{
TokenApprovalRef storage tokenApproval = _tokenApprovals[tokenId];
// The following is equivalent to `approvedAddress = _tokenApprovals[tokenId].value`.
assembly {
approvedAddressSlot := tokenApproval.slot
approvedAddress := sload(approvedAddressSlot)
}
}
// =============================================================
// TRANSFER OPERATIONS
// =============================================================
/**
* @dev Transfers `tokenId` from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token
* by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 tokenId
) public payable virtual override {
uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);
// Mask `from` to the lower 160 bits, in case the upper bits somehow aren't clean.
from = address(uint160(uint256(uint160(from)) & _BITMASK_ADDRESS));
if (address(uint160(prevOwnershipPacked)) != from) _revert(TransferFromIncorrectOwner.selector);
(uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
// The nested ifs save around 20+ gas over a compound boolean condition.
if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
if (!isApprovedForAll(from, _msgSenderERC721A())) _revert(TransferCallerNotOwnerNorApproved.selector);
_beforeTokenTransfers(from, to, tokenId, 1);
// Clear approvals from the previous owner.
assembly {
if approvedAddress {
// This is equivalent to `delete _tokenApprovals[tokenId]`.
sstore(approvedAddressSlot, 0)
}
}
// Underflow of the sender's balance is impossible because we check for
// ownership above and the recipient's balance can't realistically overflow.
// Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
unchecked {
// We can directly increment and decrement the balances.
--_packedAddressData[from]; // Updates: `balance -= 1`.
++_packedAddressData[to]; // Updates: `balance += 1`.
// Updates:
// - `address` to the next owner.
// - `startTimestamp` to the timestamp of transfering.
// - `burned` to `false`.
// - `nextInitialized` to `true`.
_packedOwnerships[tokenId] = _packOwnershipData(
to,
_BITMASK_NEXT_INITIALIZED | _nextExtraData(from, to, prevOwnershipPacked)
);
// If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
uint256 nextTokenId = tokenId + 1;
// If the next slot's address is zero and not burned (i.e. packed value is zero).
if (_packedOwnerships[nextTokenId] == 0) {
// If the next slot is within bounds.
if (nextTokenId != _currentIndex) {
// Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
_packedOwnerships[nextTokenId] = prevOwnershipPacked;
}
}
}
}
// Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
uint256 toMasked = uint256(uint160(to)) & _BITMASK_ADDRESS;
assembly {
// Emit the `Transfer` event.
log4(
0, // Start of data (0, since no data).
0, // End of data (0, since no data).
_TRANSFER_EVENT_SIGNATURE, // Signature.
from, // `from`.
toMasked, // `to`.
tokenId // `tokenId`.
)
}
if (toMasked == 0) _revert(TransferToZeroAddress.selector);
_afterTokenTransfers(from, to, tokenId, 1);
}
/**
* @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) public payable virtual override {
safeTransferFrom(from, to, tokenId, '');
}
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token
* by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement
* {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes memory _data
) public payable virtual override {
transferFrom(from, to, tokenId);
if (to.code.length != 0)
if (!_checkContractOnERC721Received(from, to, tokenId, _data)) {
_revert(TransferToNonERC721ReceiverImplementer.selector);
}
}
/**
* @dev Hook that is called before a set of serially-ordered token IDs
* are about to be transferred. This includes minting.
* And also called before burning one token.
*
* `startTokenId` - the first token ID to be transferred.
* `quantity` - the amount to be transferred.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
* transferred to `to`.
* - When `from` is zero, `tokenId` will be minted for `to`.
* - When `to` is zero, `tokenId` will be burned by `from`.
* - `from` and `to` are never both zero.
*/
function _beforeTokenTransfers(
address from,
address to,
uint256 startTokenId,
uint256 quantity
) internal virtual {}
/**
* @dev Hook that is called after a set of serially-ordered token IDs
* have been transferred. This includes minting.
* And also called after one token has been burned.
*
* `startTokenId` - the first token ID to be transferred.
* `quantity` - the amount to be transferred.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, `from`'s `tokenId` has been
* transferred to `to`.
* - When `from` is zero, `tokenId` has been minted for `to`.
* - When `to` is zero, `tokenId` has been burned by `from`.
* - `from` and `to` are never both zero.
*/
function _afterTokenTransfers(
address from,
address to,
uint256 startTokenId,
uint256 quantity
) internal virtual {}
/**
* @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target contract.
*
* `from` - Previous owner of the given token ID.
* `to` - Target address that will receive the token.
* `tokenId` - Token ID to be transferred.
* `_data` - Optional data to send along with the call.
*
* Returns whether the call correctly returned the expected magic value.
*/
function _checkContractOnERC721Received(
address from,
address to,
uint256 tokenId,
bytes memory _data
) private returns (bool) {
try ERC721A__IERC721Receiver(to).onERC721Received(_msgSenderERC721A(), from, tokenId, _data) returns (
bytes4 retval
) {
return retval == ERC721A__IERC721Receiver(to).onERC721Received.selector;
} catch (bytes memory reason) {
if (reason.length == 0) {
_revert(TransferToNonERC721ReceiverImplementer.selector);
}
assembly {
revert(add(32, reason), mload(reason))
}
}
}
// =============================================================
// MINT OPERATIONS
// =============================================================
/**
* @dev Mints `quantity` tokens and transfers them to `to`.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `quantity` must be greater than 0.
*
* Emits a {Transfer} event for each mint.
*/
function _mint(address to, uint256 quantity) internal virtual {
uint256 startTokenId = _currentIndex;
if (quantity == 0) _revert(MintZeroQuantity.selector);
_beforeTokenTransfers(address(0), to, startTokenId, quantity);
// Overflows are incredibly unrealistic.
// `balance` and `numberMinted` have a maximum limit of 2**64.
// `tokenId` has a maximum limit of 2**256.
unchecked {
// Updates:
// - `address` to the owner.
// - `startTimestamp` to the timestamp of minting.
// - `burned` to `false`.
// - `nextInitialized` to `quantity == 1`.
_packedOwnerships[startTokenId] = _packOwnershipData(
to,
_nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
);
// Updates:
// - `balance += quantity`.
// - `numberMinted += quantity`.
//
// We can directly add to the `balance` and `numberMinted`.
_packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);
// Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
uint256 toMasked = uint256(uint160(to)) & _BITMASK_ADDRESS;
if (toMasked == 0) _revert(MintToZeroAddress.selector);
uint256 end = startTokenId + quantity;
uint256 tokenId = startTokenId;
if (end - 1 > _sequentialUpTo()) _revert(SequentialMintExceedsLimit.selector);
do {
assembly {
// Emit the `Transfer` event.
log4(
0, // Start of data (0, since no data).
0, // End of data (0, since no data).
_TRANSFER_EVENT_SIGNATURE, // Signature.
0, // `address(0)`.
toMasked, // `to`.
tokenId // `tokenId`.
)
}
// The `!=` check ensures that large values of `quantity`
// that overflows uint256 will make the loop run out of gas.
} while (++tokenId != end);
_currentIndex = end;
}
_afterTokenTransfers(address(0), to, startTokenId, quantity);
}
/**
* @dev Mints `quantity` tokens and transfers them to `to`.
*
* This function is intended for efficient minting only during contract creation.
*
* It emits only one {ConsecutiveTransfer} as defined in
* [ERC2309](https://eips.ethereum.org/EIPS/eip-2309),
* instead of a sequence of {Transfer} event(s).
*
* Calling this function outside of contract creation WILL make your contract
* non-compliant with the ERC721 standard.
* For full ERC721 compliance, substituting ERC721 {Transfer} event(s) with the ERC2309
* {ConsecutiveTransfer} event is only permissible during contract creation.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `quantity` must be greater than 0.
*
* Emits a {ConsecutiveTransfer} event.
*/
function _mintERC2309(address to, uint256 quantity) internal virtual {
uint256 startTokenId = _currentIndex;
if (to == address(0)) _revert(MintToZeroAddress.selector);
if (quantity == 0) _revert(MintZeroQuantity.selector);
if (quantity > _MAX_MINT_ERC2309_QUANTITY_LIMIT) _revert(MintERC2309QuantityExceedsLimit.selector);
_beforeTokenTransfers(address(0), to, startTokenId, quantity);
// Overflows are unrealistic due to the above check for `quantity` to be below the limit.
unchecked {
// Updates:
// - `balance += quantity`.
// - `numberMinted += quantity`.
//
// We can directly add to the `balance` and `numberMinted`.
_packedAddressData[to] += quantity * ((1 << _BITPOS_NUMBER_MINTED) | 1);
// Updates:
// - `address` to the owner.
// - `startTimestamp` to the timestamp of minting.
// - `burned` to `false`.
// - `nextInitialized` to `quantity == 1`.
_packedOwnerships[startTokenId] = _packOwnershipData(
to,
_nextInitializedFlag(quantity) | _nextExtraData(address(0), to, 0)
);
if (startTokenId + quantity - 1 > _sequentialUpTo()) _revert(SequentialMintExceedsLimit.selector);
emit ConsecutiveTransfer(startTokenId, startTokenId + quantity - 1, address(0), to);
_currentIndex = startTokenId + quantity;
}
_afterTokenTransfers(address(0), to, startTokenId, quantity);
}
/**
* @dev Safely mints `quantity` tokens and transfers them to `to`.
*
* Requirements:
*
* - If `to` refers to a smart contract, it must implement
* {IERC721Receiver-onERC721Received}, which is called for each safe transfer.
* - `quantity` must be greater than 0.
*
* See {_mint}.
*
* Emits a {Transfer} event for each mint.
*/
function _safeMint(
address to,
uint256 quantity,
bytes memory _data
) internal virtual {
_mint(to, quantity);
unchecked {
if (to.code.length != 0) {
uint256 end = _currentIndex;
uint256 index = end - quantity;
do {
if (!_checkContractOnERC721Received(address(0), to, index++, _data)) {
_revert(TransferToNonERC721ReceiverImplementer.selector);
}
} while (index < end);
// This prevents reentrancy to `_safeMint`.
// It does not prevent reentrancy to `_safeMintSpot`.
if (_currentIndex != end) revert();
}
}
}
/**
* @dev Equivalent to `_safeMint(to, quantity, '')`.
*/
function _safeMint(address to, uint256 quantity) internal virtual {
_safeMint(to, quantity, '');
}
/**
* @dev Mints a single token at `tokenId`.
*
* Note: A spot-minted `tokenId` that has been burned can be re-minted again.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `tokenId` must be greater than `_sequentialUpTo()`.
* - `tokenId` must not exist.
*
* Emits a {Transfer} event for each mint.
*/
function _mintSpot(address to, uint256 tokenId) internal virtual {
if (tokenId <= _sequentialUpTo()) _revert(SpotMintTokenIdTooSmall.selector);
uint256 prevOwnershipPacked = _packedOwnerships[tokenId];
if (_packedOwnershipExists(prevOwnershipPacked)) _revert(TokenAlreadyExists.selector);
_beforeTokenTransfers(address(0), to, tokenId, 1);
// Overflows are incredibly unrealistic.
// The `numberMinted` for `to` is incremented by 1, and has a max limit of 2**64 - 1.
// `_spotMinted` is incremented by 1, and has a max limit of 2**256 - 1.
unchecked {
// Updates:
// - `address` to the owner.
// - `startTimestamp` to the timestamp of minting.
// - `burned` to `false`.
// - `nextInitialized` to `true` (as `quantity == 1`).
_packedOwnerships[tokenId] = _packOwnershipData(
to,
_nextInitializedFlag(1) | _nextExtraData(address(0), to, prevOwnershipPacked)
);
// Updates:
// - `balance += 1`.
// - `numberMinted += 1`.
//
// We can directly add to the `balance` and `numberMinted`.
_packedAddressData[to] += (1 << _BITPOS_NUMBER_MINTED) | 1;
// Mask `to` to the lower 160 bits, in case the upper bits somehow aren't clean.
uint256 toMasked = uint256(uint160(to)) & _BITMASK_ADDRESS;
if (toMasked == 0) _revert(MintToZeroAddress.selector);
assembly {
// Emit the `Transfer` event.
log4(
0, // Start of data (0, since no data).
0, // End of data (0, since no data).
_TRANSFER_EVENT_SIGNATURE, // Signature.
0, // `address(0)`.
toMasked, // `to`.
tokenId // `tokenId`.
)
}
++_spotMinted;
}
_afterTokenTransfers(address(0), to, tokenId, 1);
}
/**
* @dev Safely mints a single token at `tokenId`.
*
* Note: A spot-minted `tokenId` that has been burned can be re-minted again.
*
* Requirements:
*
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}.
* - `tokenId` must be greater than `_sequentialUpTo()`.
* - `tokenId` must not exist.
*
* See {_mintSpot}.
*
* Emits a {Transfer} event.
*/
function _safeMintSpot(
address to,
uint256 tokenId,
bytes memory _data
) internal virtual {
_mintSpot(to, tokenId);
unchecked {
if (to.code.length != 0) {
uint256 currentSpotMinted = _spotMinted;
if (!_checkContractOnERC721Received(address(0), to, tokenId, _data)) {
_revert(TransferToNonERC721ReceiverImplementer.selector);
}
// This prevents reentrancy to `_safeMintSpot`.
// It does not prevent reentrancy to `_safeMint`.
if (_spotMinted != currentSpotMinted) revert();
}
}
}
/**
* @dev Equivalent to `_safeMintSpot(to, tokenId, '')`.
*/
function _safeMintSpot(address to, uint256 tokenId) internal virtual {
_safeMintSpot(to, tokenId, '');
}
// =============================================================
// APPROVAL OPERATIONS
// =============================================================
/**
* @dev Equivalent to `_approve(to, tokenId, false)`.
*/
function _approve(address to, uint256 tokenId) internal virtual {
_approve(to, tokenId, false);
}
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the
* zero address clears previous approvals.
*
* Requirements:
*
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function _approve(
address to,
uint256 tokenId,
bool approvalCheck
) internal virtual {
address owner = ownerOf(tokenId);
if (approvalCheck && _msgSenderERC721A() != owner)
if (!isApprovedForAll(owner, _msgSenderERC721A())) {
_revert(ApprovalCallerNotOwnerNorApproved.selector);
}
_tokenApprovals[tokenId].value = to;
emit Approval(owner, to, tokenId);
}
// =============================================================
// BURN OPERATIONS
// =============================================================
/**
* @dev Equivalent to `_burn(tokenId, false)`.
*/
function _burn(uint256 tokenId) internal virtual {
_burn(tokenId, false);
}
/**
* @dev Destroys `tokenId`.
* The approval is cleared when the token is burned.
*
* Requirements:
*
* - `tokenId` must exist.
*
* Emits a {Transfer} event.
*/
function _burn(uint256 tokenId, bool approvalCheck) internal virtual {
uint256 prevOwnershipPacked = _packedOwnershipOf(tokenId);
address from = address(uint160(prevOwnershipPacked));
(uint256 approvedAddressSlot, address approvedAddress) = _getApprovedSlotAndAddress(tokenId);
if (approvalCheck) {
// The nested ifs save around 20+ gas over a compound boolean condition.
if (!_isSenderApprovedOrOwner(approvedAddress, from, _msgSenderERC721A()))
if (!isApprovedForAll(from, _msgSenderERC721A())) _revert(TransferCallerNotOwnerNorApproved.selector);
}
_beforeTokenTransfers(from, address(0), tokenId, 1);
// Clear approvals from the previous owner.
assembly {
if approvedAddress {
// This is equivalent to `delete _tokenApprovals[tokenId]`.
sstore(approvedAddressSlot, 0)
}
}
// Underflow of the sender's balance is impossible because we check for
// ownership above and the recipient's balance can't realistically overflow.
// Counter overflow is incredibly unrealistic as `tokenId` would have to be 2**256.
unchecked {
// Updates:
// - `balance -= 1`.
// - `numberBurned += 1`.
//
// We can directly decrement the balance, and increment the number burned.
// This is equivalent to `packed -= 1; packed += 1 << _BITPOS_NUMBER_BURNED;`.
_packedAddressData[from] += (1 << _BITPOS_NUMBER_BURNED) - 1;
// Updates:
// - `address` to the last owner.
// - `startTimestamp` to the timestamp of burning.
// - `burned` to `true`.
// - `nextInitialized` to `true`.
_packedOwnerships[tokenId] = _packOwnershipData(
from,
(_BITMASK_BURNED | _BITMASK_NEXT_INITIALIZED) | _nextExtraData(from, address(0), prevOwnershipPacked)
);
// If the next slot may not have been initialized (i.e. `nextInitialized == false`) .
if (prevOwnershipPacked & _BITMASK_NEXT_INITIALIZED == 0) {
uint256 nextTokenId = tokenId + 1;
// If the next slot's address is zero and not burned (i.e. packed value is zero).
if (_packedOwnerships[nextTokenId] == 0) {
// If the next slot is within bounds.
if (nextTokenId != _currentIndex) {
// Initialize the next slot to maintain correctness for `ownerOf(tokenId + 1)`.
_packedOwnerships[nextTokenId] = prevOwnershipPacked;
}
}
}
}
emit Transfer(from, address(0), tokenId);
_afterTokenTransfers(from, address(0), tokenId, 1);
// Overflow not possible, as `_burnCounter` cannot be exceed `_currentIndex + _spotMinted` times.
unchecked {
_burnCounter++;
}
}
// =============================================================
// EXTRA DATA OPERATIONS
// =============================================================
/**
* @dev Directly sets the extra data for the ownership data `index`.
*/
function _setExtraDataAt(uint256 index, uint24 extraData) internal virtual {
uint256 packed = _packedOwnerships[index];
if (packed == 0) _revert(OwnershipNotInitializedForExtraData.selector);
uint256 extraDataCasted;
// Cast `extraData` with assembly to avoid redundant masking.
assembly {
extraDataCasted := extraData
}
packed = (packed & _BITMASK_EXTRA_DATA_COMPLEMENT) | (extraDataCasted << _BITPOS_EXTRA_DATA);
_packedOwnerships[index] = packed;
}
/**
* @dev Called during each token transfer to set the 24bit `extraData` field.
* Intended to be overridden by the cosumer contract.
*
* `previousExtraData` - the value of `extraData` before transfer.
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
* transferred to `to`.
* - When `from` is zero, `tokenId` will be minted for `to`.
* - When `to` is zero, `tokenId` will be burned by `from`.
* - `from` and `to` are never both zero.
*/
function _extraData(
address from,
address to,
uint24 previousExtraData
) internal view virtual returns (uint24) {}
/**
* @dev Returns the next extra data for the packed ownership data.
* The returned result is shifted into position.
*/
function _nextExtraData(
address from,
address to,
uint256 prevOwnershipPacked
) private view returns (uint256) {
uint24 extraData = uint24(prevOwnershipPacked >> _BITPOS_EXTRA_DATA);
return uint256(_extraData(from, to, extraData)) << _BITPOS_EXTRA_DATA;
}
// =============================================================
// OTHER OPERATIONS
// =============================================================
/**
* @dev Returns the message sender (defaults to `msg.sender`).
*
* If you are writing GSN compatible contracts, you need to override this function.
*/
function _msgSenderERC721A() internal view virtual returns (address) {
return msg.sender;
}
/**
* @dev Converts a uint256 to its ASCII string decimal representation.
*/
function _toString(uint256 value) internal pure virtual returns (string memory str) {
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. Total: 5 * 0x20 = 0xa0.
let m := add(mload(0x40), 0xa0)
// Update the free memory pointer to allocate.
mstore(0x40, m)
// Assign the `str` to the end.
str := sub(m, 0x20)
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end of the memory to calculate the length later.
let end := str
// We write the string from rightmost digit to leftmost digit.
// The following is essentially a do-while loop that also handles the zero case.
// prettier-ignore
for { let temp := value } 1 {} {
str := sub(str, 1)
// Write the character to the pointer.
// The ASCII index of the '0' character is 48.
mstore8(str, add(48, mod(temp, 10)))
// Keep dividing `temp` until zero.
temp := div(temp, 10)
// prettier-ignore
if iszero(temp) { break }
}
let length := sub(end, str)
// Move the pointer 32 bytes leftwards to make room for the length.
str := sub(str, 0x20)
// Store the length.
mstore(str, length)
}
}
/**
* @dev For more efficient reverts.
*/
function _revert(bytes4 errorSelector) internal pure {
assembly {
mstore(0x00, errorSelector)
revert(0x00, 0x04)
}
}
}// 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.0) (access/Ownable2Step.sol)
pragma solidity ^0.8.20;
import {Ownable} from "./Ownable.sol";
/**
* @dev Contract module which provides access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* This extension of the {Ownable} contract includes a two-step mechanism to transfer
* ownership, where the new owner must call {acceptOwnership} in order to replace the
* old one. This can help prevent common mistakes, such as transfers of ownership to
* incorrect accounts, or to contracts that are unable to interact with the
* permission system.
*
* The initial owner is specified at deployment time in the constructor for `Ownable`. This
* can later be changed with {transferOwnership} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/
abstract contract Ownable2Step is Ownable {
address private _pendingOwner;
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
/**
* @dev Returns the address of the pending owner.
*/
function pendingOwner() public view virtual returns (address) {
return _pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual override onlyOwner {
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual override {
delete _pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/
function acceptOwnership() public virtual {
address sender = _msgSender();
if (pendingOwner() != sender) {
revert OwnableUnauthorizedAccount(sender);
}
_transferOwnership(sender);
}
}// 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();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The constant returned when the `search` is not found in the string.
uint256 internal constant NOT_FOUND = type(uint256).max;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* 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)
// Update the free memory pointer to allocate.
mstore(0x40, add(str, 0x20))
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end of the memory to calculate the length later.
let end := str
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)`.
// Write the character to the pointer.
// The ASCII index of the '0' character is 48.
mstore8(str, add(48, mod(temp, 10)))
// Keep dividing `temp` until zero.
temp := div(temp, 10)
if iszero(temp) { break }
}
let length := sub(end, str)
// Move the pointer 32 bytes leftwards to make room for the length.
str := sub(str, 0x20)
// Store the length.
mstore(str, 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) // Write the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Write 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)))
// Allocate the memory.
mstore(0x40, add(str, 0x20))
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end to calculate the length later.
let end := str
// 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)
}
// Compute the string's length.
let strLength := sub(end, str)
// Move the pointer and write the length.
str := sub(str, 0x20)
mstore(str, strLength)
}
}
/// @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) // Write the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Write 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) // Write 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)) // Write 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)) // Write 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)
// Allocate the memory.
mstore(0x40, add(str, 0x20))
// Zeroize the slot after the string.
mstore(str, 0)
// Cache the end to calculate the length later.
let end := str
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
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 }
}
// Compute the string's length.
let strLength := sub(end, str)
// Move the pointer and write the length.
str := sub(str, 0x20)
mstore(str, strLength)
}
}
/// @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) // Write the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Write 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))
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
str := add(str, 2)
mstore(str, 40)
let o := add(str, 0x20)
mstore(add(o, 40), 0)
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) // Write the "0x" prefix.
str := sub(str, 2) // Move the pointer.
mstore(str, strLength) // Write 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.
// Store "0123456789abcdef" in scratch space.
mstore(0x0f, 0x30313233343536373839616263646566)
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)
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* 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.
// Allocate the memory.
mstore(0x40, add(result, add(resultLength, 0x20)))
}
}
}
/// @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)
// Allocate memory for the length and the bytes,
// rounded up to a multiple of 32.
mstore(0x40, add(result, and(add(resultLength, 0x3f), w)))
}
}
}
/// @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)
// Zeroize the slot after the string.
mstore(last, 0)
// Stores the length.
mstore(result, totalLength)
// Allocate memory for the length and the bytes,
// rounded up to a multiple of 32.
mstore(0x40, and(add(last, 0x1f), w))
}
}
/// @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)
let o := add(result, 0x20)
mstore(o, s)
mstore(add(o, n), 0)
mstore(0x40, add(result, 0x40))
}
}
/// @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 {
// Grab the free memory pointer.
result := mload(0x40)
// Allocate 2 words (1 for the length, 1 for the bytes).
mstore(0x40, add(result, 0x40))
// Zeroize the length slot.
mstore(result, 0)
// Store the length and bytes.
mstore(add(result, 0x1f), packed)
// Right pad with zeroes.
mstore(add(add(result, 0x20), mload(result)), 0)
}
}
/// @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(
// Load the length and the bytes of `a` and `b`.
or(
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 {
// Grab the free memory pointer.
resultA := mload(0x40)
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 retSize := 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, retSize), 0)
// Store the return offset.
mstore(retStart, 0x20)
// End the transaction, returning the string.
return(retStart, retSize)
}
}
}// 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
}
}
}pragma solidity ^0.8.20;
enum YieldMode {
AUTOMATIC,
VOID,
CLAIMABLE
}
enum GasMode {
VOID,
CLAIMABLE
}
interface IBlast {
// configure
function configureContract(
address contractAddress,
YieldMode _yield,
GasMode gasMode,
address governor
) external;
function configure(YieldMode _yield, GasMode gasMode, address governor) external;
function isAuthorized(address contractAddress) external returns (bool);
// base configuration options
function configureClaimableYield() external;
function configureClaimableYieldOnBehalf(address contractAddress) external;
function configureAutomaticYield() external;
function configureAutomaticYieldOnBehalf(address contractAddress) external;
function configureVoidYield() external;
function configureVoidYieldOnBehalf(address contractAddress) external;
function configureClaimableGas() external;
function configureClaimableGasOnBehalf(address contractAddress) external;
function configureVoidGas() external;
function configureVoidGasOnBehalf(address contractAddress) external;
function configureGovernor(address _governor) external;
function configureGovernorOnBehalf(address _newGovernor, address contractAddress)
external;
// claim yield
function claimYield(address contractAddress, address recipientOfYield, uint256 amount)
external
returns (uint256);
function claimAllYield(address contractAddress, address recipientOfYield)
external
returns (uint256);
// claim gas
function claimAllGas(address contractAddress, address recipientOfGas)
external
returns (uint256);
function claimGasAtMinClaimRate(
address contractAddress,
address recipientOfGas,
uint256 minClaimRateBips
) external returns (uint256);
function claimMaxGas(address contractAddress, address recipientOfGas)
external
returns (uint256);
function claimGas(
address contractAddress,
address recipientOfGas,
uint256 gasToClaim,
uint256 gasSecondsToConsume
) external returns (uint256);
// read functions
function readClaimableYield(address contractAddress) external view returns (uint256);
function readYieldConfiguration(address contractAddress) external view returns (uint8);
function readGasParams(address contractAddress)
external
view
returns (uint256 etherSeconds, uint256 etherBalance, uint256 lastUpdated, GasMode);
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.23; /// @notice Emitted when `baseURI` is empty or `protocolFee` is above `MAX_BPS`. error InvalidParams(); /// @notice Emitted when the phase end is before the start. error InvalidWhitelistPhases(); /// @notice Emitted when the `lockupPeriod` value is not valid. error InvalidLockupPeriod(); /// @notice Emitted if the `_tokenId` for `tokenURI()` does not exist. error TokenDoesNotExist(); /// @notice Emitted if the public mint hasn't started or has already ended. error MintNotActive(); /// @notice Emitted if the whitelist phase hasn't started or has already ended. error WhitelistPhaseNotActive(); /// @notice Emitted if the amount for `mint()` or `whitelistMint()` would exceed the /// `maxSupply`. error MaxSupplyReached(); /// @notice Emitted if `_amount` for `mint()` exceeds the maximum amount mintable per tx. error MaxMintsReached(); /// @notice Emitted if `_amount` for `whitelistMint()` exceeds the maximum amount mintable /// per tx. error MaxWhitelistMintsReached(); /// @notice Emitted if the caller to `mint()` would exceed the max mints per address. error MaxTotalMintsReached(); /// @notice Emitted if the caller to `whitelistMint()` would exceed the max mints per /// address. error MaxTotalWhitelistMintsReached(); /// @notice Emitted when calls to withdraw funds fail. error WithdrawalFailed(); /// @notice Emitted if `msg.value` is too low in `mint()` or `whitelistMint()` or if /// the amount to `claimYield()` is too high. error InsufficientFunds(); /// @notice Emitted if the `_merkleProof` provided to `whitelistMint()` is invalid. error InvalidMerkleProof(); /// @notice Emitted if a function is called for an invalid mint mode. error InvalidMode(); /// @notice Emittted if the `_tokenId` for `refund()` was minted to the owner. error NonRefundable(); /// @notice Emitted if the lockup period for the refund has not ended. error LockupIncomplete(); /// @notice Emitted if the `_tokenId` for `refund()` has already been refunded. error RefundRenounced(); /// @notice Emitted if the `_tokenId` for `renounceRefund()` was minted to the owner. error NonRenounceable(); /// @notice Emitted if the caller to `renounceRefund()` is not the token owner. error Unauthorized(); /// @notice Emitted if the `_tokenId` for `renounceRefund()` has already been refunded. error Refunded(); /// @notice Emitted if `baseURI` has been locked. error BaseURILocked(); /// @notice Emitted if `contractURI` has been locked. error ContractURILocked(); /// @notice Emitted if a new max supply would be lower than present total supply. error InvalidMaxSupply(); /// @notice Emitted if `royaltyLocked` is true. error RoyaltyParamsLocked(); /// @notice Emitted if the default royalty is too high in 'setRoyaltyParams()` error InvalidDefaultRoyalty(); /// @notice Emitted if setters affecting mints are called while minting is live. error MintingActive();
// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.3.0
// Creator: Chiru Labs
pragma solidity ^0.8.4;
/**
* @dev Interface of ERC721A.
*/
interface IERC721A {
/**
* The caller must own the token or be an approved operator.
*/
error ApprovalCallerNotOwnerNorApproved();
/**
* The token does not exist.
*/
error ApprovalQueryForNonexistentToken();
/**
* Cannot query the balance for the zero address.
*/
error BalanceQueryForZeroAddress();
/**
* Cannot mint to the zero address.
*/
error MintToZeroAddress();
/**
* The quantity of tokens minted must be more than zero.
*/
error MintZeroQuantity();
/**
* The token does not exist.
*/
error OwnerQueryForNonexistentToken();
/**
* The caller must own the token or be an approved operator.
*/
error TransferCallerNotOwnerNorApproved();
/**
* The token must be owned by `from`.
*/
error TransferFromIncorrectOwner();
/**
* Cannot safely transfer to a contract that does not implement the
* ERC721Receiver interface.
*/
error TransferToNonERC721ReceiverImplementer();
/**
* Cannot transfer to the zero address.
*/
error TransferToZeroAddress();
/**
* The token does not exist.
*/
error URIQueryForNonexistentToken();
/**
* The `quantity` minted with ERC2309 exceeds the safety limit.
*/
error MintERC2309QuantityExceedsLimit();
/**
* The `extraData` cannot be set on an unintialized ownership slot.
*/
error OwnershipNotInitializedForExtraData();
/**
* `_sequentialUpTo()` must be greater than `_startTokenId()`.
*/
error SequentialUpToTooSmall();
/**
* The `tokenId` of a sequential mint exceeds `_sequentialUpTo()`.
*/
error SequentialMintExceedsLimit();
/**
* Spot minting requires a `tokenId` greater than `_sequentialUpTo()`.
*/
error SpotMintTokenIdTooSmall();
/**
* Cannot mint over a token that already exists.
*/
error TokenAlreadyExists();
/**
* The feature is not compatible with spot mints.
*/
error NotCompatibleWithSpotMints();
// =============================================================
// STRUCTS
// =============================================================
struct TokenOwnership {
// The address of the owner.
address addr;
// Stores the start time of ownership with minimal overhead for tokenomics.
uint64 startTimestamp;
// Whether the token has been burned.
bool burned;
// Arbitrary data similar to `startTimestamp` that can be set via {_extraData}.
uint24 extraData;
}
// =============================================================
// TOKEN COUNTERS
// =============================================================
/**
* @dev Returns the total number of tokens in existence.
* Burned tokens will reduce the count.
* To get the total number of tokens minted, please see {_totalMinted}.
*/
function totalSupply() external view returns (uint256);
// =============================================================
// IERC165
// =============================================================
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
* to learn more about how these ids are created.
*
* This function call must use less than 30000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
// =============================================================
// IERC721
// =============================================================
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables
* (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in `owner`'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`,
* checking first that contract recipients are aware of the ERC721 protocol
* to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be have been allowed to move
* this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement
* {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external payable;
/**
* @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external payable;
/**
* @dev Transfers `tokenId` from `from` to `to`.
*
* WARNING: Usage of this method is discouraged, use {safeTransferFrom}
* whenever possible.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token
* by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 tokenId
) external payable;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the
* zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external payable;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom}
* for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool _approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}.
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
// =============================================================
// IERC721Metadata
// =============================================================
/**
* @dev Returns the token collection name.
*/
function name() external view returns (string memory);
/**
* @dev Returns the token collection symbol.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
*/
function tokenURI(uint256 tokenId) external view returns (string memory);
// =============================================================
// IERC2309
// =============================================================
/**
* @dev Emitted when tokens in `fromTokenId` to `toTokenId`
* (inclusive) is transferred from `from` to `to`, as defined in the
* [ERC2309](https://eips.ethereum.org/EIPS/eip-2309) standard.
*
* See {_mintERC2309} for more details.
*/
event ConsecutiveTransfer(uint256 indexed fromTokenId, uint256 toTokenId, address indexed from, address indexed to);
}{
"remappings": [
"ERC721A/=lib/ERC721A/contracts/",
"gaslite-core/=lib/gaslite-core/src/",
"multicall/=lib/multicall/src/",
"@murky/=lib/murky/src/",
"@openzeppelin/=lib/openzeppelin-contracts/contracts/",
"@pythnetwork/pyth-sdk-solidity/=node_modules/@pythnetwork/pyth-sdk-solidity/",
"@solady/=lib/solady/src/",
"@ERC721A/=lib/gaslite-core/lib/ERC721A/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"ds-test/=lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"murky/=lib/murky/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"solady/=lib/solady/src/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "shanghai",
"viaIR": true,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"blazeType","type":"address"}],"name":"Deployed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"components":[{"internalType":"string","name":"baseURI","type":"string"},{"internalType":"string","name":"contractURI","type":"string"},{"internalType":"uint256","name":"mintsToOwner","type":"uint256"},{"internalType":"uint256","name":"maxSupply","type":"uint256"},{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"end","type":"uint256"},{"internalType":"uint120","name":"mintPrice","type":"uint120"},{"internalType":"uint64","name":"maxMintsPerCall","type":"uint64"},{"internalType":"uint64","name":"maxTotalMints","type":"uint64"},{"internalType":"uint16","name":"defaultRoyalty","type":"uint16"},{"internalType":"address","name":"royaltyReceiver","type":"address"},{"components":[{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"end","type":"uint256"},{"internalType":"uint120","name":"whitelistMintPrice","type":"uint120"},{"internalType":"uint64","name":"maxWhitelistMintsPerCall","type":"uint64"},{"internalType":"uint64","name":"maxTotalWhitelistMints","type":"uint64"},{"internalType":"bytes32","name":"merkleRoot","type":"bytes32"}],"internalType":"struct WhitelistPhase[]","name":"whitelistPhases","type":"tuple[]"}],"internalType":"struct Params","name":"_params","type":"tuple"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"address","name":"_owner","type":"address"}],"name":"deployBlazeType","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract BlazeType","name":"_blazeType","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"mintBlazeType","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"mintFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_mintFee","type":"uint256"}],"name":"setMintBasis","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract BlazeType","name":"_blazeType","type":"address"},{"internalType":"uint8","name":"_phase","type":"uint8"},{"internalType":"bytes32[]","name":"_merkleProof","type":"bytes32[]"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"whitelistMintBlazeType","outputs":[],"stateMutability":"payable","type":"function"}]Contract Creation Code
608080604052346100fb5733156100ff575f8054336001600160a01b03198216811783556040519290916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09080a3732536fe9ab3f511540f2f9e2ec2a805005c3dd800803b156100fb57816024815f80946336b91f2b60e01b835273f8a82748e7df10d0684b758d02cf6c43ad83ad2560048401525af180156100f0576100c5575b6602615c87ffe000600155604051613e9090816101158239f35b6001600160401b0381116100dc57604052806100ab565b634e487b7160e01b5f52604160045260245ffd5b6040513d5f823e3d90fd5b5f80fd5b631e4fbdf760e01b81525f6004820152602490fdfe608080604052600436101562000013575f80fd5b5f3560e01c90816313966db51462000731575080636497ed2d146200060b57806371114bf114620005e6578063715018a6146200058b5780638da5cb5b1462000562578063921b998214620004a1578063abdd875914620001125763f2fde38b146200007d575f80fd5b346200010e5760203660031901126200010e576200009a6200074d565b620000a46200093c565b6001600160a01b03908116908115620000f6575f54826bffffffffffffffffffffffff60a01b8216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b604051631e4fbdf760e01b81525f6004820152602490fd5b5f80fd5b346200010e576003196080368201126200010e576001600160401b03600435116200010e576004356004016101808092600435360301126200010e576024356001600160401b0381116200010e576200017090369060040162000764565b916044356001600160401b0381116200010e576200019390369060040162000764565b6001600160a01b03946064359392919086851685036200010e57604051956134f29487868101106001600160401b03878a0111176200048d57620002309086620009698a396080878a0152620002196200020b8c8b6102008b620001f88780620008be565b93909460808383010152010191620008f2565b9160246004350190620008be565b8a8901808403607f190160a09091015290620008f2565b600435604481013589880160c0810191909152606482013560e0820152608482013561010082015260a4820135610120909101529099906001600160781b03906200027e9060c40162000912565b16610140878a0101526001600160401b036200029f60e46004350162000927565b16610160878a0101526001600160401b03620002c16101046004350162000927565b16908689010152610124600435013561ffff81168091036200010e576101a08689010152610144600435013588811681036200010e5788168786016101c00152600435610164810135903603602219018112156200010e5760043501986001600160401b0360048b0135116200010e5760c060048b013502360360248b01136200010e57602090607f9a949a19878a018203016101e0888b01015260048501358152019260248101905f5b600482013581106200040357505050928798620003a0889994620003b3946060978b60208b808301860392010152620008f2565b9186890183036040888b010152620008f2565b9316918401015203905ff0908115620003f857602091166040519080337f09e48df7857bd0c1e0d31bb8a85d42cf1874817895f171c917f6ee2cea73ec205f80a38152f35b6040513d5f823e3d90fd5b90919a94600160c0806004938f8060a091358352602081013560208401526001600160781b03620004376040830162000912565b16604084015260606001600160401b036200045482840162000927565b16908401526001600160401b036200046f6080830162000927565b166080840152013560a0820152989e9801970193920190506200036c565b634e487b7160e01b5f52604160045260245ffd5b60403660031901126200010e57620004b86200074d565b620004eb5f80808060015473a804b7d57a4a872dbfbe2987347403039ade20dc5af1620004e4620007b6565b50620007f8565b620004f9600154346200084f565b906040519060208201906340c10f1960e01b825233602484015260243560448401526044835260808301918383106001600160401b038411176200048d5762000560945f948594604052519260018060a01b03165af162000559620007b6565b5062000871565b005b346200010e575f3660031901126200010e575f546040516001600160a01b039091168152602090f35b346200010e575f3660031901126200010e57620005a76200093c565b5f80546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346200010e5760203660031901126200010e57620006036200093c565b600435600155005b60803660031901126200010e57620006226200074d565b60243560ff81168091036200010e57604435906001600160401b03928383116200010e57366023840112156200010e5782600401359384116200010e578360051b9136602484860101116200010e576200069d5f80808060015473a804b7d57a4a872dbfbe2987347403039ade20dc5af1620004e4620007b6565b620006ab600154346200084f565b60405163725f060360e11b6020820190815260248201939093526080604482015260a48101879052956001600160fb1b03106200010e57856200071a60c46200056098875f999860248b9a0184840137336064830152606435608483015281010360a481018452018262000794565b51926001600160a01b03165af162000559620007b6565b346200010e575f3660031901126200010e576020906001548152f35b600435906001600160a01b03821682036200010e57565b9181601f840112156200010e578235916001600160401b0383116200010e57602083818601950101116200010e57565b90601f801991011681019081106001600160401b038211176200048d57604052565b3d15620007f3573d906001600160401b0382116200048d5760405191620007e8601f8201601f19166020018462000794565b82523d5f602084013e565b606090565b156200080057565b60405162461bcd60e51b815260206004820152602160248201527f426c617a654d616e616765723a20666565207472616e73666572206661696c656044820152601960fa1b6064820152608490fd5b919082039182116200085d57565b634e487b7160e01b5f52601160045260245ffd5b156200087957565b60405162461bcd60e51b815260206004820152601960248201527f426c617a654d616e616765723a206d696e74206661696c6564000000000000006044820152606490fd5b9035601e19823603018112156200010e5701602081359101916001600160401b0382116200010e5781360383136200010e57565b908060209392818452848401375f828201840152601f01601f1916010190565b35906001600160781b03821682036200010e57565b35906001600160401b03821682036200010e57565b5f546001600160a01b031633036200095057565b60405163118cdaa760e01b8152336004820152602490fdfe60808060405234620001a457620034f29081380380926200002082620001bc565b8239818101818312620001a45781516001600160401b0393909290848411620001a45783906101809182910312620001a4576200005c6200023d565b9380830151868111620001a457848462000079928401016200025e565b855260a0810151868111620001a457848462000098928401016200025e565b602086015260c0810151604086015260e0810151606086015261010080820151848701526101208083015160a088015261014091620000d9838501620002d2565b60c08901526200010061016095620000f3878701620002e7565b60e08b01528501620002e7565b90880152620001136101a08401620002fc565b90870152620001266101c0830162000323565b908601526101e0810151868111620001a4578385916200014893010162000338565b9084015260a051848111620001a45782620001659183016200025e565b9160c051948511620001a45762000194946200018292016200025e565b906200018d6200030c565b9262000b20565b60405161242c9081620010a68239f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b6080601f91909101601f19168101906001600160401b03821190821017620001e357604052565b620001a8565b60c081019081106001600160401b03821117620001e357604052565b6001600160401b038111620001e357604052565b601f909101601f19168101906001600160401b03821190821017620001e357604052565b6040519061018082016001600160401b03811183821017620001e357604052565b919080601f84011215620001a45782516001600160401b038111620001e357602090604051926200029983601f19601f850116018562000219565b818452828287010111620001a4575f5b818110620002be5750825f9394955001015290565b8581018301518482018401528201620002a9565b51906001600160781b0382168203620001a457565b51906001600160401b0382168203620001a457565b519061ffff82168203620001a457565b60e051906001600160a01b0382168203620001a457565b51906001600160a01b0382168203620001a457565b81601f82011215620001a45780519060206001600160401b038311620001e3576040936040519462000370838660051b018762000219565b848652828601918360c080970286010194818611620001a4578401925b8584106200039f575050505050505090565b8684830312620001a4578487918451620003b981620001e9565b865181528287015183820152620003d2868801620002d2565b868201526060620003e5818901620002e7565b908201526080620003f8818901620002e7565b9082015260a080880151908201528152019301926200038d565b5f910312620001a457565b6040513d5f823e3d90fd5b60405190602082016001600160401b03811183821017620001e3576040525f8252565b90600182811c921680156200047b575b60208310146200046757565b634e487b7160e01b5f52602260045260245ffd5b91607f16916200045b565b601f811162000493575050565b60025f5260205f20906020601f840160051c83019310620004d0575b601f0160051c01905b818110620004c4575050565b5f8155600101620004b8565b9091508190620004af565b601f8111620004e8575050565b60035f5260205f20906020601f840160051c8301931062000525575b601f0160051c01905b81811062000519575050565b5f81556001016200050d565b909150819062000504565b601f81116200053d575050565b600b5f5260205f20906020601f840160051c830193106200057a575b601f0160051c01905b8181106200056e575050565b5f815560010162000562565b909150819062000559565b601f811162000592575050565b600c5f5260205f20906020601f840160051c83019310620005cf575b601f0160051c01905b818110620005c3575050565b5f8155600101620005b7565b9091508190620005ae565b80519091906001600160401b038111620001e3576200060681620006006003546200044b565b620004db565b602080601f83116001146200064b575081906200063a93945f926200063f575b50508160011b915f199060031b1c19161790565b600355565b015190505f8062000626565b60035f52601f198316949091907fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b925f905b878210620006b75750508360019596106200069e575b505050811b01600355565b01515f1960f88460031b161c191690555f808062000693565b806001859682949686015181550195019301906200067d565b80519091906001600160401b038111620001e357620006fc81620006f6600b546200044b565b62000530565b602080601f831160011462000734575081906200072f93945f926200063f5750508160011b915f199060031b1c19161790565b600b55565b600b5f52601f198316949091907f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9925f905b878210620007a057505083600195961062000787575b505050811b01600b55565b01515f1960f88460031b161c191690555f80806200077c565b8060018596829496860151815501950193019062000766565b80519091906001600160401b038111620001e357620007e581620007df600c546200044b565b62000585565b602080601f83116001146200081d575081906200081893945f926200063f5750508160011b915f199060031b1c19161790565b600c55565b600c5f52601f198316949091907fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7925f905b8782106200088957505083600195961062000870575b505050811b01600c55565b01515f1960f88460031b161c191690555f808062000865565b806001859682949686015181550195019301906200084f565b600281901b91906001600160fe1b03811603620008bb57565b634e487b7160e01b5f52601160045260245ffd5b805190680100000000000000008211620001e357601354826013558083106200099d575b5060135f526020908101905f80516020620034d28339815191525f925b8484106200091f575050505050565b600483826200099060019451869060a060039180518455602081015160018501556002840160018060781b03604083015116815490600160781b600160b81b03606085015160781b1690600160b81b600160f81b03608086015160b81b169260ff60f81b1617171790550151910155565b0192019301929062000910565b620009a890620008a2565b620009b383620008a2565b60135f525f80516020620034d283398151915291820191015b818110620009db5750620008f3565b805f600492555f60018201555f60028201555f600382015501620009cc565b61016062000b1e9162000a0e8151620006d0565b62000a1d6020820151620007b9565b6040810151600d556060810151600e556080810151600f5560a081015160105560c08101516011805460e084015161010085015160789190911b600160781b600160b81b03166001600160781b039094167fff00000000000000000000000000000000000000000000000000000000000000909216919091179290921760b89290921b600160b81b600160f81b031691909117905562000ad962000ac761012083015161ffff1690565b61ffff1661ffff196012541617601255565b61014081015162000b16906001600160a01b03166012805462010000600160b01b03191660109290921b62010000600160b01b0316919091179055565b0151620008cf565b565b929162000b2e918362000cdc565b734300000000000000000000000000000000000002803b15620001a4575f809160046040518094819363388a0bbd60e11b83525af1801562000cbf5762000cc5575b50732536fe9ab3f511540f2f9e2ec2a805005c3dd800803b15620001a4576040516336b91f2b60e01b815273f8a82748e7df10d0684b758d02cf6c43ad83ad256004820152905f908290602490829084905af1801562000cbf5762000ca1575b5081516020815191012062000be462000428565b6020815191012014801562000c81575b62000c6f5761014082015162000b1e9261016092909162000c3b906001600160a01b031662000c3462000c2d61012086015161ffff1690565b61ffff1690565b9062000e14565b60408201518062000c5c575b505062000c5481620009fa565b015162000f68565b62000c679162000e5c565b5f8062000c47565b604051635435b28960e11b8152600490fd5b506107d061ffff62000c9961012085015161ffff1690565b161162000bf4565b8062000cb162000cb89262000205565b8062000412565b5f62000bd0565b6200041d565b8062000cb162000cd59262000205565b5f62000b70565b815191939290916001600160401b038111620001e35762000d0a8162000d046002546200044b565b62000486565b602080601f831160011462000d825750908062000d439262000d4c9596975f926200063f5750508160011b915f199060031b1c19161790565b600255620005da565b5f80556001600160a01b0381161562000d6a5762000b1e9062001044565b604051631e4fbdf760e01b81525f6004820152602490fd5b60025f52601f198316969091907f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace925f905b89821062000dfb5750509083929160019462000d4c9798991062000de2575b505050811b01600255620005da565b01515f1960f88460031b161c191690555f808062000dd3565b8060018596829496860151815501950193019062000db4565b6001600160601b0390911690612710821162000e4f5760601b801562000e42571768aa4ec00224afccfdb755565b63b4457eaa5f526004601cfd5b63350a88b35f526004601cfd5b905f5491811562000f305760019162000eb360018060a01b038316926001831460e11b4260a01b17841762000e99875f52600460205260405f2090565b556001600160a01b03165f90815260056020526040902090565b6801000000000000000182028154019055811562000f2a57830192916001815b62000ee1575b505050505f55565b1562000f17575b5f8184845f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8180a462000ed3565b8092019183830362000ee8578062000ed9565b62001097565b63b562e8dd60e01b5f5260045ffd5b805182101562000f545760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b90600382511162000fd5575f805b83518210156200103e5762000f8c828562000f3f565b5151906020918262000f9f858862000f3f565b5101511090811562001021575b811562000fe7575b5062000fd55760019062000fc9838662000f3f565b51015191019062000f76565b60405163097191df60e41b8152600490fd5b905062000ff5838662000f3f565b51511515908162001009575b505f62000fb4565b905062001017838662000f3f565b5151105f62001001565b90508162001030848762000f3f565b510151600f54109062000fac565b50509050565b600a80546001600160a01b0319908116909155600980549182166001600160a01b0393841690811790915591167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3565b622e076360e81b5f5260045ffdfe60806040526004361015610011575f80fd5b5f3560e01c806301ffc9a7146102a457806306fdde031461029f578063081812fc1461029a57806309430a7e14610295578063095ea7b31461029057806318160ddd1461028b57806323b872dd146102865780632a55205a146102815780632e1a7d4d1461027c5780633ccfd60b14610277578063406cf2291461027257806340bd2e231461026d57806340c10f191461026857806342842e0e1461026357806353df5c7c1461025e57806355f804b3146102595780635e0cee0f146102545780636352211e1461024f5780636f8b44b01461024a57806370a0823114610245578063715018a61461024057806375eedb451461023b57806379ba5097146102365780638503e714146102315780638da5cb5b1461022c578063938e3d7b1461022757806395d89b41146102225780639d0172f31461021d578063a22cb46514610218578063b88d4fde14610213578063c87b56dd1461020e578063cff0ab9614610209578063d558296514610204578063e1c2ffad146101ff578063e30c3978146101fa578063e4be0c06146101f5578063e8a3d485146101f0578063e985e9c5146101eb578063f2fde38b146101e6578063f557ab03146101e15763f8bd83e1146101dc575f80fd5b611a9b565b611a5f565b6119f3565b611997565b611968565b611832565b6117fa565b6117d1565b611756565b611638565b611419565b611397565b6112ba565b611270565b6111cb565b61109f565b611077565b61104c565b610fca565b610e9b565b610da3565b610d4d565b610cde565b610caf565b610bd0565b610aae565b610a3e565b610a06565b61093d565b6108ec565b6108b7565b610883565b61080f565b610787565b610773565b61071d565b610673565b610521565b6104d2565b6103f2565b6102bf565b6001600160e01b03198116036102bb57565b5f80fd5b346102bb5760203660031901126102bb5760206004356102de816102a9565b6001600160e01b031981166301ffc9a760e01b811491908215610341575b8215610330575b508115610316575b506040519015158152f35b905060e01c6301ffc9a7632a55205a82149114175f61030b565b635b5e139f60e01b1491505f610303565b6380ac58cd60e01b811492506102fc565b5f5b8381106103635750505f910152565b8181015183820152602001610354565b9060209161038c81518092818552858086019101610352565b601f01601f1916010190565b90602091602081526064518060208301525f5b8181106103cb575060409293505f838284010152601f8019910116010190565b60848101518382016040015284016103ab565b9060206103ef928181520190610373565b90565b346102bb575f3660031901126102bb576040515f600254610412816114b5565b808452906020906001908181169081156104a8575060011461044f575b61044b8561043f8187038261135b565b604051918291826103de565b0390f35b60025f90815293507f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace5b8385106104955750505050810160200161043f8261044b61042f565b8054868601840152938201938101610479565b86955061044b9693506020925061043f94915060ff191682840152151560051b820101929361042f565b346102bb5760203660031901126102bb576004356104ef81612034565b15610512575f526006602052602060018060a01b0360405f205416604051908152f35b6333d1c03960e21b5f5260045ffd5b346102bb575f3660031901126102bb5760135461053d81611b01565b60409161054d604051928361135b565b8082526020808301918260135f527f66de8ffda797e3de9c05e8fc57b3bf0ec28a930d40b0d285d93c06501cf6a0905f915b83831061060e57505050506040519281840190828552518091526040840192915f5b8281106105ae5785850386f35b8351805186528083015186840152878101516001600160781b0316888701526060808201516001600160401b03908116918801919091526080808301519091169087015260a0908101519086015260c090940193928101926001016105a1565b600485600192610620859a989a611b18565b81520192019201919095939561057f565b600435906001600160a01b03821682036102bb57565b602435906001600160a01b03821682036102bb57565b604435906001600160a01b03821682036102bb57565b60403660031901126102bb57610687610631565b602435906001600160a01b038061069d846120ae565b16908133036106ee575b835f52600660205260405f20921691826001600160601b0360a01b8254161790557f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f80a4005b5f82815260076020908152604080832033845290915290205460ff166106a7576367d9dca160e11b5f5260045ffd5b346102bb575f3660031901126102bb5760205f546001549003604051908152f35b60609060031901126102bb576001600160a01b039060043582811681036102bb579160243590811681036102bb579060443590565b61078561077f3661073e565b91611b7c565b005b346102bb5760403660031901126102bb576024356004355f5268aa4ec00224afccfdb7908160205260405f20548060601c9283156107fd575b5061044b908360601b1892835f1904831184023d3d3e6127106040519485940204908360209093929193604081019460018060a01b031681520152565b54606081901c9350905061044b6107c0565b346102bb5760203660031901126102bb5760043561082b61211e565b6015548082111561086657505f80808093816015555b335af161084c611d28565b501561085457005b6040516327fcd9d160e01b8152600490fd5b81810390811161087e575f8080938193601555610841565b611d14565b346102bb575f3660031901126102bb5761089b61211e565b5f80808047335af16108ab611d28565b5015610854575f601555005b346102bb575f3660031901126102bb576108cf61211e565b47601554810390811161087e575f80808093335af161084c611d28565b346102bb5760203660031901126102bb5760043561090861211e565b47601554810390811161087e57811161092b575f80808093335af161084c611d28565b60405163356680b760e01b8152600490fd5b60403660031901126102bb57610951610631565b602435600f54421080156109fb575b80156109ef575b6109dd57610976815f54611d57565b600e54106109cb576109a8816109a36109976011546001600160781b031690565b6001600160781b031690565b611d64565b341061092b57610785916109c66109c134601554611d57565b601555565b612132565b60405163d05cb60960e01b8152600490fd5b60405163914edb0f60e01b8152600490fd5b5060175460ff16610967565b504260105410610960565b610a0f3661073e565b6040519160208301938385106001600160401b03861117610a3957610785946040525f8452611f02565b611347565b346102bb575f3660031901126102bb57610a5661211e565b6014805460ff19166001179055005b9060206003198301126102bb576004356001600160401b03928382116102bb57806023830112156102bb5781600401359384116102bb57602484830101116102bb576024019190565b346102bb57610abc36610a65565b610ac461211e565b60ff60145416610bbe576001600160401b038111610a3957610af081610aeb600b546114b5565b611d77565b5f601f8211600114610b52578190610b1d935f92610b47575b50508160011b915f199060031b1c19161790565b600b555b7fa1731ca444c73d019f0dbb4ee5546c98730f4ffcdaa1c29776ab542aa64d5e1b5f80a1005b013590505f80610b09565b600b5f52601f198216925f805160206123d7833981519152915f5b858110610ba657508360019510610b8d575b505050811b01600b55610b21565b01355f19600384901b60f8161c191690555f8080610b7f565b90926020600181928686013581550194019101610b6d565b60405163696c636960e01b8152600490fd5b346102bb5760403660031901126102bb57610be9610631565b60243561ffff8116918282036102bb57610c0161211e565b60ff60145460101c16610c9d576107d08311610c8b57601280546001600160b01b031916601083901b62010000600160b01b03161761ffff84161790557f4db95622f7059a0983b8b21ce94db601f1f2e63da11a652d59d8d7f77c4ff1a192610c6a908261220c565b604080516001600160a01b0392909216825261ffff929092166020820152a1005b60405163a2a65b5360e01b8152600490fd5b604051635a214b2560e11b8152600490fd5b346102bb5760203660031901126102bb5760206001600160a01b03610cd56004356120ae565b16604051908152f35b346102bb5760203660031901126102bb57600435610cfa61211e565b610d02612251565b5f548110610d3b576020817f7810bd47de260c3e9ee10061cf438099dd12256c79485f12f94dbccc981e806c92600e55604051908152a1005b60405163066f305360e21b8152600490fd5b346102bb5760203660031901126102bb576001600160a01b03610d6e610631565b168015610d94575f52600560205260206001600160401b0360405f205416604051908152f35b6323d3ad8160e21b5f5260045ffd5b346102bb575f3660031901126102bb57610dbb61211e565b600a80546001600160a01b03199081169091556009805491821690555f906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b9181601f840112156102bb578235916001600160401b0383116102bb576020808501948460051b0101116102bb57565b6020808201906020835283518092526040830192602060408460051b8301019501935f915b848310610e6d5750505050505090565b9091929394958480610e8b600193603f198682030187528a51610373565b9801930193019194939290610e5d565b346102bb5760203660031901126102bb57600480356001600160401b0381116102bb57610ecc903690600401610e08565b91610ed561211e565b610ede83611e3e565b925f5b818110610ef6576040518061044b8782610e38565b5f80610f03838588611e9b565b60409391610f15855180938193611ee1565b0390305af490610f23611d28565b9115610f4a575090600191610f388288611eee565b52610f438187611eee565b5001610ee1565b846044835110610f7157905162461bcd60e51b8152908190610f6d908201610398565b0390fd5b610f6d915191829162461bcd60e51b8352820160809060208152602560208201527f5472616e73616374696f6e20726576657274656420776974686f75742061207260408201526432b0b9b7b760d91b60608201520190565b346102bb575f3660031901126102bb57600a546001600160a01b033381831603611034576001600160601b0360a01b809216600a556009549133908316176009553391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b60405163118cdaa760e01b8152336004820152602490fd5b346102bb575f3660031901126102bb5761106461211e565b6014805462ff0000191662010000179055005b346102bb575f3660031901126102bb576009546040516001600160a01b039091168152602090f35b346102bb576110ad36610a65565b6110b561211e565b60ff60145460081c166111b9576001600160401b038111610a39576110e4816110df600c546114b5565b611dd1565b5f601f821160011461113a578190611110935f92610b475750508160011b915f199060031b1c19161790565b600c555b7fa5d4097edda6d87cb9329af83fb3712ef77eeb13738ffe43cc35a4ce305ad9625f80a1005b600c5f52601f198216927fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7915f5b8581106111a157508360019510611188575b505050811b01600c55611114565b01355f19600384901b60f8161c191690555f808061117a565b90926020600181928686013581550194019101611168565b604051631fd0326960e01b8152600490fd5b346102bb575f3660031901126102bb576040515f6003546111eb816114b5565b808452906020906001908181169081156104a857506001146112175761044b8561043f8187038261135b565b60035f90815293507fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b5b83851061125d5750505050810160200161043f8261044b61042f565b8054868601840152938201938101611241565b346102bb5760203660031901126102bb576004356001600160781b0381168091036102bb5761129d61211e565b6112a5612251565b6001600160781b031960115416176011555f80f35b346102bb5760403660031901126102bb576112d3610631565b602435908115158092036102bb57335f9081526007602090815260408083206001600160a01b0385168452909152902060ff1981541660ff841617905560405191825260018060a01b0316907f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160203392a3005b634e487b7160e01b5f52604160045260245ffd5b90601f801991011681019081106001600160401b03821117610a3957604052565b6001600160401b038111610a3957601f01601f191660200190565b60803660031901126102bb576113ab610631565b6113b3610647565b606435916001600160401b0383116102bb57366023840112156102bb578260040135916113df8361137c565b926113ed604051948561135b565b80845236602482870101116102bb576020815f9260246107859801838801378501015260443591611f02565b346102bb5760203660031901126102bb5760043561143681612034565b156114a35760405190608082019060a083016040525f8252905b5f190190600a9060308282060183530490816114505761149761043f61044b92856080601f1992838101920301815260405193849161149160208401611f43565b90611fbc565b0390810183528261135b565b60405163677510db60e11b8152600490fd5b90600182811c921680156114e3575b60208310146114cf57565b634e487b7160e01b5f52602260045260245ffd5b91607f16916114c4565b604051905f82600c5491611500836114b5565b8083529260209060019081811690811561158c575060011461152d575b505061152b9250038361135b565b565b915092600c5f527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7935f925b828410611574575061152b9450505081016020015f8061151d565b85548885018301529485019487945092810192611559565b9150506020925061152b94915060ff191682840152151560051b8201015f8061151d565b9995909461014099946115ef8c6001600160781b03969f9e9b98956115e161ffff9c97610160808552840190610373565b916020818403910152610373565b60408d019e909e5260608c015260808b015260a08a01521660c08801526001600160401b0391821660e088015216610100860152166101208401526001600160a01b0316910152565b346102bb575f3660031901126102bb57604051600b54815f611659836114b5565b8083529260209060019081811690811561173257506001146116e6575b50506116849250038261135b565b61168c6114ed565b9061044b600d5491600e5493600f549160105490601154906001600160401b039260125495604051998a9961ffff60018060a01b038a60101c169916976001600160781b03888860b81c16988860781c169716958c6115b0565b915092600b5f525f805160206123d7833981519152935f925b82841061171a57506116849450505081016020015f80611676565b855487850183015294850194869450928101926116ff565b9150506020925061168494915060ff191682840152151560051b8201015f80611676565b346102bb575f3660031901126102bb5761176e61211e565b600160ff1960175416176017557f7810bd47de260c3e9ee10061cf438099dd12256c79485f12f94dbccc981e806c60205f5480600e55604051907fcbbaae1b89885aa88b0db407075a1f3e6df45931447d19c7da5f5b7471a729e55f80a18152a1005b346102bb575f3660031901126102bb576117e961211e565b6014805461ff001916610100179055005b346102bb575f3660031901126102bb57600a546040516001600160a01b039091168152602090f35b6004359060ff821682036102bb57565b60803660031901126102bb57611846611822565b6024356001600160401b0381116102bb57611865903690600401610e08565b919061186f61065d565b9261188561187f60643594611fd3565b50611b18565b80514210801561195b575b6119495761189f845f54611d57565b600e54106109cb576118c2846109a361099760408501516001600160781b031690565b341061092b57604080516001600160a01b038716602082019081528183018790529181526119209461191c949092909160a09161190c9161190460608261135b565b51902061200b565b602081519101209301519161238c565b1590565b61193757610785916109c66109c134601554611d57565b60405163582f497d60e11b8152600490fd5b60405163cbe8d62360e01b8152600490fd5b5042602082015110611890565b346102bb575f3660031901126102bb5761044b6119836114ed565b604051918291602083526020830190610373565b346102bb5760403660031901126102bb57602060ff6119e76119b7610631565b6119bf610647565b6001600160a01b039182165f9081526007865260408082209290931681526020919091522090565b54166040519015158152f35b346102bb5760203660031901126102bb57611a0c610631565b611a1461211e565b600a80546001600160a01b0319166001600160a01b039283169081179091556009549091167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e227005f80a3005b346102bb575f3660031901126102bb57606060145460ff6040519181811615158352818160081c161515602084015260101c1615156040820152f35b346102bb5760403660031901126102bb57611ab4611822565b602435906001600160781b0382168092036102bb57611ae4600291611ad761211e565b611adf612251565b611fd3565b500180546effffffffffffffffffffffffffffff19169091179055005b6001600160401b038111610a395760051b60200190565b6040516001600160401b03929160c0820184811183821017610a395760a0916003916040528395815485526001820154602086015260028201546001600160781b0381166040870152818160781c16606087015260b81c1660808501520154910152565b919091611b88826120ae565b6001600160a01b039182169390828116859003611d0f575f8481526006602052604090208054611bc76001600160a01b03881633908114908314171590565b611cc5575b611cbc575b506001600160a01b0385165f90815260056020526040902080545f190190556001600160a01b0382165f908152600560205260409020805460010190556001600160a01b0382164260a01b17600160e11b17611c35855f52600460205260405f2090565b55600160e11b811615611c77575b501680927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a415611c7257565b612090565b60018401611c8d815f52600460205260405f2090565b5415611c9a575b50611c43565b5f548114611c9457611cb4905f52600460205260405f2090565b555f80611c94565b5f90555f611bd1565b611d0561191c611cfe33611ce98b60018060a01b03165f52600760205260405f2090565b9060018060a01b03165f5260205260405f2090565b5460ff1690565b15611bcc57612081565b612073565b634e487b7160e01b5f52601160045260245ffd5b3d15611d52573d90611d398261137c565b91611d47604051938461135b565b82523d5f602084013e565b606090565b9190820180921161087e57565b8181029291811591840414171561087e57565b601f8111611d83575050565b600b5f525f805160206123d7833981519152906020601f840160051c83019310611dc7575b601f0160051c01905b818110611dbc575050565b5f8155600101611db1565b9091508190611da8565b601f8111611ddd575050565b600c5f527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7906020601f840160051c83019310611e34575b601f0160051c01905b818110611e29575050565b5f8155600101611e1e565b9091508190611e15565b90611e4882611b01565b611e55604051918261135b565b8281528092611e66601f1991611b01565b01905f5b828110611e7657505050565b806060602080938501015201611e6a565b634e487b7160e01b5f52603260045260245ffd5b9190811015611edc5760051b81013590601e19813603018212156102bb5701908135916001600160401b0383116102bb5760200182360381136102bb579190565b611e87565b908092918237015f815290565b8051821015611edc5760209160051b010190565b929190611f10828286611b7c565b803b611f1d575b50505050565b611f26936122d7565b15611f34575f808080611f17565b6368d2bf6b60e11b5f5260045ffd5b600b545f9291611f52826114b5565b91600190818116908115611fa95750600114611f6d57505050565b9091929350600b5f525f805160206123d7833981519152905f915b848310611f96575050500190565b8181602092548587015201920191611f88565b60ff191683525050811515909102019150565b90611fcf60209282815194859201610352565b0190565b601354811015611edc5760135f5260021b7f66de8ffda797e3de9c05e8fc57b3bf0ec28a930d40b0d285d93c06501cf6a09001905f90565b9060405191602083015260208252604082018281106001600160401b03821117610a3957604052565b905f915f5481106120425750565b9091505b805f52600460205260405f2054806120675750801561087e575f1901612046565b600160e01b1615919050565b62a1148160e81b5f5260045ffd5b632ce44b5f60e11b5f5260045ffd5b633a954ecd60e21b5f5260045ffd5b636f96cda160e11b5f5260045ffd5b6120c0815f52600460205260405f2090565b549081156120d75750600160e01b811661209f5790565b90505f5481101561209f575b5f19015f818152600460205260409020549081156121175750600160e01b8116156103ef57636f96cda160e11b5f5260045ffd5b90506120e3565b6009546001600160a01b0316330361103457565b5f549180156121fd576001916001600160a01b0381164260a01b83851460e11b1717612166855f52600460205260405f2090565b556001600160a01b03165f81815260056020526040902080546801000000000000000184020190559081156121ef57830192916001815b6121aa575b505050505f55565b156121de575b5f8184845f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8180a461219d565b809201918383036121b057806121a2565b622e076360e81b5f5260045ffd5b63b562e8dd60e01b5f5260045ffd5b906001600160601b03169061271082116122445760601b8015612237571768aa4ec00224afccfdb755565b63b4457eaa5f526004601cfd5b63350a88b35f526004601cfd5b600f544211806122b7575b61228f576013545f5b818110612270575050565b61227981611fd3565b50544211806122a1575b61228f57600101612265565b604051633f19d52960e21b8152600490fd5b5060016122ad82611fd3565b5001544210612283565b50601054421061225c565b908160209103126102bb57516103ef816102a9565b9260209161231f935f60018060a01b0360405180978196829584630a85bd0160e11b9c8d86523360048701521660248501526044840152608060648401526084830190610373565b0393165af15f918161235b575b5061234d57612339611d28565b80511561234857805190602001fd5b611f34565b6001600160e01b0319161490565b61237e91925060203d602011612385575b612376818361135b565b8101906122c2565b905f61232c565b503d61236c565b8193929361239b575b50501490565b60059291831b8101915b8135808211851b91825260208092185260405f20910192828410156123cb5792906123a5565b509150505f8061239556fe0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9a26469706673582212200bd450c8e526e18463fa69d512ccdd32c5e5ec8d110c3069a50488d40f082e8664736f6c6343000818003366de8ffda797e3de9c05e8fc57b3bf0ec28a930d40b0d285d93c06501cf6a090a2646970667358221220eb11e5bfc5921e796b4f06a754ed01833da0c003e9118f2ca3c74db1a5d04f5664736f6c63430008180033
Deployed Bytecode
0x608080604052600436101562000013575f80fd5b5f3560e01c90816313966db51462000731575080636497ed2d146200060b57806371114bf114620005e6578063715018a6146200058b5780638da5cb5b1462000562578063921b998214620004a1578063abdd875914620001125763f2fde38b146200007d575f80fd5b346200010e5760203660031901126200010e576200009a6200074d565b620000a46200093c565b6001600160a01b03908116908115620000f6575f54826bffffffffffffffffffffffff60a01b8216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b604051631e4fbdf760e01b81525f6004820152602490fd5b5f80fd5b346200010e576003196080368201126200010e576001600160401b03600435116200010e576004356004016101808092600435360301126200010e576024356001600160401b0381116200010e576200017090369060040162000764565b916044356001600160401b0381116200010e576200019390369060040162000764565b6001600160a01b03946064359392919086851685036200010e57604051956134f29487868101106001600160401b03878a0111176200048d57620002309086620009698a396080878a0152620002196200020b8c8b6102008b620001f88780620008be565b93909460808383010152010191620008f2565b9160246004350190620008be565b8a8901808403607f190160a09091015290620008f2565b600435604481013589880160c0810191909152606482013560e0820152608482013561010082015260a4820135610120909101529099906001600160781b03906200027e9060c40162000912565b16610140878a0101526001600160401b036200029f60e46004350162000927565b16610160878a0101526001600160401b03620002c16101046004350162000927565b16908689010152610124600435013561ffff81168091036200010e576101a08689010152610144600435013588811681036200010e5788168786016101c00152600435610164810135903603602219018112156200010e5760043501986001600160401b0360048b0135116200010e5760c060048b013502360360248b01136200010e57602090607f9a949a19878a018203016101e0888b01015260048501358152019260248101905f5b600482013581106200040357505050928798620003a0889994620003b3946060978b60208b808301860392010152620008f2565b9186890183036040888b010152620008f2565b9316918401015203905ff0908115620003f857602091166040519080337f09e48df7857bd0c1e0d31bb8a85d42cf1874817895f171c917f6ee2cea73ec205f80a38152f35b6040513d5f823e3d90fd5b90919a94600160c0806004938f8060a091358352602081013560208401526001600160781b03620004376040830162000912565b16604084015260606001600160401b036200045482840162000927565b16908401526001600160401b036200046f6080830162000927565b166080840152013560a0820152989e9801970193920190506200036c565b634e487b7160e01b5f52604160045260245ffd5b60403660031901126200010e57620004b86200074d565b620004eb5f80808060015473a804b7d57a4a872dbfbe2987347403039ade20dc5af1620004e4620007b6565b50620007f8565b620004f9600154346200084f565b906040519060208201906340c10f1960e01b825233602484015260243560448401526044835260808301918383106001600160401b038411176200048d5762000560945f948594604052519260018060a01b03165af162000559620007b6565b5062000871565b005b346200010e575f3660031901126200010e575f546040516001600160a01b039091168152602090f35b346200010e575f3660031901126200010e57620005a76200093c565b5f80546001600160a01b0319811682556001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346200010e5760203660031901126200010e57620006036200093c565b600435600155005b60803660031901126200010e57620006226200074d565b60243560ff81168091036200010e57604435906001600160401b03928383116200010e57366023840112156200010e5782600401359384116200010e578360051b9136602484860101116200010e576200069d5f80808060015473a804b7d57a4a872dbfbe2987347403039ade20dc5af1620004e4620007b6565b620006ab600154346200084f565b60405163725f060360e11b6020820190815260248201939093526080604482015260a48101879052956001600160fb1b03106200010e57856200071a60c46200056098875f999860248b9a0184840137336064830152606435608483015281010360a481018452018262000794565b51926001600160a01b03165af162000559620007b6565b346200010e575f3660031901126200010e576020906001548152f35b600435906001600160a01b03821682036200010e57565b9181601f840112156200010e578235916001600160401b0383116200010e57602083818601950101116200010e57565b90601f801991011681019081106001600160401b038211176200048d57604052565b3d15620007f3573d906001600160401b0382116200048d5760405191620007e8601f8201601f19166020018462000794565b82523d5f602084013e565b606090565b156200080057565b60405162461bcd60e51b815260206004820152602160248201527f426c617a654d616e616765723a20666565207472616e73666572206661696c656044820152601960fa1b6064820152608490fd5b919082039182116200085d57565b634e487b7160e01b5f52601160045260245ffd5b156200087957565b60405162461bcd60e51b815260206004820152601960248201527f426c617a654d616e616765723a206d696e74206661696c6564000000000000006044820152606490fd5b9035601e19823603018112156200010e5701602081359101916001600160401b0382116200010e5781360383136200010e57565b908060209392818452848401375f828201840152601f01601f1916010190565b35906001600160781b03821682036200010e57565b35906001600160401b03821682036200010e57565b5f546001600160a01b031633036200095057565b60405163118cdaa760e01b8152336004820152602490fdfe60808060405234620001a457620034f29081380380926200002082620001bc565b8239818101818312620001a45781516001600160401b0393909290848411620001a45783906101809182910312620001a4576200005c6200023d565b9380830151868111620001a457848462000079928401016200025e565b855260a0810151868111620001a457848462000098928401016200025e565b602086015260c0810151604086015260e0810151606086015261010080820151848701526101208083015160a088015261014091620000d9838501620002d2565b60c08901526200010061016095620000f3878701620002e7565b60e08b01528501620002e7565b90880152620001136101a08401620002fc565b90870152620001266101c0830162000323565b908601526101e0810151868111620001a4578385916200014893010162000338565b9084015260a051848111620001a45782620001659183016200025e565b9160c051948511620001a45762000194946200018292016200025e565b906200018d6200030c565b9262000b20565b60405161242c9081620010a68239f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b6080601f91909101601f19168101906001600160401b03821190821017620001e357604052565b620001a8565b60c081019081106001600160401b03821117620001e357604052565b6001600160401b038111620001e357604052565b601f909101601f19168101906001600160401b03821190821017620001e357604052565b6040519061018082016001600160401b03811183821017620001e357604052565b919080601f84011215620001a45782516001600160401b038111620001e357602090604051926200029983601f19601f850116018562000219565b818452828287010111620001a4575f5b818110620002be5750825f9394955001015290565b8581018301518482018401528201620002a9565b51906001600160781b0382168203620001a457565b51906001600160401b0382168203620001a457565b519061ffff82168203620001a457565b60e051906001600160a01b0382168203620001a457565b51906001600160a01b0382168203620001a457565b81601f82011215620001a45780519060206001600160401b038311620001e3576040936040519462000370838660051b018762000219565b848652828601918360c080970286010194818611620001a4578401925b8584106200039f575050505050505090565b8684830312620001a4578487918451620003b981620001e9565b865181528287015183820152620003d2868801620002d2565b868201526060620003e5818901620002e7565b908201526080620003f8818901620002e7565b9082015260a080880151908201528152019301926200038d565b5f910312620001a457565b6040513d5f823e3d90fd5b60405190602082016001600160401b03811183821017620001e3576040525f8252565b90600182811c921680156200047b575b60208310146200046757565b634e487b7160e01b5f52602260045260245ffd5b91607f16916200045b565b601f811162000493575050565b60025f5260205f20906020601f840160051c83019310620004d0575b601f0160051c01905b818110620004c4575050565b5f8155600101620004b8565b9091508190620004af565b601f8111620004e8575050565b60035f5260205f20906020601f840160051c8301931062000525575b601f0160051c01905b81811062000519575050565b5f81556001016200050d565b909150819062000504565b601f81116200053d575050565b600b5f5260205f20906020601f840160051c830193106200057a575b601f0160051c01905b8181106200056e575050565b5f815560010162000562565b909150819062000559565b601f811162000592575050565b600c5f5260205f20906020601f840160051c83019310620005cf575b601f0160051c01905b818110620005c3575050565b5f8155600101620005b7565b9091508190620005ae565b80519091906001600160401b038111620001e3576200060681620006006003546200044b565b620004db565b602080601f83116001146200064b575081906200063a93945f926200063f575b50508160011b915f199060031b1c19161790565b600355565b015190505f8062000626565b60035f52601f198316949091907fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b925f905b878210620006b75750508360019596106200069e575b505050811b01600355565b01515f1960f88460031b161c191690555f808062000693565b806001859682949686015181550195019301906200067d565b80519091906001600160401b038111620001e357620006fc81620006f6600b546200044b565b62000530565b602080601f831160011462000734575081906200072f93945f926200063f5750508160011b915f199060031b1c19161790565b600b55565b600b5f52601f198316949091907f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9925f905b878210620007a057505083600195961062000787575b505050811b01600b55565b01515f1960f88460031b161c191690555f80806200077c565b8060018596829496860151815501950193019062000766565b80519091906001600160401b038111620001e357620007e581620007df600c546200044b565b62000585565b602080601f83116001146200081d575081906200081893945f926200063f5750508160011b915f199060031b1c19161790565b600c55565b600c5f52601f198316949091907fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7925f905b8782106200088957505083600195961062000870575b505050811b01600c55565b01515f1960f88460031b161c191690555f808062000865565b806001859682949686015181550195019301906200084f565b600281901b91906001600160fe1b03811603620008bb57565b634e487b7160e01b5f52601160045260245ffd5b805190680100000000000000008211620001e357601354826013558083106200099d575b5060135f526020908101905f80516020620034d28339815191525f925b8484106200091f575050505050565b600483826200099060019451869060a060039180518455602081015160018501556002840160018060781b03604083015116815490600160781b600160b81b03606085015160781b1690600160b81b600160f81b03608086015160b81b169260ff60f81b1617171790550151910155565b0192019301929062000910565b620009a890620008a2565b620009b383620008a2565b60135f525f80516020620034d283398151915291820191015b818110620009db5750620008f3565b805f600492555f60018201555f60028201555f600382015501620009cc565b61016062000b1e9162000a0e8151620006d0565b62000a1d6020820151620007b9565b6040810151600d556060810151600e556080810151600f5560a081015160105560c08101516011805460e084015161010085015160789190911b600160781b600160b81b03166001600160781b039094167fff00000000000000000000000000000000000000000000000000000000000000909216919091179290921760b89290921b600160b81b600160f81b031691909117905562000ad962000ac761012083015161ffff1690565b61ffff1661ffff196012541617601255565b61014081015162000b16906001600160a01b03166012805462010000600160b01b03191660109290921b62010000600160b01b0316919091179055565b0151620008cf565b565b929162000b2e918362000cdc565b734300000000000000000000000000000000000002803b15620001a4575f809160046040518094819363388a0bbd60e11b83525af1801562000cbf5762000cc5575b50732536fe9ab3f511540f2f9e2ec2a805005c3dd800803b15620001a4576040516336b91f2b60e01b815273f8a82748e7df10d0684b758d02cf6c43ad83ad256004820152905f908290602490829084905af1801562000cbf5762000ca1575b5081516020815191012062000be462000428565b6020815191012014801562000c81575b62000c6f5761014082015162000b1e9261016092909162000c3b906001600160a01b031662000c3462000c2d61012086015161ffff1690565b61ffff1690565b9062000e14565b60408201518062000c5c575b505062000c5481620009fa565b015162000f68565b62000c679162000e5c565b5f8062000c47565b604051635435b28960e11b8152600490fd5b506107d061ffff62000c9961012085015161ffff1690565b161162000bf4565b8062000cb162000cb89262000205565b8062000412565b5f62000bd0565b6200041d565b8062000cb162000cd59262000205565b5f62000b70565b815191939290916001600160401b038111620001e35762000d0a8162000d046002546200044b565b62000486565b602080601f831160011462000d825750908062000d439262000d4c9596975f926200063f5750508160011b915f199060031b1c19161790565b600255620005da565b5f80556001600160a01b0381161562000d6a5762000b1e9062001044565b604051631e4fbdf760e01b81525f6004820152602490fd5b60025f52601f198316969091907f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace925f905b89821062000dfb5750509083929160019462000d4c9798991062000de2575b505050811b01600255620005da565b01515f1960f88460031b161c191690555f808062000dd3565b8060018596829496860151815501950193019062000db4565b6001600160601b0390911690612710821162000e4f5760601b801562000e42571768aa4ec00224afccfdb755565b63b4457eaa5f526004601cfd5b63350a88b35f526004601cfd5b905f5491811562000f305760019162000eb360018060a01b038316926001831460e11b4260a01b17841762000e99875f52600460205260405f2090565b556001600160a01b03165f90815260056020526040902090565b6801000000000000000182028154019055811562000f2a57830192916001815b62000ee1575b505050505f55565b1562000f17575b5f8184845f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8180a462000ed3565b8092019183830362000ee8578062000ed9565b62001097565b63b562e8dd60e01b5f5260045ffd5b805182101562000f545760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b90600382511162000fd5575f805b83518210156200103e5762000f8c828562000f3f565b5151906020918262000f9f858862000f3f565b5101511090811562001021575b811562000fe7575b5062000fd55760019062000fc9838662000f3f565b51015191019062000f76565b60405163097191df60e41b8152600490fd5b905062000ff5838662000f3f565b51511515908162001009575b505f62000fb4565b905062001017838662000f3f565b5151105f62001001565b90508162001030848762000f3f565b510151600f54109062000fac565b50509050565b600a80546001600160a01b0319908116909155600980549182166001600160a01b0393841690811790915591167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3565b622e076360e81b5f5260045ffdfe60806040526004361015610011575f80fd5b5f3560e01c806301ffc9a7146102a457806306fdde031461029f578063081812fc1461029a57806309430a7e14610295578063095ea7b31461029057806318160ddd1461028b57806323b872dd146102865780632a55205a146102815780632e1a7d4d1461027c5780633ccfd60b14610277578063406cf2291461027257806340bd2e231461026d57806340c10f191461026857806342842e0e1461026357806353df5c7c1461025e57806355f804b3146102595780635e0cee0f146102545780636352211e1461024f5780636f8b44b01461024a57806370a0823114610245578063715018a61461024057806375eedb451461023b57806379ba5097146102365780638503e714146102315780638da5cb5b1461022c578063938e3d7b1461022757806395d89b41146102225780639d0172f31461021d578063a22cb46514610218578063b88d4fde14610213578063c87b56dd1461020e578063cff0ab9614610209578063d558296514610204578063e1c2ffad146101ff578063e30c3978146101fa578063e4be0c06146101f5578063e8a3d485146101f0578063e985e9c5146101eb578063f2fde38b146101e6578063f557ab03146101e15763f8bd83e1146101dc575f80fd5b611a9b565b611a5f565b6119f3565b611997565b611968565b611832565b6117fa565b6117d1565b611756565b611638565b611419565b611397565b6112ba565b611270565b6111cb565b61109f565b611077565b61104c565b610fca565b610e9b565b610da3565b610d4d565b610cde565b610caf565b610bd0565b610aae565b610a3e565b610a06565b61093d565b6108ec565b6108b7565b610883565b61080f565b610787565b610773565b61071d565b610673565b610521565b6104d2565b6103f2565b6102bf565b6001600160e01b03198116036102bb57565b5f80fd5b346102bb5760203660031901126102bb5760206004356102de816102a9565b6001600160e01b031981166301ffc9a760e01b811491908215610341575b8215610330575b508115610316575b506040519015158152f35b905060e01c6301ffc9a7632a55205a82149114175f61030b565b635b5e139f60e01b1491505f610303565b6380ac58cd60e01b811492506102fc565b5f5b8381106103635750505f910152565b8181015183820152602001610354565b9060209161038c81518092818552858086019101610352565b601f01601f1916010190565b90602091602081526064518060208301525f5b8181106103cb575060409293505f838284010152601f8019910116010190565b60848101518382016040015284016103ab565b9060206103ef928181520190610373565b90565b346102bb575f3660031901126102bb576040515f600254610412816114b5565b808452906020906001908181169081156104a8575060011461044f575b61044b8561043f8187038261135b565b604051918291826103de565b0390f35b60025f90815293507f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace5b8385106104955750505050810160200161043f8261044b61042f565b8054868601840152938201938101610479565b86955061044b9693506020925061043f94915060ff191682840152151560051b820101929361042f565b346102bb5760203660031901126102bb576004356104ef81612034565b15610512575f526006602052602060018060a01b0360405f205416604051908152f35b6333d1c03960e21b5f5260045ffd5b346102bb575f3660031901126102bb5760135461053d81611b01565b60409161054d604051928361135b565b8082526020808301918260135f527f66de8ffda797e3de9c05e8fc57b3bf0ec28a930d40b0d285d93c06501cf6a0905f915b83831061060e57505050506040519281840190828552518091526040840192915f5b8281106105ae5785850386f35b8351805186528083015186840152878101516001600160781b0316888701526060808201516001600160401b03908116918801919091526080808301519091169087015260a0908101519086015260c090940193928101926001016105a1565b600485600192610620859a989a611b18565b81520192019201919095939561057f565b600435906001600160a01b03821682036102bb57565b602435906001600160a01b03821682036102bb57565b604435906001600160a01b03821682036102bb57565b60403660031901126102bb57610687610631565b602435906001600160a01b038061069d846120ae565b16908133036106ee575b835f52600660205260405f20921691826001600160601b0360a01b8254161790557f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f80a4005b5f82815260076020908152604080832033845290915290205460ff166106a7576367d9dca160e11b5f5260045ffd5b346102bb575f3660031901126102bb5760205f546001549003604051908152f35b60609060031901126102bb576001600160a01b039060043582811681036102bb579160243590811681036102bb579060443590565b61078561077f3661073e565b91611b7c565b005b346102bb5760403660031901126102bb576024356004355f5268aa4ec00224afccfdb7908160205260405f20548060601c9283156107fd575b5061044b908360601b1892835f1904831184023d3d3e6127106040519485940204908360209093929193604081019460018060a01b031681520152565b54606081901c9350905061044b6107c0565b346102bb5760203660031901126102bb5760043561082b61211e565b6015548082111561086657505f80808093816015555b335af161084c611d28565b501561085457005b6040516327fcd9d160e01b8152600490fd5b81810390811161087e575f8080938193601555610841565b611d14565b346102bb575f3660031901126102bb5761089b61211e565b5f80808047335af16108ab611d28565b5015610854575f601555005b346102bb575f3660031901126102bb576108cf61211e565b47601554810390811161087e575f80808093335af161084c611d28565b346102bb5760203660031901126102bb5760043561090861211e565b47601554810390811161087e57811161092b575f80808093335af161084c611d28565b60405163356680b760e01b8152600490fd5b60403660031901126102bb57610951610631565b602435600f54421080156109fb575b80156109ef575b6109dd57610976815f54611d57565b600e54106109cb576109a8816109a36109976011546001600160781b031690565b6001600160781b031690565b611d64565b341061092b57610785916109c66109c134601554611d57565b601555565b612132565b60405163d05cb60960e01b8152600490fd5b60405163914edb0f60e01b8152600490fd5b5060175460ff16610967565b504260105410610960565b610a0f3661073e565b6040519160208301938385106001600160401b03861117610a3957610785946040525f8452611f02565b611347565b346102bb575f3660031901126102bb57610a5661211e565b6014805460ff19166001179055005b9060206003198301126102bb576004356001600160401b03928382116102bb57806023830112156102bb5781600401359384116102bb57602484830101116102bb576024019190565b346102bb57610abc36610a65565b610ac461211e565b60ff60145416610bbe576001600160401b038111610a3957610af081610aeb600b546114b5565b611d77565b5f601f8211600114610b52578190610b1d935f92610b47575b50508160011b915f199060031b1c19161790565b600b555b7fa1731ca444c73d019f0dbb4ee5546c98730f4ffcdaa1c29776ab542aa64d5e1b5f80a1005b013590505f80610b09565b600b5f52601f198216925f805160206123d7833981519152915f5b858110610ba657508360019510610b8d575b505050811b01600b55610b21565b01355f19600384901b60f8161c191690555f8080610b7f565b90926020600181928686013581550194019101610b6d565b60405163696c636960e01b8152600490fd5b346102bb5760403660031901126102bb57610be9610631565b60243561ffff8116918282036102bb57610c0161211e565b60ff60145460101c16610c9d576107d08311610c8b57601280546001600160b01b031916601083901b62010000600160b01b03161761ffff84161790557f4db95622f7059a0983b8b21ce94db601f1f2e63da11a652d59d8d7f77c4ff1a192610c6a908261220c565b604080516001600160a01b0392909216825261ffff929092166020820152a1005b60405163a2a65b5360e01b8152600490fd5b604051635a214b2560e11b8152600490fd5b346102bb5760203660031901126102bb5760206001600160a01b03610cd56004356120ae565b16604051908152f35b346102bb5760203660031901126102bb57600435610cfa61211e565b610d02612251565b5f548110610d3b576020817f7810bd47de260c3e9ee10061cf438099dd12256c79485f12f94dbccc981e806c92600e55604051908152a1005b60405163066f305360e21b8152600490fd5b346102bb5760203660031901126102bb576001600160a01b03610d6e610631565b168015610d94575f52600560205260206001600160401b0360405f205416604051908152f35b6323d3ad8160e21b5f5260045ffd5b346102bb575f3660031901126102bb57610dbb61211e565b600a80546001600160a01b03199081169091556009805491821690555f906001600160a01b03167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b9181601f840112156102bb578235916001600160401b0383116102bb576020808501948460051b0101116102bb57565b6020808201906020835283518092526040830192602060408460051b8301019501935f915b848310610e6d5750505050505090565b9091929394958480610e8b600193603f198682030187528a51610373565b9801930193019194939290610e5d565b346102bb5760203660031901126102bb57600480356001600160401b0381116102bb57610ecc903690600401610e08565b91610ed561211e565b610ede83611e3e565b925f5b818110610ef6576040518061044b8782610e38565b5f80610f03838588611e9b565b60409391610f15855180938193611ee1565b0390305af490610f23611d28565b9115610f4a575090600191610f388288611eee565b52610f438187611eee565b5001610ee1565b846044835110610f7157905162461bcd60e51b8152908190610f6d908201610398565b0390fd5b610f6d915191829162461bcd60e51b8352820160809060208152602560208201527f5472616e73616374696f6e20726576657274656420776974686f75742061207260408201526432b0b9b7b760d91b60608201520190565b346102bb575f3660031901126102bb57600a546001600160a01b033381831603611034576001600160601b0360a01b809216600a556009549133908316176009553391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b60405163118cdaa760e01b8152336004820152602490fd5b346102bb575f3660031901126102bb5761106461211e565b6014805462ff0000191662010000179055005b346102bb575f3660031901126102bb576009546040516001600160a01b039091168152602090f35b346102bb576110ad36610a65565b6110b561211e565b60ff60145460081c166111b9576001600160401b038111610a39576110e4816110df600c546114b5565b611dd1565b5f601f821160011461113a578190611110935f92610b475750508160011b915f199060031b1c19161790565b600c555b7fa5d4097edda6d87cb9329af83fb3712ef77eeb13738ffe43cc35a4ce305ad9625f80a1005b600c5f52601f198216927fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7915f5b8581106111a157508360019510611188575b505050811b01600c55611114565b01355f19600384901b60f8161c191690555f808061117a565b90926020600181928686013581550194019101611168565b604051631fd0326960e01b8152600490fd5b346102bb575f3660031901126102bb576040515f6003546111eb816114b5565b808452906020906001908181169081156104a857506001146112175761044b8561043f8187038261135b565b60035f90815293507fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b5b83851061125d5750505050810160200161043f8261044b61042f565b8054868601840152938201938101611241565b346102bb5760203660031901126102bb576004356001600160781b0381168091036102bb5761129d61211e565b6112a5612251565b6001600160781b031960115416176011555f80f35b346102bb5760403660031901126102bb576112d3610631565b602435908115158092036102bb57335f9081526007602090815260408083206001600160a01b0385168452909152902060ff1981541660ff841617905560405191825260018060a01b0316907f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160203392a3005b634e487b7160e01b5f52604160045260245ffd5b90601f801991011681019081106001600160401b03821117610a3957604052565b6001600160401b038111610a3957601f01601f191660200190565b60803660031901126102bb576113ab610631565b6113b3610647565b606435916001600160401b0383116102bb57366023840112156102bb578260040135916113df8361137c565b926113ed604051948561135b565b80845236602482870101116102bb576020815f9260246107859801838801378501015260443591611f02565b346102bb5760203660031901126102bb5760043561143681612034565b156114a35760405190608082019060a083016040525f8252905b5f190190600a9060308282060183530490816114505761149761043f61044b92856080601f1992838101920301815260405193849161149160208401611f43565b90611fbc565b0390810183528261135b565b60405163677510db60e11b8152600490fd5b90600182811c921680156114e3575b60208310146114cf57565b634e487b7160e01b5f52602260045260245ffd5b91607f16916114c4565b604051905f82600c5491611500836114b5565b8083529260209060019081811690811561158c575060011461152d575b505061152b9250038361135b565b565b915092600c5f527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7935f925b828410611574575061152b9450505081016020015f8061151d565b85548885018301529485019487945092810192611559565b9150506020925061152b94915060ff191682840152151560051b8201015f8061151d565b9995909461014099946115ef8c6001600160781b03969f9e9b98956115e161ffff9c97610160808552840190610373565b916020818403910152610373565b60408d019e909e5260608c015260808b015260a08a01521660c08801526001600160401b0391821660e088015216610100860152166101208401526001600160a01b0316910152565b346102bb575f3660031901126102bb57604051600b54815f611659836114b5565b8083529260209060019081811690811561173257506001146116e6575b50506116849250038261135b565b61168c6114ed565b9061044b600d5491600e5493600f549160105490601154906001600160401b039260125495604051998a9961ffff60018060a01b038a60101c169916976001600160781b03888860b81c16988860781c169716958c6115b0565b915092600b5f525f805160206123d7833981519152935f925b82841061171a57506116849450505081016020015f80611676565b855487850183015294850194869450928101926116ff565b9150506020925061168494915060ff191682840152151560051b8201015f80611676565b346102bb575f3660031901126102bb5761176e61211e565b600160ff1960175416176017557f7810bd47de260c3e9ee10061cf438099dd12256c79485f12f94dbccc981e806c60205f5480600e55604051907fcbbaae1b89885aa88b0db407075a1f3e6df45931447d19c7da5f5b7471a729e55f80a18152a1005b346102bb575f3660031901126102bb576117e961211e565b6014805461ff001916610100179055005b346102bb575f3660031901126102bb57600a546040516001600160a01b039091168152602090f35b6004359060ff821682036102bb57565b60803660031901126102bb57611846611822565b6024356001600160401b0381116102bb57611865903690600401610e08565b919061186f61065d565b9261188561187f60643594611fd3565b50611b18565b80514210801561195b575b6119495761189f845f54611d57565b600e54106109cb576118c2846109a361099760408501516001600160781b031690565b341061092b57604080516001600160a01b038716602082019081528183018790529181526119209461191c949092909160a09161190c9161190460608261135b565b51902061200b565b602081519101209301519161238c565b1590565b61193757610785916109c66109c134601554611d57565b60405163582f497d60e11b8152600490fd5b60405163cbe8d62360e01b8152600490fd5b5042602082015110611890565b346102bb575f3660031901126102bb5761044b6119836114ed565b604051918291602083526020830190610373565b346102bb5760403660031901126102bb57602060ff6119e76119b7610631565b6119bf610647565b6001600160a01b039182165f9081526007865260408082209290931681526020919091522090565b54166040519015158152f35b346102bb5760203660031901126102bb57611a0c610631565b611a1461211e565b600a80546001600160a01b0319166001600160a01b039283169081179091556009549091167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e227005f80a3005b346102bb575f3660031901126102bb57606060145460ff6040519181811615158352818160081c161515602084015260101c1615156040820152f35b346102bb5760403660031901126102bb57611ab4611822565b602435906001600160781b0382168092036102bb57611ae4600291611ad761211e565b611adf612251565b611fd3565b500180546effffffffffffffffffffffffffffff19169091179055005b6001600160401b038111610a395760051b60200190565b6040516001600160401b03929160c0820184811183821017610a395760a0916003916040528395815485526001820154602086015260028201546001600160781b0381166040870152818160781c16606087015260b81c1660808501520154910152565b919091611b88826120ae565b6001600160a01b039182169390828116859003611d0f575f8481526006602052604090208054611bc76001600160a01b03881633908114908314171590565b611cc5575b611cbc575b506001600160a01b0385165f90815260056020526040902080545f190190556001600160a01b0382165f908152600560205260409020805460010190556001600160a01b0382164260a01b17600160e11b17611c35855f52600460205260405f2090565b55600160e11b811615611c77575b501680927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a415611c7257565b612090565b60018401611c8d815f52600460205260405f2090565b5415611c9a575b50611c43565b5f548114611c9457611cb4905f52600460205260405f2090565b555f80611c94565b5f90555f611bd1565b611d0561191c611cfe33611ce98b60018060a01b03165f52600760205260405f2090565b9060018060a01b03165f5260205260405f2090565b5460ff1690565b15611bcc57612081565b612073565b634e487b7160e01b5f52601160045260245ffd5b3d15611d52573d90611d398261137c565b91611d47604051938461135b565b82523d5f602084013e565b606090565b9190820180921161087e57565b8181029291811591840414171561087e57565b601f8111611d83575050565b600b5f525f805160206123d7833981519152906020601f840160051c83019310611dc7575b601f0160051c01905b818110611dbc575050565b5f8155600101611db1565b9091508190611da8565b601f8111611ddd575050565b600c5f527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7906020601f840160051c83019310611e34575b601f0160051c01905b818110611e29575050565b5f8155600101611e1e565b9091508190611e15565b90611e4882611b01565b611e55604051918261135b565b8281528092611e66601f1991611b01565b01905f5b828110611e7657505050565b806060602080938501015201611e6a565b634e487b7160e01b5f52603260045260245ffd5b9190811015611edc5760051b81013590601e19813603018212156102bb5701908135916001600160401b0383116102bb5760200182360381136102bb579190565b611e87565b908092918237015f815290565b8051821015611edc5760209160051b010190565b929190611f10828286611b7c565b803b611f1d575b50505050565b611f26936122d7565b15611f34575f808080611f17565b6368d2bf6b60e11b5f5260045ffd5b600b545f9291611f52826114b5565b91600190818116908115611fa95750600114611f6d57505050565b9091929350600b5f525f805160206123d7833981519152905f915b848310611f96575050500190565b8181602092548587015201920191611f88565b60ff191683525050811515909102019150565b90611fcf60209282815194859201610352565b0190565b601354811015611edc5760135f5260021b7f66de8ffda797e3de9c05e8fc57b3bf0ec28a930d40b0d285d93c06501cf6a09001905f90565b9060405191602083015260208252604082018281106001600160401b03821117610a3957604052565b905f915f5481106120425750565b9091505b805f52600460205260405f2054806120675750801561087e575f1901612046565b600160e01b1615919050565b62a1148160e81b5f5260045ffd5b632ce44b5f60e11b5f5260045ffd5b633a954ecd60e21b5f5260045ffd5b636f96cda160e11b5f5260045ffd5b6120c0815f52600460205260405f2090565b549081156120d75750600160e01b811661209f5790565b90505f5481101561209f575b5f19015f818152600460205260409020549081156121175750600160e01b8116156103ef57636f96cda160e11b5f5260045ffd5b90506120e3565b6009546001600160a01b0316330361103457565b5f549180156121fd576001916001600160a01b0381164260a01b83851460e11b1717612166855f52600460205260405f2090565b556001600160a01b03165f81815260056020526040902080546801000000000000000184020190559081156121ef57830192916001815b6121aa575b505050505f55565b156121de575b5f8184845f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8180a461219d565b809201918383036121b057806121a2565b622e076360e81b5f5260045ffd5b63b562e8dd60e01b5f5260045ffd5b906001600160601b03169061271082116122445760601b8015612237571768aa4ec00224afccfdb755565b63b4457eaa5f526004601cfd5b63350a88b35f526004601cfd5b600f544211806122b7575b61228f576013545f5b818110612270575050565b61227981611fd3565b50544211806122a1575b61228f57600101612265565b604051633f19d52960e21b8152600490fd5b5060016122ad82611fd3565b5001544210612283565b50601054421061225c565b908160209103126102bb57516103ef816102a9565b9260209161231f935f60018060a01b0360405180978196829584630a85bd0160e11b9c8d86523360048701521660248501526044840152608060648401526084830190610373565b0393165af15f918161235b575b5061234d57612339611d28565b80511561234857805190602001fd5b611f34565b6001600160e01b0319161490565b61237e91925060203d602011612385575b612376818361135b565b8101906122c2565b905f61232c565b503d61236c565b8193929361239b575b50501490565b60059291831b8101915b8135808211851b91825260208092185260405f20910192828410156123cb5792906123a5565b509150505f8061239556fe0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9a26469706673582212200bd450c8e526e18463fa69d512ccdd32c5e5ec8d110c3069a50488d40f082e8664736f6c6343000818003366de8ffda797e3de9c05e8fc57b3bf0ec28a930d40b0d285d93c06501cf6a090a2646970667358221220eb11e5bfc5921e796b4f06a754ed01833da0c003e9118f2ca3c74db1a5d04f5664736f6c63430008180033
Net Worth in USD
Net Worth in ETH
Token Allocations
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| BASE | 100.00% | $2,835.5 | 0.00134 | $3.8 |
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.