// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;
import { Ownable, OwnableRoles } from "solady/auth/OwnableRoles.sol";
import { ISoundEditionV1_2 } from "@core/interfaces/ISoundEditionV1_2.sol";
import { IMinterModuleV2_1 } from "@core/interfaces/IMinterModuleV2_1.sol";
import { IERC165 } from "openzeppelin/utils/introspection/IERC165.sol";
import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol";
import { FixedPointMathLib } from "solady/utils/FixedPointMathLib.sol";
import { SafeCastLib } from "solady/utils/SafeCastLib.sol";
import { MerkleProofLib } from "solady/utils/MerkleProofLib.sol";
import { LibMulticaller } from "multicaller/LibMulticaller.sol";
/**
* @title Minter Base
* @dev The `BaseMinterV2_1` class maintains a central storage record of edition mint instances.
*/
abstract contract BaseMinterV2_1 is IMinterModuleV2_1, Ownable {
// =============================================================
// CONSTANTS
// =============================================================
/**
* @dev This is the denominator, in basis points (BPS), for any of the fees.
*/
uint16 public constant BPS_DENOMINATOR = 10_000;
/**
* @dev The maximum basis points (BPS) limit allowed for the affiliate fees.
*/
uint16 public constant MAX_AFFILIATE_FEE_BPS = 1000;
/**
* @dev The maximum basis points (BPS) limit allowed for the platform fees.
*/
uint16 public constant MAX_PLATFORM_FEE_BPS = 1000;
/**
* @dev The maximum platform flat fee per transaction NFT.
*/
uint96 public constant MAX_PLATFORM_PER_TX_FLAT_FEE = 0.1 ether;
/**
* @dev The maximum platform flat fee per NFT.
*/
uint96 public constant MAX_PLATFORM_FLAT_FEE = 0.1 ether;
/**
* @dev The interface id for IMinterModuleV2.
*/
bytes4 internal constant _INTERFACE_ID_MINTER_MODULE_V2 = 0xf8ccd08e;
// =============================================================
// STORAGE
// =============================================================
/**
* @dev The platform fee address.
*/
address public platformFeeAddress;
/**
* @dev The next mint ID. Shared amongst all editions connected.
*/
uint96 private _nextMintId;
/**
* @dev How much platform fees have been accrued.
*/
uint128 public platformFeesAccrued;
/**
* @dev The amount of platform flat fees per token.
*/
uint96 public platformFlatFee;
/**
* @dev The platform fee in basis points.
*/
uint16 public platformFeeBPS;
/**
* @dev The amount of platform flat fees per transaction.
*/
uint96 public platformPerTxFlatFee;
/**
* @dev Maps an edition and the mint ID to a mint instance.
*/
mapping(address => mapping(uint256 => BaseData)) private _baseData;
/**
* @dev Maps an address to how much affiliate fees have they accrued.
*/
mapping(address => uint128) public affiliateFeesAccrued;
// =============================================================
// CONSTRUCTOR
// =============================================================
constructor() payable {
_initializeOwner(msg.sender);
}
// =============================================================
// PUBLIC / EXTERNAL WRITE FUNCTIONS
// =============================================================
// Per edition mint parameter setters:
// -----------------------------------
// These functions can only be called by the owner or admin of the edition.
/**
* @inheritdoc IMinterModuleV2_1
*/
function setEditionMintPaused(
address edition,
uint128 mintId,
bool paused
) public virtual onlyEditionOwnerOrAdmin(edition) {
_getBaseData(edition, mintId).mintPaused = paused;
emit MintPausedSet(edition, mintId, paused);
}
/**
* @inheritdoc IMinterModuleV2_1
*/
function setTimeRange(
address edition,
uint128 mintId,
uint32 startTime,
uint32 endTime
) public virtual onlyEditionOwnerOrAdmin(edition) {
if (startTime >= endTime) revert InvalidTimeRange();
BaseData storage baseData = _getBaseData(edition, mintId);
baseData.startTime = startTime;
baseData.endTime = endTime;
emit TimeRangeSet(edition, mintId, startTime, endTime);
}
/**
* @inheritdoc IMinterModuleV2_1
*/
function setAffiliateFee(
address edition,
uint128 mintId,
uint16 bps
) public virtual override onlyEditionOwnerOrAdmin(edition) {
if (bps > MAX_AFFILIATE_FEE_BPS) revert InvalidAffiliateFeeBPS();
_getBaseData(edition, mintId).affiliateFeeBPS = bps;
emit AffiliateFeeSet(edition, mintId, bps);
}
/**
* @inheritdoc IMinterModuleV2_1
*/
function setAffiliateMerkleRoot(
address edition,
uint128 mintId,
bytes32 root
) public virtual override onlyEditionOwnerOrAdmin(edition) {
_getBaseData(edition, mintId).affiliateMerkleRoot = root;
emit AffiliateMerkleRootSet(edition, mintId, root);
}
// Withdrawal functions:
// ---------------------
// These functions can be called by anyone.
/**
* @inheritdoc IMinterModuleV2_1
*/
function withdrawForAffiliate(address affiliate) public override {
uint128 accrued = affiliateFeesAccrued[affiliate];
if (accrued != 0) {
affiliateFeesAccrued[affiliate] = 0;
SafeTransferLib.forceSafeTransferETH(affiliate, accrued);
emit AffiliateFeesWithdrawn(affiliate, accrued);
}
}
/**
* @inheritdoc IMinterModuleV2_1
*/
function withdrawForPlatform() public override {
address to = platformFeeAddress;
if (to == address(0)) revert PlatformFeeAddressIsZero();
uint128 accrued = platformFeesAccrued;
if (accrued != 0) {
platformFeesAccrued = 0;
SafeTransferLib.forceSafeTransferETH(to, accrued);
emit PlatformFeesWithdrawn(accrued);
}
}
// Only owner setters:
// -------------------
// These functions can only be called by the owner of the minter contract.
/**
* @inheritdoc IMinterModuleV2_1
*/
function setPlatformFee(uint16 bps) public onlyOwner {
if (bps > MAX_PLATFORM_FEE_BPS) revert InvalidPlatformFeeBPS();
platformFeeBPS = bps;
emit PlatformFeeSet(bps);
}
/**
* @inheritdoc IMinterModuleV2_1
*/
function setPlatformFlatFee(uint96 flatFee) public onlyOwner {
if (flatFee > MAX_PLATFORM_FLAT_FEE) revert InvalidPlatformFlatFee();
platformFlatFee = flatFee;
emit PlatformFlatFeeSet(flatFee);
}
/**
* @inheritdoc IMinterModuleV2_1
*/
function setPlatformPerTxFlatFee(uint96 perTxFlatFee) public onlyOwner {
if (perTxFlatFee > MAX_PLATFORM_PER_TX_FLAT_FEE) revert InvalidPlatformPerTxFlatFee();
platformPerTxFlatFee = perTxFlatFee;
emit PlatformPerTxFlatFeeSet(perTxFlatFee);
}
/**
* @inheritdoc IMinterModuleV2_1
*/
function setPlatformFeeAddress(address addr) public onlyOwner {
if (addr == address(0)) revert PlatformFeeAddressIsZero();
platformFeeAddress = addr;
emit PlatformFeeAddressSet(addr);
}
// =============================================================
// PUBLIC / EXTERNAL VIEW FUNCTIONS
// =============================================================
/**
* @inheritdoc IMinterModuleV2_1
*/
function nextMintId() external view returns (uint128) {
return _nextMintId;
}
/**
* @inheritdoc IMinterModuleV2_1
*/
function isAffiliatedWithProof(
address edition,
uint128 mintId,
address affiliate,
bytes32[] calldata affiliateProof
) public view virtual override returns (bool) {
bytes32 root = _getBaseData(edition, mintId).affiliateMerkleRoot;
// If the root is empty, then use the default logic.
if (root == bytes32(0)) {
return affiliate != address(0);
}
// Otherwise, check if the affiliate is in the Merkle tree.
// The check that that affiliate is not a zero address is to prevent libraries
// that fill up partial Merkle trees with empty leafs from screwing things up.
return
affiliate != address(0) &&
MerkleProofLib.verifyCalldata(affiliateProof, root, _keccak256EncodePacked(affiliate));
}
/**
* @inheritdoc IMinterModuleV2_1
*/
function isAffiliated(
address edition,
uint128 mintId,
address affiliate
) public view virtual override returns (bool) {
return isAffiliatedWithProof(edition, mintId, affiliate, MerkleProofLib.emptyProof());
}
/**
* @inheritdoc IMinterModuleV2_1
*/
function affiliateMerkleRoot(address edition, uint128 mintId) external view returns (bytes32) {
return _getBaseData(edition, mintId).affiliateMerkleRoot;
}
/**
* @inheritdoc IERC165
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return
interfaceId == _INTERFACE_ID_MINTER_MODULE_V2 ||
interfaceId == type(IMinterModuleV2_1).interfaceId ||
interfaceId == this.supportsInterface.selector;
}
/**
* @inheritdoc IMinterModuleV2_1
*/
function totalPriceAndFees(
address edition,
uint128 mintId,
uint32 quantity
)
public
view
virtual
override
returns (
uint256 total,
uint256 subTotal,
uint256 platformFlatFeeTotal,
uint256 platformFee,
uint256 affiliateFee
)
{
BaseData storage baseData = _getBaseData(edition, mintId);
unchecked {
subTotal = uint256(quantity) * uint256(baseData.price); // Before additional fees.
platformFlatFeeTotal = uint256(quantity) * uint256(platformFlatFee) + uint256(platformPerTxFlatFee);
total = subTotal + platformFlatFeeTotal;
platformFee = (subTotal * uint256(platformFeeBPS)) / uint256(BPS_DENOMINATOR) + platformFlatFeeTotal;
affiliateFee = (subTotal * uint256(baseData.affiliateFeeBPS)) / uint256(BPS_DENOMINATOR);
}
}
// =============================================================
// INTERNAL / PRIVATE HELPERS
// =============================================================
/**
* @dev Requires that the caller is the owner or admin of `edition`.
* @param edition The edition address.
*/
modifier onlyEditionOwnerOrAdmin(address edition) {
_requireOnlyEditionOwnerOrAdmin(edition);
_;
}
/**
* @dev Requires that the caller is the owner or admin of `edition`.
* @param edition The edition address.
*/
function _requireOnlyEditionOwnerOrAdmin(address edition) internal view {
address sender = LibMulticaller.sender();
if (sender != OwnableRoles(edition).owner())
if (!OwnableRoles(edition).hasAnyRole(sender, ISoundEditionV1_2(edition).ADMIN_ROLE()))
revert Unauthorized();
}
/**
* @dev If overriden to return true, the amount of ETH paid must be exact.
* @return The constant value.
*/
function _useExactPayment() internal view virtual returns (bool) {
return true;
}
/**
* @dev Creates an edition mint instance.
* @param edition The edition address.
* @param startTime The start time of the mint.
* @param endTime The end time of the mint.
* @param affiliateFeeBPS The affiliate fee in basis points.
* @return mintId The ID for the mint instance.
* Calling conditions:
* - Must be owner or admin of the edition.
*/
function _createEditionMint(
address edition,
uint32 startTime,
uint32 endTime,
uint16 affiliateFeeBPS
) internal onlyEditionOwnerOrAdmin(edition) returns (uint128 mintId) {
if (startTime >= endTime) revert InvalidTimeRange();
if (affiliateFeeBPS > MAX_AFFILIATE_FEE_BPS) revert InvalidAffiliateFeeBPS();
mintId = _nextMintId;
BaseData storage data = _getBaseDataUnchecked(edition, mintId);
data.startTime = startTime;
data.endTime = endTime;
data.affiliateFeeBPS = affiliateFeeBPS;
data.created = true;
unchecked {
_nextMintId = SafeCastLib.toUint96(mintId + 1);
}
emit MintConfigCreated(edition, msg.sender, mintId, startTime, endTime, affiliateFeeBPS);
}
/**
* For avoiding stack too deep.
*/
struct _MintTemps {
bool affiliated;
uint256 affiliateFee;
uint256 remainingPayment;
uint256 platformFee;
uint256 requiredEtherValue;
}
/**
* @dev Mints `quantity` of `edition` to `to` with a required payment of `requiredEtherValue`.
* Note: this function should be called at the end of a function due to it refunding any
* excess ether paid, to adhere to the checks-effects-interactions pattern.
* Otherwise, a reentrancy guard must be used.
* @param edition The edition address.
* @param mintId The ID for the mint instance.
* @param to The address to mint to.
* @param quantity The quantity of tokens to mint.
* @param affiliate The affiliate (referral) address.
* @param affiliateProof The Merkle proof needed for verifying the affiliate, if any.
* @param attributionId The attribution ID.
*/
function _mintTo(
address edition,
uint128 mintId,
address to,
uint32 quantity,
address affiliate,
bytes32[] calldata affiliateProof,
uint256 attributionId
) internal {
BaseData storage baseData = _getBaseData(edition, mintId);
_MintTemps memory t;
/* --------------------- GENERAL CHECKS --------------------- */
{
uint32 startTime = baseData.startTime;
uint32 endTime = baseData.endTime;
if (block.timestamp < startTime) revert MintNotOpen(block.timestamp, startTime, endTime);
if (block.timestamp > endTime) revert MintNotOpen(block.timestamp, startTime, endTime);
if (baseData.mintPaused) revert MintPaused();
}
/* ----------- AFFILIATE AND PLATFORM FEES LOGIC ------------ */
unchecked {
// `requiredEtherValue = platformFee + affiliateFee + artistFee`.
(t.requiredEtherValue, , , t.platformFee, t.affiliateFee) = totalPriceAndFees(edition, mintId, quantity);
// Reverts if the payment is not exact, or not enough.
if (_useExactPayment()) {
if (msg.value != t.requiredEtherValue) revert WrongPayment(msg.value, t.requiredEtherValue);
} else {
if (msg.value < t.requiredEtherValue) revert Underpaid(msg.value, t.requiredEtherValue);
}
// Increment the platform fees accrued.
platformFeesAccrued = SafeCastLib.toUint128(uint256(platformFeesAccrued) + t.platformFee);
// Deduct the platform BPS fee, and the platform flat fees.
// Won't underflow as `platformFee <= requiredEtherValue`;
t.remainingPayment = t.requiredEtherValue - t.platformFee;
}
// Check if the mint is an affiliated mint.
t.affiliated = isAffiliatedWithProof(edition, mintId, affiliate, affiliateProof);
unchecked {
if (t.affiliated) {
// Deduct the affiliate fee from the remaining payment.
// Won't underflow as `affiliateFee <= remainingPayment`.
t.remainingPayment -= t.affiliateFee;
// Increment the affiliate fees accrued.
affiliateFeesAccrued[affiliate] = SafeCastLib.toUint128(
uint256(affiliateFeesAccrued[affiliate]) + t.affiliateFee
);
} else {
t.affiliateFee = 0;
// If the affiliate is not the zero address despite not being
// affiliated, it might be due to an invalid affiliate proof.
// Revert to prevent unintended skipping of affiliate payment.
if (affiliate != address(0)) {
revert InvalidAffiliate();
}
}
}
/* ------------------------- MINT --------------------------- */
// Emit the event.
emit Minted(
edition,
mintId,
to,
// Need to put this call here to avoid stack-too-deep error (it returns `fromTokenId`).
uint32(ISoundEditionV1_2(edition).mint{ value: t.remainingPayment }(to, quantity)),
quantity,
uint128(t.requiredEtherValue),
uint128(t.platformFee),
uint128(t.affiliateFee),
affiliate,
t.affiliated,
attributionId
);
/* ------------------------- REFUND ------------------------- */
unchecked {
if (!_useExactPayment()) {
// Note: We do this at the end to avoid creating a reentrancy vector.
// Refund the user any ETH they spent over the current total price of the NFTs.
if (msg.value > t.requiredEtherValue) {
SafeTransferLib.forceSafeTransferETH(msg.sender, msg.value - t.requiredEtherValue);
}
}
}
}
/**
* @dev Increments `totalMinted` with `quantity`, reverting if `totalMinted + quantity > maxMintable`.
* @param totalMinted The current total number of minted tokens.
* @param maxMintable The maximum number of mintable tokens.
* @return `totalMinted` + `quantity`.
*/
function _incrementTotalMinted(
uint32 totalMinted,
uint32 quantity,
uint32 maxMintable
) internal pure returns (uint32) {
unchecked {
// Won't overflow as both are 32 bits.
uint256 sum = uint256(totalMinted) + uint256(quantity);
if (sum > maxMintable) {
// Note that the `maxMintable` may vary and drop over time
// and cause `totalMinted` to be greater than `maxMintable`.
// The `zeroFloorSub` is equivalent to `max(0, x - y)`.
uint32 available = uint32(FixedPointMathLib.zeroFloorSub(maxMintable, totalMinted));
revert ExceedsAvailableSupply(available);
}
return uint32(sum);
}
}
/**
* @dev Returns the storage pointer to the BaseData for (`edition`, `mintId`).
* @param edition The edition address.
* @param mintId The mint ID.
* @return data Storage pointer to a BaseData.
*/
function _getBaseDataUnchecked(address edition, uint128 mintId) internal view returns (BaseData storage data) {
data = _baseData[edition][mintId];
}
/**
* @dev Returns the storage pointer to the BaseData for (`edition`, `mintId`).
* Reverts if the mint does not exist.
* @param edition The edition address.
* @param mintId The mint ID.
* @return data Storage pointer to a BaseData.
*/
function _getBaseData(address edition, uint128 mintId) internal view returns (BaseData storage data) {
data = _getBaseDataUnchecked(edition, mintId);
if (!data.created) revert MintDoesNotExist();
}
/**
* @dev Casts the storage pointer to the BaseData to a bytes32.
* @param data Storage pointer to a BaseData.
* @return result The casted value of the slot.
*/
function _baseDataSlot(BaseData storage data) internal pure returns (bytes32 result) {
assembly {
result := data.slot
}
}
/**
* @dev Equivalent to `keccak256(abi.encodePacked(addr))`.
* @param addr The address to hash.
* @return result The hash of the address.
*/
function _keccak256EncodePacked(address addr) internal pure returns (bytes32 result) {
assembly {
mstore(0x00, addr)
result := keccak256(0x0c, 0x14)
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
library FixedPointMathLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The operation failed, as the output exceeds the maximum value of uint256.
error ExpOverflow();
/// @dev The operation failed, as the output exceeds the maximum value of uint256.
error FactorialOverflow();
/// @dev The operation failed, due to an multiplication overflow.
error MulWadFailed();
/// @dev The operation failed, either due to a
/// multiplication overflow, or a division by a zero.
error DivWadFailed();
/// @dev The multiply-divide operation failed, either due to a
/// multiplication overflow, or a division by a zero.
error MulDivFailed();
/// @dev The division failed, as the denominator is zero.
error DivFailed();
/// @dev The full precision multiply-divide operation failed, either due
/// to the result being larger than 256 bits, or a division by a zero.
error FullMulDivFailed();
/// @dev The output is undefined, as the input is less-than-or-equal to zero.
error LnWadUndefined();
/// @dev The output is undefined, as the input is zero.
error Log2Undefined();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The scalar of ETH and most ERC20s.
uint256 internal constant WAD = 1e18;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* SIMPLIFIED FIXED POINT OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Equivalent to `(x * y) / WAD` rounded down.
function mulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
if mul(y, gt(x, div(not(0), y))) {
// Store the function selector of `MulWadFailed()`.
mstore(0x00, 0xbac65e5b)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
z := div(mul(x, y), WAD)
}
}
/// @dev Equivalent to `(x * y) / WAD` rounded up.
function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
if mul(y, gt(x, div(not(0), y))) {
// Store the function selector of `MulWadFailed()`.
mstore(0x00, 0xbac65e5b)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), WAD))
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded down.
function divWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `require(y != 0 && (WAD == 0 || x <= type(uint256).max / WAD))`.
if iszero(mul(y, iszero(mul(WAD, gt(x, div(not(0), WAD)))))) {
// Store the function selector of `DivWadFailed()`.
mstore(0x00, 0x7c5f487d)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
z := div(mul(x, WAD), y)
}
}
/// @dev Equivalent to `(x * WAD) / y` rounded up.
function divWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to `require(y != 0 && (WAD == 0 || x <= type(uint256).max / WAD))`.
if iszero(mul(y, iszero(mul(WAD, gt(x, div(not(0), WAD)))))) {
// Store the function selector of `DivWadFailed()`.
mstore(0x00, 0x7c5f487d)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
}
}
/// @dev Equivalent to `x` to the power of `y`.
/// because `x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)`.
function powWad(int256 x, int256 y) internal pure returns (int256) {
// Using `ln(x)` means `x` must be greater than 0.
return expWad((lnWad(x) * y) / int256(WAD));
}
/// @dev Returns `exp(x)`, denominated in `WAD`.
function expWad(int256 x) internal pure returns (int256 r) {
unchecked {
// When the result is < 0.5 we return zero. This happens when
// x <= floor(log(0.5e18) * 1e18) ~ -42e18
if (x <= -42139678854452767551) return r;
/// @solidity memory-safe-assembly
assembly {
// When the result is > (2**255 - 1) / 1e18 we can not represent it as an
// int. This happens when x >= floor(log((2**255 - 1) / 1e18) * 1e18) ~ 135.
if iszero(slt(x, 135305999368893231589)) {
// Store the function selector of `ExpOverflow()`.
mstore(0x00, 0xa37bfec9)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
}
// x is now in the range (-42, 136) * 1e18. Convert to (-42, 136) * 2**96
// for more intermediate precision and a binary basis. This base conversion
// is a multiplication by 1e18 / 2**96 = 5**18 / 2**78.
x = (x << 78) / 5 ** 18;
// Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers
// of two such that exp(x) = exp(x') * 2**k, where k is an integer.
// Solving this gives k = round(x / log(2)) and x' = x - k * log(2).
int256 k = ((x << 96) / 54916777467707473351141471128 + 2 ** 95) >> 96;
x = x - k * 54916777467707473351141471128;
// k is in the range [-61, 195].
// Evaluate using a (6, 7)-term rational approximation.
// p is made monic, we'll multiply by a scale factor later.
int256 y = x + 1346386616545796478920950773328;
y = ((y * x) >> 96) + 57155421227552351082224309758442;
int256 p = y + x - 94201549194550492254356042504812;
p = ((p * y) >> 96) + 28719021644029726153956944680412240;
p = p * x + (4385272521454847904659076985693276 << 96);
// We leave p in 2**192 basis so we don't need to scale it back up for the division.
int256 q = x - 2855989394907223263936484059900;
q = ((q * x) >> 96) + 50020603652535783019961831881945;
q = ((q * x) >> 96) - 533845033583426703283633433725380;
q = ((q * x) >> 96) + 3604857256930695427073651918091429;
q = ((q * x) >> 96) - 14423608567350463180887372962807573;
q = ((q * x) >> 96) + 26449188498355588339934803723976023;
/// @solidity memory-safe-assembly
assembly {
// Div in assembly because solidity adds a zero check despite the unchecked.
// The q polynomial won't have zeros in the domain as all its roots are complex.
// No scaling is necessary because p is already 2**96 too large.
r := sdiv(p, q)
}
// r should be in the range (0.09, 0.25) * 2**96.
// We now need to multiply r by:
// * the scale factor s = ~6.031367120.
// * the 2**k factor from the range reduction.
// * the 1e18 / 2**96 factor for base conversion.
// We do this all at once, with an intermediate result in 2**213
// basis, so the final right shift is always by a positive amount.
r = int256(
(uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k)
);
}
}
/// @dev Returns `ln(x)`, denominated in `WAD`.
function lnWad(int256 x) internal pure returns (int256 r) {
unchecked {
/// @solidity memory-safe-assembly
assembly {
if iszero(sgt(x, 0)) {
// Store the function selector of `LnWadUndefined()`.
mstore(0x00, 0x1615e638)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
}
// We want to convert x from 10**18 fixed point to 2**96 fixed point.
// We do this by multiplying by 2**96 / 10**18. But since
// ln(x * C) = ln(x) + ln(C), we can simply do nothing here
// and add ln(2**96 / 10**18) at the end.
// Compute k = log2(x) - 96.
int256 k;
/// @solidity memory-safe-assembly
assembly {
let v := x
k := shl(7, lt(0xffffffffffffffffffffffffffffffff, v))
k := or(k, shl(6, lt(0xffffffffffffffff, shr(k, v))))
k := or(k, shl(5, lt(0xffffffff, shr(k, v))))
// For the remaining 32 bits, use a De Bruijn lookup.
// See: https://graphics.stanford.edu/~seander/bithacks.html
v := shr(k, v)
v := or(v, shr(1, v))
v := or(v, shr(2, v))
v := or(v, shr(4, v))
v := or(v, shr(8, v))
v := or(v, shr(16, v))
// forgefmt: disable-next-item
k := sub(or(k, byte(shr(251, mul(v, shl(224, 0x07c4acdd))),
0x0009010a0d15021d0b0e10121619031e080c141c0f111807131b17061a05041f)), 96)
}
// Reduce range of x to (1, 2) * 2**96
// ln(2^k * x) = k * ln(2) + ln(x)
x <<= uint256(159 - k);
x = int256(uint256(x) >> 159);
// Evaluate using a (8, 8)-term rational approximation.
// p is made monic, we will multiply by a scale factor later.
int256 p = x + 3273285459638523848632254066296;
p = ((p * x) >> 96) + 24828157081833163892658089445524;
p = ((p * x) >> 96) + 43456485725739037958740375743393;
p = ((p * x) >> 96) - 11111509109440967052023855526967;
p = ((p * x) >> 96) - 45023709667254063763336534515857;
p = ((p * x) >> 96) - 14706773417378608786704636184526;
p = p * x - (795164235651350426258249787498 << 96);
// We leave p in 2**192 basis so we don't need to scale it back up for the division.
// q is monic by convention.
int256 q = x + 5573035233440673466300451813936;
q = ((q * x) >> 96) + 71694874799317883764090561454958;
q = ((q * x) >> 96) + 283447036172924575727196451306956;
q = ((q * x) >> 96) + 401686690394027663651624208769553;
q = ((q * x) >> 96) + 204048457590392012362485061816622;
q = ((q * x) >> 96) + 31853899698501571402653359427138;
q = ((q * x) >> 96) + 909429971244387300277376558375;
/// @solidity memory-safe-assembly
assembly {
// Div in assembly because solidity adds a zero check despite the unchecked.
// The q polynomial is known not to have zeros in the domain.
// No scaling required because p is already 2**96 too large.
r := sdiv(p, q)
}
// r is in the range (0, 0.125) * 2**96
// Finalization, we need to:
// * multiply by the scale factor s = 5.549…
// * add ln(2**96 / 10**18)
// * add k * ln(2)
// * multiply by 10**18 / 2**96 = 5**18 >> 78
// mul s * 5e18 * 2**96, base is now 5**18 * 2**192
r *= 1677202110996718588342820967067443963516166;
// add ln(2) * k * 5e18 * 2**192
r += 16597577552685614221487285958193947469193820559219878177908093499208371 * k;
// add ln(2**96 / 10**18) * 5e18 * 2**192
r += 600920179829731861736702779321621459595472258049074101567377883020018308;
// base conversion: mul 2**18 / 2**192
r >>= 174;
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* GENERAL NUMBER UTILITIES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Calculates `floor(a * b / d)` with full precision.
/// Throws if result overflows a uint256 or when `d` is zero.
/// Credit to Remco Bloemen under MIT license: https://2π.com/21/muldiv
function fullMulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
// forgefmt: disable-next-item
for {} 1 {} {
// 512-bit multiply `[prod1 prod0] = x * y`.
// Compute the product mod `2**256` and mod `2**256 - 1`
// then use the Chinese Remainder Theorem to reconstruct
// the 512 bit result. The result is stored in two 256
// variables such that `product = prod1 * 2**256 + prod0`.
// Least significant 256 bits of the product.
let prod0 := mul(x, y)
let mm := mulmod(x, y, not(0))
// Most significant 256 bits of the product.
let prod1 := sub(mm, add(prod0, lt(mm, prod0)))
// Handle non-overflow cases, 256 by 256 division.
if iszero(prod1) {
if iszero(d) {
// Store the function selector of `FullMulDivFailed()`.
mstore(0x00, 0xae47f702)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
result := div(prod0, d)
break
}
// Make sure the result is less than `2**256`.
// Also prevents `d == 0`.
if iszero(gt(d, prod1)) {
// Store the function selector of `FullMulDivFailed()`.
mstore(0x00, 0xae47f702)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from `[prod1 prod0]`.
// Compute remainder using mulmod.
let remainder := mulmod(x, y, d)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
// Factor powers of two out of `d`.
// Compute largest power of two divisor of `d`.
// Always greater or equal to 1.
let twos := and(d, sub(0, d))
// Divide d by power of two.
d := div(d, twos)
// Divide [prod1 prod0] by the factors of two.
prod0 := div(prod0, twos)
// Shift in bits from `prod1` into `prod0`. For this we need
// to flip `twos` such that it is `2**256 / twos`.
// If `twos` is zero, then it becomes one.
prod0 := or(prod0, mul(prod1, add(div(sub(0, twos), twos), 1)))
// Invert `d mod 2**256`
// Now that `d` is an odd number, it has an inverse
// modulo `2**256` such that `d * inv = 1 mod 2**256`.
// Compute the inverse by starting with a seed that is correct
// correct for four bits. That is, `d * inv = 1 mod 2**4`.
let inv := xor(mul(3, d), 2)
// Now use Newton-Raphson iteration to improve the precision.
// Thanks to Hensel's lifting lemma, this also works in modular
// arithmetic, doubling the correct bits in each step.
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**8
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**16
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**32
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**64
inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**128
result := mul(prod0, mul(inv, sub(2, mul(d, inv)))) // inverse mod 2**256
break
}
}
}
/// @dev Calculates `floor(x * y / d)` with full precision, rounded up.
/// Throws if result overflows a uint256 or when `d` is zero.
/// Credit to Uniswap-v3-core under MIT license:
/// https://github.com/Uniswap/v3-core/blob/contracts/libraries/FullMath.sol
function fullMulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 result) {
result = fullMulDiv(x, y, d);
/// @solidity memory-safe-assembly
assembly {
if mulmod(x, y, d) {
if iszero(add(result, 1)) {
// Store the function selector of `FullMulDivFailed()`.
mstore(0x00, 0xae47f702)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
result := add(result, 1)
}
}
}
/// @dev Returns `floor(x * y / d)`.
/// Reverts if `x * y` overflows, or `d` is zero.
function mulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to require(d != 0 && (y == 0 || x <= type(uint256).max / y))
if iszero(mul(d, iszero(mul(y, gt(x, div(not(0), y)))))) {
// Store the function selector of `MulDivFailed()`.
mstore(0x00, 0xad251c27)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
z := div(mul(x, y), d)
}
}
/// @dev Returns `ceil(x * y / d)`.
/// Reverts if `x * y` overflows, or `d` is zero.
function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to require(d != 0 && (y == 0 || x <= type(uint256).max / y))
if iszero(mul(d, iszero(mul(y, gt(x, div(not(0), y)))))) {
// Store the function selector of `MulDivFailed()`.
mstore(0x00, 0xad251c27)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
z := add(iszero(iszero(mod(mul(x, y), d))), div(mul(x, y), d))
}
}
/// @dev Returns `ceil(x / d)`.
/// Reverts if `d` is zero.
function divUp(uint256 x, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
if iszero(d) {
// Store the function selector of `DivFailed()`.
mstore(0x00, 0x65244e4e)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
z := add(iszero(iszero(mod(x, d))), div(x, d))
}
}
/// @dev Returns `max(0, x - y)`.
function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mul(gt(x, y), sub(x, y))
}
}
/// @dev Returns the square root of `x`.
function sqrt(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// `floor(sqrt(2**15)) = 181`. `sqrt(2**15) - 181 = 2.84`.
z := 181 // The "correct" value is 1, but this saves a multiplication later.
// This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
// start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
// Let `y = x / 2**r`.
// We check `y >= 2**(k + 8)` but shift right by `k` bits
// each branch to ensure that if `x >= 256`, then `y >= 256`.
let r := shl(7, lt(0xffffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffffff, shr(r, x))))
z := shl(shr(1, r), z)
// Goal was to get `z*z*y` within a small factor of `x`. More iterations could
// get y in a tighter range. Currently, we will have y in `[256, 256*(2**16))`.
// We ensured `y >= 256` so that the relative difference between `y` and `y+1` is small.
// That's not possible if `x < 256` but we can just verify those cases exhaustively.
// Now, `z*z*y <= x < z*z*(y+1)`, and `y <= 2**(16+8)`, and either `y >= 256`, or `x < 256`.
// Correctness can be checked exhaustively for `x < 256`, so we assume `y >= 256`.
// Then `z*sqrt(y)` is within `sqrt(257)/sqrt(256)` of `sqrt(x)`, or about 20bps.
// For `s` in the range `[1/256, 256]`, the estimate `f(s) = (181/1024) * (s+1)`
// is in the range `(1/2.84 * sqrt(s), 2.84 * sqrt(s))`,
// with largest error when `s = 1` and when `s = 256` or `1/256`.
// Since `y` is in `[256, 256*(2**16))`, let `a = y/65536`, so that `a` is in `[1/256, 256)`.
// Then we can estimate `sqrt(y)` using
// `sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2**18`.
// There is no overflow risk here since `y < 2**136` after the first branch above.
z := shr(18, mul(z, add(shr(r, x), 65536))) // A `mul()` is saved from starting `z` at 181.
// Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
// If `x+1` is a perfect square, the Babylonian method cycles between
// `floor(sqrt(x))` and `ceil(sqrt(x))`. This statement ensures we return floor.
// See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
// Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
// If you don't care whether the floor or ceil square root is returned, you can remove this statement.
z := sub(z, lt(div(x, z), z))
}
}
/// @dev Returns the cube root of `x`.
/// Credit to bout3fiddy and pcaversaccio under AGPLv3 license:
/// https://github.com/pcaversaccio/snekmate/blob/main/src/utils/Math.vy
function cbrt(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
let r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
z := shl(add(div(r, 3), lt(0xf, shr(r, x))), 0xff)
z := div(z, byte(mod(r, 3), shl(232, 0x7f624b)))
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := div(add(add(div(x, mul(z, z)), z), z), 3)
z := sub(z, lt(div(x, mul(z, z)), z))
}
}
/// @dev Returns the factorial of `x`.
function factorial(uint256 x) internal pure returns (uint256 result) {
/// @solidity memory-safe-assembly
assembly {
for {} 1 {} {
if iszero(lt(10, x)) {
// forgefmt: disable-next-item
result := and(
shr(mul(22, x), 0x375f0016260009d80004ec0002d00001e0000180000180000200000400001),
0x3fffff
)
break
}
if iszero(lt(57, x)) {
let end := 31
result := 8222838654177922817725562880000000
if iszero(lt(end, x)) {
end := 10
result := 3628800
}
for { let w := not(0) } 1 {} {
result := mul(result, x)
x := add(x, w)
if eq(x, end) { break }
}
break
}
// Store the function selector of `FactorialOverflow()`.
mstore(0x00, 0xaba0f2a2)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
}
}
/// @dev Returns the log2 of `x`.
/// Equivalent to computing the index of the most significant bit (MSB) of `x`.
function log2(uint256 x) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
if iszero(x) {
// Store the function selector of `Log2Undefined()`.
mstore(0x00, 0x5be3aa5c)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
// For the remaining 32 bits, use a De Bruijn lookup.
// See: https://graphics.stanford.edu/~seander/bithacks.html
x := shr(r, x)
x := or(x, shr(1, x))
x := or(x, shr(2, x))
x := or(x, shr(4, x))
x := or(x, shr(8, x))
x := or(x, shr(16, x))
// forgefmt: disable-next-item
r := or(r, byte(shr(251, mul(x, shl(224, 0x07c4acdd))),
0x0009010a0d15021d0b0e10121619031e080c141c0f111807131b17061a05041f))
}
}
/// @dev Returns the log2 of `x`, rounded up.
function log2Up(uint256 x) internal pure returns (uint256 r) {
unchecked {
uint256 isNotPo2;
assembly {
isNotPo2 := iszero(iszero(and(x, sub(x, 1))))
}
return log2(x) + isNotPo2;
}
}
/// @dev Returns the average of `x` and `y`.
function avg(uint256 x, uint256 y) internal pure returns (uint256 z) {
unchecked {
z = (x & y) + ((x ^ y) >> 1);
}
}
/// @dev Returns the average of `x` and `y`.
function avg(int256 x, int256 y) internal pure returns (int256 z) {
unchecked {
z = (x >> 1) + (y >> 1) + (((x & 1) + (y & 1)) >> 1);
}
}
/// @dev Returns the absolute value of `x`.
function abs(int256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
let mask := sub(0, shr(255, x))
z := xor(mask, add(mask, x))
}
}
/// @dev Returns the absolute distance between `x` and `y`.
function dist(int256 x, int256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
let a := sub(y, x)
z := xor(a, mul(xor(a, sub(x, y)), sgt(x, y)))
}
}
/// @dev Returns the minimum of `x` and `y`.
function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), lt(y, x)))
}
}
/// @dev Returns the minimum of `x` and `y`.
function min(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), slt(y, x)))
}
}
/// @dev Returns the maximum of `x` and `y`.
function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), gt(y, x)))
}
}
/// @dev Returns the maximum of `x` and `y`.
function max(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := xor(x, mul(xor(x, y), sgt(y, x)))
}
}
/// @dev Returns `x`, bounded to `minValue` and `maxValue`.
function clamp(uint256 x, uint256 minValue, uint256 maxValue)
internal
pure
returns (uint256 z)
{
z = min(max(x, minValue), maxValue);
}
/// @dev Returns `x`, bounded to `minValue` and `maxValue`.
function clamp(int256 x, int256 minValue, int256 maxValue) internal pure returns (int256 z) {
z = min(max(x, minValue), maxValue);
}
/// @dev Returns greatest common divisor of `x` and `y`.
function gcd(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// forgefmt: disable-next-item
for { z := x } y {} {
let t := y
y := mod(z, y)
z := t
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* RAW NUMBER OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns `x + y`, without checking for overflow.
function rawAdd(uint256 x, uint256 y) internal pure returns (uint256 z) {
unchecked {
z = x + y;
}
}
/// @dev Returns `x + y`, without checking for overflow.
function rawAdd(int256 x, int256 y) internal pure returns (int256 z) {
unchecked {
z = x + y;
}
}
/// @dev Returns `x - y`, without checking for underflow.
function rawSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
unchecked {
z = x - y;
}
}
/// @dev Returns `x - y`, without checking for underflow.
function rawSub(int256 x, int256 y) internal pure returns (int256 z) {
unchecked {
z = x - y;
}
}
/// @dev Returns `x * y`, without checking for overflow.
function rawMul(uint256 x, uint256 y) internal pure returns (uint256 z) {
unchecked {
z = x * y;
}
}
/// @dev Returns `x * y`, without checking for overflow.
function rawMul(int256 x, int256 y) internal pure returns (int256 z) {
unchecked {
z = x * y;
}
}
/// @dev Returns `x / y`, returning 0 if `y` is zero.
function rawDiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := div(x, y)
}
}
/// @dev Returns `x / y`, returning 0 if `y` is zero.
function rawSDiv(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := sdiv(x, y)
}
}
/// @dev Returns `x % y`, returning 0 if `y` is zero.
function rawMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mod(x, y)
}
}
/// @dev Returns `x % y`, returning 0 if `y` is zero.
function rawSMod(int256 x, int256 y) internal pure returns (int256 z) {
/// @solidity memory-safe-assembly
assembly {
z := smod(x, y)
}
}
/// @dev Returns `(x + y) % d`, return 0 if `d` if zero.
function rawAddMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := addmod(x, y, d)
}
}
/// @dev Returns `(x * y) % d`, return 0 if `d` if zero.
function rawMulMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
z := mulmod(x, y, d)
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165Upgradeable {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (interfaces/IERC2981.sol)
pragma solidity ^0.8.0;
import "../utils/introspection/IERC165Upgradeable.sol";
/**
* @dev Interface for the NFT Royalty Standard.
*
* A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
* support for royalty payments across all NFT marketplaces and ecosystem participants.
*
* _Available since v4.5._
*/
interface IERC2981Upgradeable is IERC165Upgradeable {
/**
* @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
* exchange. The royalty amount is denominated and should be paid in that same unit of exchange.
*/
function royaltyInfo(uint256 tokenId, uint256 salePrice)
external
view
returns (address receiver, uint256 royaltyAmount);
}
// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.2.3
// Creator: Chiru Labs
pragma solidity ^0.8.4;
/**
* @dev Interface of ERC721A.
*/
interface IERC721AUpgradeable {
/**
* 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();
// =============================================================
// 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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;
/**
* @title IMetadataModule
* @notice The interface for custom metadata modules.
*/
interface IMetadataModule {
/**
* @dev When implemented, SoundEdition's `tokenURI` redirects execution to this `tokenURI`.
* @param tokenId The token ID to retrieve the token URI for.
* @return The token URI string.
*/
function tokenURI(uint256 tokenId) external view returns (string memory);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;
import { IERC165 } from "openzeppelin/utils/introspection/IERC165.sol";
/**
* @title IMinterModuleV2_1
* @notice The interface for Sound protocol minter modules.
*/
interface IMinterModuleV2_1 is IERC165 {
// =============================================================
// STRUCTS
// =============================================================
struct BaseData {
// Auxillary variable for storing the price.
// May or may not be used.
uint96 price;
// Auxillary variable for storing the max amount mintable by an account.
// May or may not be used.
uint32 maxMintablePerAccount;
// The start unix timestamp of the mint.
uint32 startTime;
// The end unix timestamp of the mint.
uint32 endTime;
// The affiliate fee in basis points.
uint16 affiliateFeeBPS;
// Whether the mint is paused.
bool mintPaused;
// Whether the mint has been created.
bool created;
// The Merkle root of the affiliate allow list, if any.
bytes32 affiliateMerkleRoot;
}
// =============================================================
// EVENTS
// =============================================================
/**
* @dev Emitted when the mint instance for an `edition` is created.
* @param edition The edition address.
* @param mintId The mint ID, a global incrementing identifier used within the minter
* @param startTime The start time of the mint.
* @param endTime The end time of the mint.
* @param affiliateFeeBPS The affiliate fee in basis points.
*/
event MintConfigCreated(
address indexed edition,
address indexed creator,
uint128 mintId,
uint32 startTime,
uint32 endTime,
uint16 affiliateFeeBPS
);
/**
* @dev Emitted when the `paused` status of `edition` is updated.
* @param edition The edition address.
* @param mintId The mint ID, to distinguish between multiple mints for the same edition.
* @param paused The new paused status.
*/
event MintPausedSet(address indexed edition, uint128 mintId, bool paused);
/**
* @dev Emitted when the `paused` status of `edition` is updated.
* @param edition The edition address.
* @param mintId The mint ID, to distinguish between multiple mints for the same edition.
* @param startTime The start time of the mint.
* @param endTime The end time of the mint.
*/
event TimeRangeSet(address indexed edition, uint128 mintId, uint32 startTime, uint32 endTime);
/**
* @notice Emitted when the `affiliateFeeBPS` is updated.
* @param edition The edition address.
* @param mintId The mint ID, to distinguish between multiple mints for the same edition.
* @param bps The affiliate fee basis points.
*/
event AffiliateFeeSet(address indexed edition, uint128 mintId, uint16 bps);
/**
* @dev Emitted when the Merkle root for an affiliate allow list is set.
* @param edition The edition address.
* @param mintId The mint ID, to distinguish between multiple mints for the same edition.
* @param root The Merkle root for the affiliate allow list.
*/
event AffiliateMerkleRootSet(address indexed edition, uint128 mintId, bytes32 root);
/**
* @notice Emitted when a mint happens.
* @param edition The edition address.
* @param mintId The mint ID, to distinguish between multiple mints for
* the same edition.
* @param buyer The buyer address.
* @param fromTokenId The first token ID of the batch.
* @param quantity The size of the batch.
* @param requiredEtherValue Total amount of Ether required for payment.
* @param platformFee The cut paid to the platform.
* @param affiliateFee The cut paid to the affiliate.
* @param affiliate The affiliate's address.
* @param affiliated Whether the affiliate is affiliated.
* @param attributionId The attribution ID.
*/
event Minted(
address indexed edition,
uint128 mintId,
address indexed buyer,
uint32 fromTokenId,
uint32 quantity,
uint128 requiredEtherValue,
uint128 platformFee,
uint128 affiliateFee,
address affiliate,
bool affiliated,
uint256 indexed attributionId
);
/**
* @dev Emitted when the `platformFeeBPS` is updated.
* @param bps The platform fee basis points.
*/
event PlatformFeeSet(uint16 bps);
/**
* @dev Emitted when the `platformFlatFee` is updated.
* @param flatFee The amount of platform flat fee per token.
*/
event PlatformFlatFeeSet(uint96 flatFee);
/**
* @dev Emitted when the `platformPerTxFlatFee` is updated.
* @param perTxFlatFee The amount of platform flat fee per transaction.
*/
event PlatformPerTxFlatFeeSet(uint96 perTxFlatFee);
/**
* @dev Emitted when the `platformFeeAddress` is updated.
* @param addr The platform fee address.
*/
event PlatformFeeAddressSet(address addr);
/**
* @dev Emitted when the accrued fees for `affiliate` are withdrawn.
* @param affiliate The affiliate address.
* @param accrued The amount of fees withdrawn.
*/
event AffiliateFeesWithdrawn(address indexed affiliate, uint256 accrued);
/**
* @dev Emitted when the accrued fees for the platform are withdrawn.
* @param accrued The amount of fees withdrawn.
*/
event PlatformFeesWithdrawn(uint128 accrued);
// =============================================================
// ERRORS
// =============================================================
/**
* @dev The Ether value paid is below the value required.
* @param paid The amount sent to the contract.
* @param required The amount required to mint.
*/
error Underpaid(uint256 paid, uint256 required);
/**
* @dev The Ether value paid is not exact.
* @param paid The amount sent to the contract.
* @param required The amount required to mint.
*/
error WrongPayment(uint256 paid, uint256 required);
/**
* @dev The number minted has exceeded the max mintable amount.
* @param available The number of tokens remaining available for mint.
*/
error ExceedsAvailableSupply(uint32 available);
/**
* @dev The mint is not opened.
* @param blockTimestamp The current block timestamp.
* @param startTime The start time of the mint.
* @param endTime The end time of the mint.
*/
error MintNotOpen(uint256 blockTimestamp, uint32 startTime, uint32 endTime);
/**
* @dev The mint is paused.
*/
error MintPaused();
/**
* @dev The `startTime` is not less than the `endTime`.
*/
error InvalidTimeRange();
/**
* @dev The affiliate fee BPS must not exceed `MAX_AFFILIATE_FEE_BPS`.
*/
error InvalidAffiliateFeeBPS();
/**
* @dev The platform fee BPS must not exceed `MAX_PLATFORM_FEE_BPS`.
*/
error InvalidPlatformFeeBPS();
/**
* @dev The platform flat fee must not exceed `MAX_PLATFORM_FLAT_FEE`.
*/
error InvalidPlatformFlatFee();
/**
* @dev The platform per-transaction flat fee must not exceed `MAX_PLATFORM_PER_TX_FLAT_FEE`.
*/
error InvalidPlatformPerTxFlatFee();
/**
* @dev The platform fee address cannot be zero.
*/
error PlatformFeeAddressIsZero();
/**
* @dev The mint does not exist.
*/
error MintDoesNotExist();
/**
* @dev The `affiliate` provided is invalid for the given `affiliateProof`.
*/
error InvalidAffiliate();
// =============================================================
// PUBLIC / EXTERNAL WRITE FUNCTIONS
// =============================================================
/**
* @dev Sets the paused status for (`edition`, `mintId`).
*
* Calling conditions:
* - The caller must be the edition's owner or admin.
*/
function setEditionMintPaused(
address edition,
uint128 mintId,
bool paused
) external;
/**
* @dev Sets the time range for an edition mint.
*
* Calling conditions:
* - The caller must be the edition's owner or admin.
*
* @param edition The edition address.
* @param mintId The mint ID, a global incrementing identifier used within the minter
* @param startTime The start time of the mint.
* @param endTime The end time of the mint.
*/
function setTimeRange(
address edition,
uint128 mintId,
uint32 startTime,
uint32 endTime
) external;
/**
* @dev Sets the affiliate fee for (`edition`, `mintId`).
*
* Calling conditions:
* - The caller must be the edition's owner or admin.
*
* @param edition The edition address.
* @param mintId The mint ID, a global incrementing identifier used within the minter
* @param affiliateFeeBPS The affiliate fee in basis points.
*/
function setAffiliateFee(
address edition,
uint128 mintId,
uint16 affiliateFeeBPS
) external;
/**
* @dev Sets the affiliate Merkle root for (`edition`, `mintId`).
*
* Calling conditions:
* - The caller must be the edition's owner or admin.
*
* @param edition The edition address.
* @param mintId The mint ID, a global incrementing identifier used within the minter
* @param root The affiliate Merkle root, if any.
*/
function setAffiliateMerkleRoot(
address edition,
uint128 mintId,
bytes32 root
) external;
/**
* @dev Sets the platform fee bps.
*
* Calling conditions:
* - The caller must be the owner of the contract.
*
* @param bps The platform fee in basis points.
*/
function setPlatformFee(uint16 bps) external;
/**
* @dev Sets the platform flat fee.
*
* Calling conditions:
* - The caller must be the owner of the contract.
*
* @param flatFee The platform flat fee.
*/
function setPlatformFlatFee(uint96 flatFee) external;
/**
* @dev Sets the per-transaction platform flat fee.
*
* Calling conditions:
* - The caller must be the owner of the contract.
*
* @param perTxFlatFee The platform per transaction flat fee.
*/
function setPlatformPerTxFlatFee(uint96 perTxFlatFee) external;
/**
* @dev Sets the platform fee address.
*
* Calling conditions:
* - The caller must be the owner of the contract.
*
* @param addr The platform fee address.
*/
function setPlatformFeeAddress(address addr) external;
/**
* @dev Withdraws all the accrued fees for `affiliate`.
*/
function withdrawForAffiliate(address affiliate) external;
/**
* @dev Withdraws all the accrued fees for the platform.
*/
function withdrawForPlatform() external;
// =============================================================
// PUBLIC / EXTERNAL VIEW FUNCTIONS
// =============================================================
/**
* @dev This is the denominator, in basis points (BPS), for any of the fees.
* @return The constant value.
*/
function BPS_DENOMINATOR() external pure returns (uint16);
/**
* @dev The maximum basis points (BPS) limit allowed for the affiliate fees.
* @return The constant value.
*/
function MAX_AFFILIATE_FEE_BPS() external pure returns (uint16);
/**
* @dev The maximum basis points (BPS) limit allowed for the platform fees.
* @return The constant value.
*/
function MAX_PLATFORM_FEE_BPS() external pure returns (uint16);
/**
* @dev The maximum value for platform flat fee per NFT.
* @return The constant value.
*/
function MAX_PLATFORM_FLAT_FEE() external pure returns (uint96);
/**
* @dev The maximum value for platform flat fee per transaction.
* @return The constant value.
*/
function MAX_PLATFORM_PER_TX_FLAT_FEE() external pure returns (uint96);
/**
* @dev The total fees accrued for `affiliate`.
* @param affiliate The affiliate's address.
* @return The latest value.
*/
function affiliateFeesAccrued(address affiliate) external view returns (uint128);
/**
* @dev The total fees accrued for the platform.
* @return The latest value.
*/
function platformFeesAccrued() external view returns (uint128);
/**
* @dev Whether `affiliate` is affiliated for (`edition`, `mintId`).
* @param edition The edition's address.
* @param mintId The mint ID.
* @param affiliate The affiliate's address.
* @param affiliateProof The Merkle proof needed for verifying the affiliate, if any.
* @return The computed value.
*/
function isAffiliatedWithProof(
address edition,
uint128 mintId,
address affiliate,
bytes32[] calldata affiliateProof
) external view returns (bool);
/**
* @dev Whether `affiliate` is affiliated for (`edition`, `mintId`).
* @param edition The edition's address.
* @param mintId The mint ID.
* @param affiliate The affiliate's address.
* @return The computed value.
*/
function isAffiliated(
address edition,
uint128 mintId,
address affiliate
) external view returns (bool);
/**
* @dev Returns the affiliate Merkle root.
* @param edition The edition's address.
* @param mintId The mint ID.
* @return The latest value.
*/
function affiliateMerkleRoot(address edition, uint128 mintId) external view returns (bytes32);
/**
* @dev Returns the total price and fees for `quantity` tokens for (`edition`, `mintId`).
* @param edition The edition's address.
* @param mintId The mint ID.
* @param quantity The number of tokens to mint.
* @return total The total buy price, inclusive of any additional platform flat fees.
* @return subTotal The total buy price, exclusive of any additional platform flat fees.
* @return platformFlatFeeTotal The total platform flat fees, which is added onto `subTotal` to give `total`.
* @return platformFee The total platform fees.
* @return affiliateFee The affiliate fees.
*/
function totalPriceAndFees(
address edition,
uint128 mintId,
uint32 quantity
)
external
view
returns (
uint256 total,
uint256 subTotal,
uint256 platformFlatFeeTotal,
uint256 platformFee,
uint256 affiliateFee
);
/**
* @dev Returns the platform fee basis points.
* @return The configured value.
*/
function platformFeeBPS() external returns (uint16);
/**
* @dev Returns the platform flat fee per item.
* @return The configured value.
*/
function platformFlatFee() external returns (uint96);
/**
* @dev Returns the platform flat fee per transaction.
* @return The configured value.
*/
function platformPerTxFlatFee() external returns (uint96);
/**
* @dev Returns the platform fee address.
* @return The configured value.
*/
function platformFeeAddress() external returns (address);
/**
* @dev The next mint ID.
* A mint ID is assigned sequentially starting from (0, 1, 2, ...),
* and is shared amongst all editions connected to the minter contract.
* @return The latest value.
*/
function nextMintId() external view returns (uint128);
/**
* @dev The interface ID of the minter.
* @return The constant value.
*/
function moduleInterfaceId() external view returns (bytes4);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;
import { IMinterModuleV2_1 } from "@core/interfaces/IMinterModuleV2_1.sol";
/**
* @dev Data unique to a range edition mint.
*/
struct EditionMintData {
// The timestamp (in seconds since unix epoch) after which the
// max amount of tokens mintable will drop from
// `maxMintableUpper` to `maxMintableLower`.
uint32 cutoffTime;
// The total number of tokens minted. Includes permissioned mints.
uint32 totalMinted;
// The lower limit of the maximum number of tokens that can be minted.
uint32 maxMintableLower;
// The upper limit of the maximum number of tokens that can be minted.
uint32 maxMintableUpper;
}
/**
* @dev All the information about a range edition mint (combines EditionMintData with BaseData).
*/
struct MintInfo {
uint32 startTime;
uint32 endTime;
uint16 affiliateFeeBPS;
bool mintPaused;
uint96 price;
uint32 maxMintableUpper;
uint32 maxMintableLower;
uint32 maxMintablePerAccount;
uint32 totalMinted;
uint32 cutoffTime;
bytes32 affiliateMerkleRoot;
uint16 platformFeeBPS;
uint96 platformFlatFee;
uint96 platformPerTxFlatFee;
}
/**
* @title IRangeEditionMinterV2_1
* @dev Interface for the `RangeEditionMinterV2_1` module.
* @author Sound.xyz
*/
interface IRangeEditionMinterV2_1 is IMinterModuleV2_1 {
// =============================================================
// EVENTS
// =============================================================
/**
* @dev Emitted when a range edition is created.
* @param edition Address of the song edition contract we are minting for.
* @param mintId The mint ID.
* @param price Sale price in ETH for minting a single token in `edition`.
* @param startTime Start timestamp of sale (in seconds since unix epoch).
* @param cutoffTime The timestamp (in seconds since unix epoch) after which the
* max amount of tokens mintable will drop from
* `maxMintableUpper` to `maxMintableLower`.
* @param endTime End timestamp of sale (in seconds since unix epoch).
* @param affiliateFeeBPS The affiliate fee in basis points.
* @param maxMintableLower The lower limit of the maximum number of tokens that can be minted.
* @param maxMintableUpper The upper limit of the maximum number of tokens that can be minted.
*/
event RangeEditionMintCreated(
address indexed edition,
uint128 mintId,
uint96 price,
uint32 startTime,
uint32 cutoffTime,
uint32 endTime,
uint16 affiliateFeeBPS,
uint32 maxMintableLower,
uint32 maxMintableUpper,
uint32 maxMintablePerAccount
);
event CutoffTimeSet(address indexed edition, uint128 mintId, uint32 cutoffTime);
/**
* @dev Emitted when the max mintable range is updated.
* @param edition Address of the song edition contract we are minting for.
* @param mintId The mint ID.
* @param maxMintableLower The lower limit of the maximum number of tokens that can be minted.
* @param maxMintableUpper The upper limit of the maximum number of tokens that can be minted.
*/
event MaxMintableRangeSet(
address indexed edition,
uint128 mintId,
uint32 maxMintableLower,
uint32 maxMintableUpper
);
/**
* @dev Emitted when the `price` is changed for (`edition`, `mintId`).
* @param edition Address of the song edition contract we are minting for.
* @param mintId The mint ID.
* @param price Sale price in ETH for minting a single token in `edition`.
*/
event PriceSet(address indexed edition, uint128 mintId, uint96 price);
/**
* @dev Emitted when the `maxMintablePerAccount` is changed for (`edition`, `mintId`).
* @param edition Address of the song edition contract we are minting for.
* @param mintId The mint ID.
* @param maxMintablePerAccount The maximum number of tokens that can be minted per account.
*/
event MaxMintablePerAccountSet(address indexed edition, uint128 mintId, uint32 maxMintablePerAccount);
// =============================================================
// ERRORS
// =============================================================
/**
* @dev The `maxMintableLower` must not be greater than `maxMintableUpper`.
*/
error InvalidMaxMintableRange();
/**
* @dev The number of tokens minted has exceeded the number allowed for each account.
*/
error ExceedsMaxPerAccount();
/**
* @dev The max mintable per account cannot be zero.
*/
error MaxMintablePerAccountIsZero();
// =============================================================
// PUBLIC / EXTERNAL WRITE FUNCTIONS
// =============================================================
/**
* @dev Initializes a range mint instance
* @param edition Address of the song edition contract we are minting for.
* @param price Sale price in ETH for minting a single token in `edition`.
* @param startTime Start timestamp of sale (in seconds since unix epoch).
* @param cutoffTime The timestamp (in seconds since unix epoch) after which the
* max amount of tokens mintable will drop from
* `maxMintableUpper` to `maxMintableLower`.
* @param endTime End timestamp of sale (in seconds since unix epoch).
* @param affiliateFeeBPS The affiliate fee in basis points.
* @param maxMintableLower The lower limit of the maximum number of tokens that can be minted.
* @param maxMintableUpper The upper limit of the maximum number of tokens that can be minted.
* @param maxMintablePerAccount_ The maximum number of tokens that can be minted by an account.
* @return mintId The ID for the new mint instance.
*/
function createEditionMint(
address edition,
uint96 price,
uint32 startTime,
uint32 cutoffTime,
uint32 endTime,
uint16 affiliateFeeBPS,
uint32 maxMintableLower,
uint32 maxMintableUpper,
uint32 maxMintablePerAccount_
) external returns (uint128 mintId);
/**
* @dev Sets the time range.
* @param edition Address of the song edition contract we are minting for.
* @param startTime Start timestamp of sale (in seconds since unix epoch).
* @param cutoffTime The timestamp (in seconds since unix epoch) after which the
* max amount of tokens mintable will drop from
* `maxMintableUpper` to `maxMintableLower`.
* @param endTime End timestamp of sale (in seconds since unix epoch).
*/
function setTimeRange(
address edition,
uint128 mintId,
uint32 startTime,
uint32 cutoffTime,
uint32 endTime
) external;
/**
* @dev Sets the max mintable range.
* @param edition Address of the song edition contract we are minting for.
* @param maxMintableLower The lower limit of the maximum number of tokens that can be minted.
* @param maxMintableUpper The upper limit of the maximum number of tokens that can be minted.
*/
function setMaxMintableRange(
address edition,
uint128 mintId,
uint32 maxMintableLower,
uint32 maxMintableUpper
) external;
/**
* @dev Mints tokens for a given edition.
* @param edition Address of the song edition contract we are minting for.
* @param mintId The mint ID.
* @param to The address to mint to.
* @param quantity Token quantity to mint in song `edition`.
* @param affiliate The affiliate address.
* @param affiliateProof The Merkle proof needed for verifying the affiliate, if any.
* @param attributionId The attribution ID.
*/
function mintTo(
address edition,
uint128 mintId,
address to,
uint32 quantity,
address affiliate,
bytes32[] calldata affiliateProof,
uint256 attributionId
) external payable;
/**
* @dev Mints tokens for a given edition.
* @param edition Address of the song edition contract we are minting for.
* @param mintId The mint ID.
* @param quantity Token quantity to mint in song `edition`.
* @param affiliate The affiliate address.
*/
function mint(
address edition,
uint128 mintId,
uint32 quantity,
address affiliate
) external payable;
/**
* @dev Sets the `price` for (`edition`, `mintId`).
* @param edition Address of the song edition contract we are minting for.
* @param mintId The mint ID.
* @param price Sale price in ETH for minting a single token in `edition`.
*/
function setPrice(
address edition,
uint128 mintId,
uint96 price
) external;
/**
* @dev Sets the `maxMintablePerAccount` for (`edition`, `mintId`).
* @param edition Address of the song edition contract we are minting for.
* @param mintId The mint ID.
* @param maxMintablePerAccount The maximum number of tokens that can be minted by an account.
*/
function setMaxMintablePerAccount(
address edition,
uint128 mintId,
uint32 maxMintablePerAccount
) external;
// =============================================================
// PUBLIC / EXTERNAL VIEW FUNCTIONS
// =============================================================
/**
* @dev Returns {IRangeEditionMinterV2_1.MintInfo} instance containing the full minter parameter set.
* @param edition The edition to get the mint instance for.
* @param mintId The ID of the mint instance.
* @return mintInfo Information about this mint.
*/
function mintInfo(address edition, uint128 mintId) external view returns (MintInfo memory);
/**
* @dev To prevent ERC165 selector collision.
*/
function isV2_1() external pure returns (bool);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;
import { IERC721AUpgradeable } from "chiru-labs/ERC721A-Upgradeable/IERC721AUpgradeable.sol";
import { IERC2981Upgradeable } from "openzeppelin-upgradeable/interfaces/IERC2981Upgradeable.sol";
import { IERC165Upgradeable } from "openzeppelin-upgradeable/utils/introspection/IERC165Upgradeable.sol";
import { IMetadataModule } from "./IMetadataModule.sol";
/**
* @dev The information pertaining to this edition.
*/
struct EditionInfo {
// Base URI for the tokenId.
string baseURI;
// Contract URI for OpenSea storefront.
string contractURI;
// Name of the collection.
string name;
// Symbol of the collection.
string symbol;
// Address that receives primary and secondary royalties.
address fundingRecipient;
// The current max mintable amount;
uint32 editionMaxMintable;
// The lower limit of the maximum number of tokens that can be minted.
uint32 editionMaxMintableUpper;
// The upper limit of the maximum number of tokens that can be minted.
uint32 editionMaxMintableLower;
// The timestamp (in seconds since unix epoch) after which the
// max amount of tokens mintable will drop from
// `maxMintableUpper` to `maxMintableLower`.
uint32 editionCutoffTime;
// Address of metadata module, address(0x00) if not used.
address metadataModule;
// The current mint randomness value.
uint256 mintRandomness;
// The royalty BPS (basis points).
uint16 royaltyBPS;
// Whether the mint randomness is enabled.
bool mintRandomnessEnabled;
// Whether the mint has concluded.
bool mintConcluded;
// Whether the metadata has been frozen.
bool isMetadataFrozen;
// Next token ID to be minted.
uint256 nextTokenId;
// Total number of tokens burned.
uint256 totalBurned;
// Total number of tokens minted.
uint256 totalMinted;
// Total number of tokens currently in existence.
uint256 totalSupply;
}
/**
* @title ISoundEditionV1
* @notice The interface for Sound edition contracts.
*/
interface ISoundEditionV1 is IERC721AUpgradeable, IERC2981Upgradeable {
// =============================================================
// EVENTS
// =============================================================
/**
* @dev Emitted when the metadata module is set.
* @param metadataModule the address of the metadata module.
*/
event MetadataModuleSet(address metadataModule);
/**
* @dev Emitted when the `baseURI` is set.
* @param baseURI the base URI of the edition.
*/
event BaseURISet(string baseURI);
/**
* @dev Emitted when the `contractURI` is set.
* @param contractURI The contract URI of the edition.
*/
event ContractURISet(string contractURI);
/**
* @dev Emitted when the metadata is frozen (e.g.: `baseURI` can no longer be changed).
* @param metadataModule The address of the metadata module.
* @param baseURI The base URI of the edition.
* @param contractURI The contract URI of the edition.
*/
event MetadataFrozen(address metadataModule, string baseURI, string contractURI);
/**
* @dev Emitted when the `fundingRecipient` is set.
* @param fundingRecipient The address of the funding recipient.
*/
event FundingRecipientSet(address fundingRecipient);
/**
* @dev Emitted when the `royaltyBPS` is set.
* @param bps The new royalty, measured in basis points.
*/
event RoyaltySet(uint16 bps);
/**
* @dev Emitted when the edition's maximum mintable token quantity range is set.
* @param editionMaxMintableLower_ The lower limit of the maximum number of tokens that can be minted.
* @param editionMaxMintableUpper_ The upper limit of the maximum number of tokens that can be minted.
*/
event EditionMaxMintableRangeSet(uint32 editionMaxMintableLower_, uint32 editionMaxMintableUpper_);
/**
* @dev Emitted when the edition's cutoff time set.
* @param editionCutoffTime_ The timestamp.
*/
event EditionCutoffTimeSet(uint32 editionCutoffTime_);
/**
* @dev Emitted when the `mintRandomnessEnabled` is set.
* @param mintRandomnessEnabled_ The boolean value.
*/
event MintRandomnessEnabledSet(bool mintRandomnessEnabled_);
/**
* @dev Emitted upon initialization.
* @param edition_ The address of the edition.
* @param name_ Name of the collection.
* @param symbol_ Symbol of the collection.
* @param metadataModule_ Address of metadata module, address(0x00) if not used.
* @param baseURI_ Base URI.
* @param contractURI_ Contract URI for OpenSea storefront.
* @param fundingRecipient_ Address that receives primary and secondary royalties.
* @param royaltyBPS_ Royalty amount in bps (basis points).
* @param editionMaxMintableLower_ The lower bound of the max mintable quantity for the edition.
* @param editionMaxMintableUpper_ The upper bound of the max mintable quantity for the edition.
* @param editionCutoffTime_ The timestamp after which `editionMaxMintable` drops from
* `editionMaxMintableUpper` to
* `max(_totalMinted(), editionMaxMintableLower)`.
* @param flags_ The bitwise OR result of the initialization flags.
* See: {METADATA_IS_FROZEN_FLAG}
* See: {MINT_RANDOMNESS_ENABLED_FLAG}
*/
event SoundEditionInitialized(
address indexed edition_,
string name_,
string symbol_,
address metadataModule_,
string baseURI_,
string contractURI_,
address fundingRecipient_,
uint16 royaltyBPS_,
uint32 editionMaxMintableLower_,
uint32 editionMaxMintableUpper_,
uint32 editionCutoffTime_,
uint8 flags_
);
/**
* @dev Emitted upon ETH withdrawal.
* @param recipient The recipient of the withdrawal.
* @param amount The amount withdrawn.
* @param caller The account that initiated the withdrawal.
*/
event ETHWithdrawn(address recipient, uint256 amount, address caller);
/**
* @dev Emitted upon ERC20 withdrawal.
* @param recipient The recipient of the withdrawal.
* @param tokens The addresses of the ERC20 tokens.
* @param amounts The amount of each token withdrawn.
* @param caller The account that initiated the withdrawal.
*/
event ERC20Withdrawn(address recipient, address[] tokens, uint256[] amounts, address caller);
/**
* @dev Emitted upon a mint.
* @param to The address to mint to.
* @param quantity The number of minted.
* @param fromTokenId The first token ID minted.
*/
event Minted(address to, uint256 quantity, uint256 fromTokenId);
/**
* @dev Emitted upon an airdrop.
* @param to The recipients of the airdrop.
* @param quantity The number of tokens airdropped to each address in `to`.
* @param fromTokenId The first token ID minted to the first address in `to`.
*/
event Airdropped(address[] to, uint256 quantity, uint256 fromTokenId);
// =============================================================
// ERRORS
// =============================================================
/**
* @dev The edition's metadata is frozen (e.g.: `baseURI` can no longer be changed).
*/
error MetadataIsFrozen();
/**
* @dev The given `royaltyBPS` is invalid.
*/
error InvalidRoyaltyBPS();
/**
* @dev The given `randomnessLockedAfterMinted` value is invalid.
*/
error InvalidRandomnessLock();
/**
* @dev The requested quantity exceeds the edition's remaining mintable token quantity.
* @param available The number of tokens remaining available for mint.
*/
error ExceedsEditionAvailableSupply(uint32 available);
/**
* @dev The given amount is invalid.
*/
error InvalidAmount();
/**
* @dev The given `fundingRecipient` address is invalid.
*/
error InvalidFundingRecipient();
/**
* @dev The `editionMaxMintableLower` must not be greater than `editionMaxMintableUpper`.
*/
error InvalidEditionMaxMintableRange();
/**
* @dev The `editionMaxMintable` has already been reached.
*/
error MaximumHasAlreadyBeenReached();
/**
* @dev The mint `quantity` cannot exceed `ADDRESS_BATCH_MINT_LIMIT` tokens.
*/
error ExceedsAddressBatchMintLimit();
/**
* @dev The mint randomness has already been revealed.
*/
error MintRandomnessAlreadyRevealed();
/**
* @dev No addresses to airdrop.
*/
error NoAddressesToAirdrop();
/**
* @dev The mint has already concluded.
*/
error MintHasConcluded();
/**
* @dev Cannot perform the operation after a token has been minted.
*/
error MintsAlreadyExist();
// =============================================================
// PUBLIC / EXTERNAL WRITE FUNCTIONS
// =============================================================
/**
* @dev Initializes the contract.
* @param name_ Name of the collection.
* @param symbol_ Symbol of the collection.
* @param metadataModule_ Address of metadata module, address(0x00) if not used.
* @param baseURI_ Base URI.
* @param contractURI_ Contract URI for OpenSea storefront.
* @param fundingRecipient_ Address that receives primary and secondary royalties.
* @param royaltyBPS_ Royalty amount in bps (basis points).
* @param editionMaxMintableLower_ The lower bound of the max mintable quantity for the edition.
* @param editionMaxMintableUpper_ The upper bound of the max mintable quantity for the edition.
* @param editionCutoffTime_ The timestamp after which `editionMaxMintable` drops from
* `editionMaxMintableUpper` to
* `max(_totalMinted(), editionMaxMintableLower)`.
* @param flags_ The bitwise OR result of the initialization flags.
* See: {METADATA_IS_FROZEN_FLAG}
* See: {MINT_RANDOMNESS_ENABLED_FLAG}
*/
function initialize(
string memory name_,
string memory symbol_,
address metadataModule_,
string memory baseURI_,
string memory contractURI_,
address fundingRecipient_,
uint16 royaltyBPS_,
uint32 editionMaxMintableLower_,
uint32 editionMaxMintableUpper_,
uint32 editionCutoffTime_,
uint8 flags_
) external;
/**
* @dev Mints `quantity` tokens to addrress `to`
* Each token will be assigned a token ID that is consecutively increasing.
*
* Calling conditions:
* - The caller must be the owner of the contract, or have either the
* `ADMIN_ROLE`, `MINTER_ROLE`, which can be granted via {grantRole}.
* Multiple minters, such as different minter contracts,
* can be authorized simultaneously.
*
* @param to Address to mint to.
* @param quantity Number of tokens to mint.
* @return fromTokenId The first token ID minted.
*/
function mint(address to, uint256 quantity) external payable returns (uint256 fromTokenId);
/**
* @dev Mints `quantity` tokens to each of the addresses in `to`.
*
* Calling conditions:
* - The caller must be the owner of the contract, or have the
* `ADMIN_ROLE`, which can be granted via {grantRole}.
*
* @param to Address to mint to.
* @param quantity Number of tokens to mint.
* @return fromTokenId The first token ID minted.
*/
function airdrop(address[] calldata to, uint256 quantity) external returns (uint256 fromTokenId);
/**
* @dev Withdraws collected ETH royalties to the fundingRecipient.
*/
function withdrawETH() external;
/**
* @dev Withdraws collected ERC20 royalties to the fundingRecipient.
* @param tokens array of ERC20 tokens to withdraw
*/
function withdrawERC20(address[] calldata tokens) external;
/**
* @dev Sets metadata module.
*
* Calling conditions:
* - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
*
* @param metadataModule Address of metadata module.
*/
function setMetadataModule(address metadataModule) external;
/**
* @dev Sets global base URI.
*
* Calling conditions:
* - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
*
* @param baseURI The base URI to be set.
*/
function setBaseURI(string memory baseURI) external;
/**
* @dev Sets contract URI.
*
* Calling conditions:
* - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
*
* @param contractURI The contract URI to be set.
*/
function setContractURI(string memory contractURI) external;
/**
* @dev Freezes metadata by preventing any more changes to base URI.
*
* Calling conditions:
* - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
*/
function freezeMetadata() external;
/**
* @dev Sets funding recipient address.
*
* Calling conditions:
* - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
*
* @param fundingRecipient Address to be set as the new funding recipient.
*/
function setFundingRecipient(address fundingRecipient) external;
/**
* @dev Sets royalty amount in bps (basis points).
*
* Calling conditions:
* - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
*
* @param bps The new royalty basis points to be set.
*/
function setRoyalty(uint16 bps) external;
/**
* @dev Sets the edition max mintable range.
*
* Calling conditions:
* - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
*
* @param editionMaxMintableLower_ The lower limit of the maximum number of tokens that can be minted.
* @param editionMaxMintableUpper_ The upper limit of the maximum number of tokens that can be minted.
*/
function setEditionMaxMintableRange(uint32 editionMaxMintableLower_, uint32 editionMaxMintableUpper_) external;
/**
* @dev Sets the timestamp after which, the `editionMaxMintable` drops
* from `editionMaxMintableUpper` to `editionMaxMintableLower.
*
* Calling conditions:
* - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
*
* @param editionCutoffTime_ The timestamp.
*/
function setEditionCutoffTime(uint32 editionCutoffTime_) external;
/**
* @dev Sets whether the `mintRandomness` is enabled.
*
* Calling conditions:
* - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
*
* @param mintRandomnessEnabled_ The boolean value.
*/
function setMintRandomnessEnabled(bool mintRandomnessEnabled_) external;
// =============================================================
// PUBLIC / EXTERNAL VIEW FUNCTIONS
// =============================================================
/**
* @dev Returns the edition info.
* @return editionInfo The latest value.
*/
function editionInfo() external view returns (EditionInfo memory editionInfo);
/**
* @dev Returns the minter role flag.
* @return The constant value.
*/
function MINTER_ROLE() external view returns (uint256);
/**
* @dev Returns the admin role flag.
* @return The constant value.
*/
function ADMIN_ROLE() external view returns (uint256);
/**
* @dev Returns the maximum limit for the mint or airdrop `quantity`.
* Prevents the first-time transfer costs for tokens near the end of large mint batches
* via ERC721A from becoming too expensive due to the need to scan many storage slots.
* See: https://chiru-labs.github.io/ERC721A/#/tips?id=batch-size
* @return The constant value.
*/
function ADDRESS_BATCH_MINT_LIMIT() external pure returns (uint256);
/**
* @dev Returns the bit flag to freeze the metadata on initialization.
* @return The constant value.
*/
function METADATA_IS_FROZEN_FLAG() external pure returns (uint8);
/**
* @dev Returns the bit flag to enable the mint randomness feature on initialization.
* @return The constant value.
*/
function MINT_RANDOMNESS_ENABLED_FLAG() external pure returns (uint8);
/**
* @dev Returns the base token URI for the collection.
* @return The configured value.
*/
function baseURI() external view returns (string memory);
/**
* @dev Returns the contract URI to be used by Opensea.
* See: https://docs.opensea.io/docs/contract-level-metadata
* @return The configured value.
*/
function contractURI() external view returns (string memory);
/**
* @dev Returns the address of the funding recipient.
* @return The configured value.
*/
function fundingRecipient() external view returns (address);
/**
* @dev Returns the maximum amount of tokens mintable for this edition.
* @return The configured value.
*/
function editionMaxMintable() external view returns (uint32);
/**
* @dev Returns the upper bound for the maximum tokens that can be minted for this edition.
* @return The configured value.
*/
function editionMaxMintableUpper() external view returns (uint32);
/**
* @dev Returns the lower bound for the maximum tokens that can be minted for this edition.
* @return The configured value.
*/
function editionMaxMintableLower() external view returns (uint32);
/**
* @dev Returns the timestamp after which `editionMaxMintable` drops from
* `editionMaxMintableUpper` to `editionMaxMintableLower`.
* @return The configured value.
*/
function editionCutoffTime() external view returns (uint32);
/**
* @dev Returns the address of the metadata module.
* @return The configured value.
*/
function metadataModule() external view returns (address);
/**
* @dev Returns the randomness based on latest block hash, which is stored upon each mint.
* unless {mintConcluded} is true.
* Used for game mechanics like the Sound Golden Egg.
* Returns 0 before revealed.
* WARNING: This value should NOT be used for any reward of significant monetary
* value, due to it being computed via a purely on-chain psuedorandom mechanism.
* @return The latest value.
*/
function mintRandomness() external view returns (uint256);
/**
* @dev Returns whether the `mintRandomness` has been enabled.
* @return The configured value.
*/
function mintRandomnessEnabled() external view returns (bool);
/**
* @dev Returns whether the mint has been concluded.
* @return The latest value.
*/
function mintConcluded() external view returns (bool);
/**
* @dev Returns the royalty basis points.
* @return The configured value.
*/
function royaltyBPS() external view returns (uint16);
/**
* @dev Returns whether the metadata module is frozen.
* @return The configured value.
*/
function isMetadataFrozen() external view returns (bool);
/**
* @dev Returns the next token ID to be minted.
* @return The latest value.
*/
function nextTokenId() external view returns (uint256);
/**
* @dev Returns the number of tokens minted by `owner`.
* @param owner Address to query for number minted.
* @return The latest value.
*/
function numberMinted(address owner) external view returns (uint256);
/**
* @dev Returns the number of tokens burned by `owner`.
* @param owner Address to query for number burned.
* @return The latest value.
*/
function numberBurned(address owner) external view returns (uint256);
/**
* @dev Returns the total amount of tokens minted.
* @return The latest value.
*/
function totalMinted() external view returns (uint256);
/**
* @dev Returns the total amount of tokens burned.
* @return The latest value.
*/
function totalBurned() external view returns (uint256);
/**
* @dev Informs other contracts which interfaces this contract supports.
* Required by https://eips.ethereum.org/EIPS/eip-165
* @param interfaceId The interface id to check.
* @return Whether the `interfaceId` is supported.
*/
function supportsInterface(bytes4 interfaceId)
external
view
override(IERC721AUpgradeable, IERC165Upgradeable)
returns (bool);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;
import { IERC721AUpgradeable } from "chiru-labs/ERC721A-Upgradeable/IERC721AUpgradeable.sol";
import { IERC2981Upgradeable } from "openzeppelin-upgradeable/interfaces/IERC2981Upgradeable.sol";
import { IERC165Upgradeable } from "openzeppelin-upgradeable/utils/introspection/IERC165Upgradeable.sol";
import { IMetadataModule } from "./IMetadataModule.sol";
/**
* @dev The information pertaining to this edition.
*/
struct EditionInfo {
// Base URI for the tokenId.
string baseURI;
// Contract URI for OpenSea storefront.
string contractURI;
// Name of the collection.
string name;
// Symbol of the collection.
string symbol;
// Address that receives primary and secondary royalties.
address fundingRecipient;
// The current max mintable amount;
uint32 editionMaxMintable;
// The lower limit of the maximum number of tokens that can be minted.
uint32 editionMaxMintableUpper;
// The upper limit of the maximum number of tokens that can be minted.
uint32 editionMaxMintableLower;
// The timestamp (in seconds since unix epoch) after which the
// max amount of tokens mintable will drop from
// `maxMintableUpper` to `maxMintableLower`.
uint32 editionCutoffTime;
// Address of metadata module, address(0x00) if not used.
address metadataModule;
// The current mint randomness value.
uint256 mintRandomness;
// The royalty BPS (basis points).
uint16 royaltyBPS;
// Whether the mint randomness is enabled.
bool mintRandomnessEnabled;
// Whether the mint has concluded.
bool mintConcluded;
// Whether the metadata has been frozen.
bool isMetadataFrozen;
// Next token ID to be minted.
uint256 nextTokenId;
// Total number of tokens burned.
uint256 totalBurned;
// Total number of tokens minted.
uint256 totalMinted;
// Total number of tokens currently in existence.
uint256 totalSupply;
}
/**
* @title ISoundEditionV1_2
* @notice The interface for Sound edition contracts.
*/
interface ISoundEditionV1_2 is IERC721AUpgradeable, IERC2981Upgradeable {
// =============================================================
// EVENTS
// =============================================================
/**
* @dev Emitted when the metadata module is set.
* @param metadataModule the address of the metadata module.
*/
event MetadataModuleSet(address metadataModule);
/**
* @dev Emitted when the `baseURI` is set.
* @param baseURI the base URI of the edition.
*/
event BaseURISet(string baseURI);
/**
* @dev Emitted when the `contractURI` is set.
* @param contractURI The contract URI of the edition.
*/
event ContractURISet(string contractURI);
/**
* @dev Emitted when the metadata is frozen (e.g.: `baseURI` can no longer be changed).
* @param metadataModule The address of the metadata module.
* @param baseURI The base URI of the edition.
* @param contractURI The contract URI of the edition.
*/
event MetadataFrozen(address metadataModule, string baseURI, string contractURI);
/**
* @dev Emitted when the `fundingRecipient` is set.
* @param fundingRecipient The address of the funding recipient.
*/
event FundingRecipientSet(address fundingRecipient);
/**
* @dev Emitted when the `royaltyBPS` is set.
* @param bps The new royalty, measured in basis points.
*/
event RoyaltySet(uint16 bps);
/**
* @dev Emitted when the edition's maximum mintable token quantity range is set.
* @param editionMaxMintableLower_ The lower limit of the maximum number of tokens that can be minted.
* @param editionMaxMintableUpper_ The upper limit of the maximum number of tokens that can be minted.
*/
event EditionMaxMintableRangeSet(uint32 editionMaxMintableLower_, uint32 editionMaxMintableUpper_);
/**
* @dev Emitted when the edition's cutoff time set.
* @param editionCutoffTime_ The timestamp.
*/
event EditionCutoffTimeSet(uint32 editionCutoffTime_);
/**
* @dev Emitted when the `mintRandomnessEnabled` is set.
* @param mintRandomnessEnabled_ The boolean value.
*/
event MintRandomnessEnabledSet(bool mintRandomnessEnabled_);
/**
* @dev Emitted when the `operatorFilteringEnabled` is set.
* @param operatorFilteringEnabled_ The boolean value.
*/
event OperatorFilteringEnablededSet(bool operatorFilteringEnabled_);
/**
* @dev Emitted upon initialization.
* @param edition_ The address of the edition.
* @param name_ Name of the collection.
* @param symbol_ Symbol of the collection.
* @param metadataModule_ Address of metadata module, address(0x00) if not used.
* @param baseURI_ Base URI.
* @param contractURI_ Contract URI for OpenSea storefront.
* @param fundingRecipient_ Address that receives primary and secondary royalties.
* @param royaltyBPS_ Royalty amount in bps (basis points).
* @param editionMaxMintableLower_ The lower bound of the max mintable quantity for the edition.
* @param editionMaxMintableUpper_ The upper bound of the max mintable quantity for the edition.
* @param editionCutoffTime_ The timestamp after which `editionMaxMintable` drops from
* `editionMaxMintableUpper` to
* `max(_totalMinted(), editionMaxMintableLower)`.
* @param flags_ The bitwise OR result of the initialization flags.
* See: {METADATA_IS_FROZEN_FLAG}
* See: {MINT_RANDOMNESS_ENABLED_FLAG}
*/
event SoundEditionInitialized(
address indexed edition_,
string name_,
string symbol_,
address metadataModule_,
string baseURI_,
string contractURI_,
address fundingRecipient_,
uint16 royaltyBPS_,
uint32 editionMaxMintableLower_,
uint32 editionMaxMintableUpper_,
uint32 editionCutoffTime_,
uint8 flags_
);
/**
* @dev Emitted upon ETH withdrawal.
* @param recipient The recipient of the withdrawal.
* @param amount The amount withdrawn.
* @param caller The account that initiated the withdrawal.
*/
event ETHWithdrawn(address recipient, uint256 amount, address caller);
/**
* @dev Emitted upon ERC20 withdrawal.
* @param recipient The recipient of the withdrawal.
* @param tokens The addresses of the ERC20 tokens.
* @param amounts The amount of each token withdrawn.
* @param caller The account that initiated the withdrawal.
*/
event ERC20Withdrawn(address recipient, address[] tokens, uint256[] amounts, address caller);
/**
* @dev Emitted upon a mint.
* @param to The address to mint to.
* @param quantity The number of minted.
* @param fromTokenId The first token ID minted.
*/
event Minted(address to, uint256 quantity, uint256 fromTokenId);
/**
* @dev Emitted upon an airdrop.
* @param to The recipients of the airdrop.
* @param quantity The number of tokens airdropped to each address in `to`.
* @param fromTokenId The first token ID minted to the first address in `to`.
*/
event Airdropped(address[] to, uint256 quantity, uint256 fromTokenId);
/**
* @dev EIP-4906 event to signal marketplaces to refresh the metadata.
* @param fromTokenId The starting token ID.
* @param toTokenId The ending token ID.
*/
event BatchMetadataUpdate(uint256 fromTokenId, uint256 toTokenId);
/**
* @dev Emiited when the Sound Automated Market (i.e. bonding curve minter) is set.
* @param sam_ The Sound Automated Market.
*/
event SAMSet(address sam_);
// =============================================================
// ERRORS
// =============================================================
/**
* @dev The edition's metadata is frozen (e.g.: `baseURI` can no longer be changed).
*/
error MetadataIsFrozen();
/**
* @dev The given `royaltyBPS` is invalid.
*/
error InvalidRoyaltyBPS();
/**
* @dev The given `randomnessLockedAfterMinted` value is invalid.
*/
error InvalidRandomnessLock();
/**
* @dev The requested quantity exceeds the edition's remaining mintable token quantity.
* @param available The number of tokens remaining available for mint.
*/
error ExceedsEditionAvailableSupply(uint32 available);
/**
* @dev The given amount is invalid.
*/
error InvalidAmount();
/**
* @dev The given `fundingRecipient` address is invalid.
*/
error InvalidFundingRecipient();
/**
* @dev The `editionMaxMintableLower` must not be greater than `editionMaxMintableUpper`.
*/
error InvalidEditionMaxMintableRange();
/**
* @dev The `editionMaxMintable` has already been reached.
*/
error MaximumHasAlreadyBeenReached();
/**
* @dev The mint `quantity` cannot exceed `ADDRESS_BATCH_MINT_LIMIT` tokens.
*/
error ExceedsAddressBatchMintLimit();
/**
* @dev The mint randomness has already been revealed.
*/
error MintRandomnessAlreadyRevealed();
/**
* @dev No addresses to airdrop.
*/
error NoAddressesToAirdrop();
/**
* @dev The mint has already concluded.
*/
error MintHasConcluded();
/**
* @dev The mint has not concluded.
*/
error MintNotConcluded();
/**
* @dev Cannot perform the operation after a token has been minted.
*/
error MintsAlreadyExist();
/**
* @dev The token IDs must be in strictly ascending order.
*/
error TokenIdsNotStrictlyAscending();
/**
* @dev Please wait for a while before you burn.
*/
error CannotBurnImmediately();
// =============================================================
// PUBLIC / EXTERNAL WRITE FUNCTIONS
// =============================================================
/**
* @dev Initializes the contract.
* @param name_ Name of the collection.
* @param symbol_ Symbol of the collection.
* @param metadataModule_ Address of metadata module, address(0x00) if not used.
* @param baseURI_ Base URI.
* @param contractURI_ Contract URI for OpenSea storefront.
* @param fundingRecipient_ Address that receives primary and secondary royalties.
* @param royaltyBPS_ Royalty amount in bps (basis points).
* @param editionMaxMintableLower_ The lower bound of the max mintable quantity for the edition.
* @param editionMaxMintableUpper_ The upper bound of the max mintable quantity for the edition.
* @param editionCutoffTime_ The timestamp after which `editionMaxMintable` drops from
* `editionMaxMintableUpper` to
* `max(_totalMinted(), editionMaxMintableLower)`.
* @param flags_ The bitwise OR result of the initialization flags.
* See: {METADATA_IS_FROZEN_FLAG}
* See: {MINT_RANDOMNESS_ENABLED_FLAG}
*/
function initialize(
string memory name_,
string memory symbol_,
address metadataModule_,
string memory baseURI_,
string memory contractURI_,
address fundingRecipient_,
uint16 royaltyBPS_,
uint32 editionMaxMintableLower_,
uint32 editionMaxMintableUpper_,
uint32 editionCutoffTime_,
uint8 flags_
) external;
/**
* @dev Mints `quantity` tokens to addrress `to`
* Each token will be assigned a token ID that is consecutively increasing.
*
* Calling conditions:
* - The caller must be the owner of the contract, or have either the
* `ADMIN_ROLE`, `MINTER_ROLE`, which can be granted via {grantRole}.
* Multiple minters, such as different minter contracts,
* can be authorized simultaneously.
*
* @param to Address to mint to.
* @param quantity Number of tokens to mint.
* @return fromTokenId The first token ID minted.
*/
function mint(address to, uint256 quantity) external payable returns (uint256 fromTokenId);
/**
* @dev Mints `quantity` tokens to each of the addresses in `to`.
*
* Calling conditions:
* - The caller must be the owner of the contract, or have the
* `ADMIN_ROLE`, which can be granted via {grantRole}.
*
* @param to Address to mint to.
* @param quantity Number of tokens to mint.
* @return fromTokenId The first token ID minted.
*/
function airdrop(address[] calldata to, uint256 quantity) external returns (uint256 fromTokenId);
/**
* @dev Mints `quantity` tokens to addrress `to`
* Each token will be assigned a token ID that is consecutively increasing.
*
* Calling conditions:
* - The caller must be the bonding curve contract.
*
* @param to Address to mint to.
* @param quantity Number of tokens to mint.
* @return fromTokenId The first token ID minted.
*/
function samMint(address to, uint256 quantity) external payable returns (uint256 fromTokenId);
/**
* @dev Burns the `tokenIds`.
*
* Calling conditions:
* - The caller must be the bonding curve contract.
*
* @param burner The initiator of the burn.
* @param tokenIds The list of token IDs to burn.
*/
function samBurn(address burner, uint256[] calldata tokenIds) external;
/**
* @dev Withdraws collected ETH royalties to the fundingRecipient.
*/
function withdrawETH() external;
/**
* @dev Withdraws collected ERC20 royalties to the fundingRecipient.
* @param tokens array of ERC20 tokens to withdraw
*/
function withdrawERC20(address[] calldata tokens) external;
/**
* @dev Sets metadata module.
*
* Calling conditions:
* - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
*
* @param metadataModule Address of metadata module.
*/
function setMetadataModule(address metadataModule) external;
/**
* @dev Sets global base URI.
*
* Calling conditions:
* - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
*
* @param baseURI The base URI to be set.
*/
function setBaseURI(string memory baseURI) external;
/**
* @dev Sets contract URI.
*
* Calling conditions:
* - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
*
* @param contractURI The contract URI to be set.
*/
function setContractURI(string memory contractURI) external;
/**
* @dev Freezes metadata by preventing any more changes to base URI.
*
* Calling conditions:
* - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
*/
function freezeMetadata() external;
/**
* @dev Sets funding recipient address.
*
* Calling conditions:
* - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
*
* @param fundingRecipient Address to be set as the new funding recipient.
*/
function setFundingRecipient(address fundingRecipient) external;
/**
* @dev Sets royalty amount in bps (basis points).
*
* Calling conditions:
* - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
*
* @param bps The new royalty basis points to be set.
*/
function setRoyalty(uint16 bps) external;
/**
* @dev Sets the edition max mintable range.
*
* Calling conditions:
* - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
*
* @param editionMaxMintableLower_ The lower limit of the maximum number of tokens that can be minted.
* @param editionMaxMintableUpper_ The upper limit of the maximum number of tokens that can be minted.
*/
function setEditionMaxMintableRange(uint32 editionMaxMintableLower_, uint32 editionMaxMintableUpper_) external;
/**
* @dev Sets the timestamp after which, the `editionMaxMintable` drops
* from `editionMaxMintableUpper` to `editionMaxMintableLower.
*
* Calling conditions:
* - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
*
* @param editionCutoffTime_ The timestamp.
*/
function setEditionCutoffTime(uint32 editionCutoffTime_) external;
/**
* @dev Sets whether the `mintRandomness` is enabled.
*
* Calling conditions:
* - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
*
* @param mintRandomnessEnabled_ The boolean value.
*/
function setMintRandomnessEnabled(bool mintRandomnessEnabled_) external;
/**
* @dev Sets whether OpenSea operator filtering is enabled.
*
* Calling conditions:
* - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
*
* @param operatorFilteringEnabled_ The boolean value.
*/
function setOperatorFilteringEnabled(bool operatorFilteringEnabled_) external;
/**
* @dev Emits an event to signal to marketplaces to refresh all the metadata.
*/
function emitAllMetadataUpdate() external;
/**
* @dev Sets the Sound Automated Market (i.e. bonding curve minter).
*
* Calling conditions:
* - The caller must be the owner of the contract, or have the `ADMIN_ROLE`.
*
* @param sam_ The Sound Automated Market.
*/
function setSAM(address sam_) external;
// =============================================================
// PUBLIC / EXTERNAL VIEW FUNCTIONS
// =============================================================
/**
* @dev Returns the edition info.
* @return editionInfo The latest value.
*/
function editionInfo() external view returns (EditionInfo memory editionInfo);
/**
* @dev Returns the minter role flag.
* @return The constant value.
*/
function MINTER_ROLE() external view returns (uint256);
/**
* @dev Returns the admin role flag.
* @return The constant value.
*/
function ADMIN_ROLE() external view returns (uint256);
/**
* @dev Returns the bit flag to freeze the metadata on initialization.
* @return The constant value.
*/
function METADATA_IS_FROZEN_FLAG() external pure returns (uint8);
/**
* @dev Returns the bit flag to enable the mint randomness feature on initialization.
* @return The constant value.
*/
function MINT_RANDOMNESS_ENABLED_FLAG() external pure returns (uint8);
/**
* @dev Returns the bit flag to enable OpenSea operator filtering.
* @return The constant value.
*/
function OPERATOR_FILTERING_ENABLED_FLAG() external pure returns (uint8);
/**
* @dev Returns the base token URI for the collection.
* @return The configured value.
*/
function baseURI() external view returns (string memory);
/**
* @dev Returns the contract URI to be used by Opensea.
* See: https://docs.opensea.io/docs/contract-level-metadata
* @return The configured value.
*/
function contractURI() external view returns (string memory);
/**
* @dev Returns the address of the funding recipient.
* @return The configured value.
*/
function fundingRecipient() external view returns (address);
/**
* @dev Returns the maximum amount of tokens mintable for this edition.
* @return The configured value.
*/
function editionMaxMintable() external view returns (uint32);
/**
* @dev Returns the upper bound for the maximum tokens that can be minted for this edition.
* @return The configured value.
*/
function editionMaxMintableUpper() external view returns (uint32);
/**
* @dev Returns the lower bound for the maximum tokens that can be minted for this edition.
* @return The configured value.
*/
function editionMaxMintableLower() external view returns (uint32);
/**
* @dev Returns the timestamp after which `editionMaxMintable` drops from
* `editionMaxMintableUpper` to `editionMaxMintableLower`.
* @return The configured value.
*/
function editionCutoffTime() external view returns (uint32);
/**
* @dev Returns the address of the metadata module.
* @return The configured value.
*/
function metadataModule() external view returns (address);
/**
* @dev Returns the randomness based on latest block hash, which is stored upon each mint.
* unless {mintConcluded} is true.
* Used for game mechanics like the Sound Golden Egg.
* Returns 0 before revealed.
* WARNING: This value should NOT be used for any reward of significant monetary
* value, due to it being computed via a purely on-chain psuedorandom mechanism.
* @return The latest value.
*/
function mintRandomness() external view returns (uint256);
/**
* @dev Returns whether the `mintRandomness` has been enabled.
* @return The configured value.
*/
function mintRandomnessEnabled() external view returns (bool);
/**
* @dev Returns whether the `operatorFilteringEnabled` has been enabled.
* @return The configured value.
*/
function operatorFilteringEnabled() external view returns (bool);
/**
* @dev Returns whether the mint has been concluded.
* @return The latest value.
*/
function mintConcluded() external view returns (bool);
/**
* @dev Returns the royalty basis points.
* @return The configured value.
*/
function royaltyBPS() external view returns (uint16);
/**
* @dev Returns whether the metadata module is frozen.
* @return The configured value.
*/
function isMetadataFrozen() external view returns (bool);
/**
* @dev Returns the sound automated market, if any.
* @return The configured value.
*/
function sam() external view returns (address);
/**
* @dev Returns the next token ID to be minted.
* @return The latest value.
*/
function nextTokenId() external view returns (uint256);
/**
* @dev Returns the number of tokens minted by `owner`.
* @param owner Address to query for number minted.
* @return The latest value.
*/
function numberMinted(address owner) external view returns (uint256);
/**
* @dev Returns the number of tokens burned by `owner`.
* @param owner Address to query for number burned.
* @return The latest value.
*/
function numberBurned(address owner) external view returns (uint256);
/**
* @dev Returns the total amount of tokens minted.
* @return The latest value.
*/
function totalMinted() external view returns (uint256);
/**
* @dev Returns the total amount of tokens burned.
* @return The latest value.
*/
function totalBurned() external view returns (uint256);
/**
* @dev Informs other contracts which interfaces this contract supports.
* Required by https://eips.ethereum.org/EIPS/eip-165
* @param interfaceId The interface id to check.
* @return Whether the `interfaceId` is supported.
*/
function supportsInterface(bytes4 interfaceId)
external
view
override(IERC721AUpgradeable, IERC165Upgradeable)
returns (bool);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/**
* @title LibMulticaller
* @author vectorized.eth
* @notice Library to read the `msg.sender` of the multicaller with sender contract.
*/
library LibMulticaller {
/**
* @dev The address of the multicaller contract.
*/
address internal constant MULTICALLER = 0x000000000088228fCF7b8af41Faf3955bD0B3A41;
/**
* @dev The address of the multicaller with sender contract.
*/
address internal constant MULTICALLER_WITH_SENDER = 0x00000000002Fd5Aeb385D324B580FCa7c83823A0;
/**
* @dev Returns the caller of `aggregateWithSender` on `MULTICALLER_WITH_SENDER`.
*/
function multicallerSender() internal view returns (address result) {
/// @solidity memory-safe-assembly
assembly {
if iszero(
staticcall(
gas(), // Remaining gas.
MULTICALLER_WITH_SENDER, // The multicaller.
0x00, // Start of calldata in memory.
0x00, // Length of calldata.
0x00, // Start of returndata in memory.
0x20 // Length of returndata.
)
) { revert(0, 0) } // For better gas estimation.
result := mul(mload(0x00), eq(returndatasize(), 0x20))
}
}
/**
* @dev Returns the caller of `aggregateWithSender` on `MULTICALLER_WITH_SENDER`,
* if the current context's `msg.sender` is `MULTICALLER_WITH_SENDER`.
* Otherwise, returns `msg.sender`.
*/
function sender() internal view returns (address result) {
/// @solidity memory-safe-assembly
assembly {
result := caller()
if eq(result, MULTICALLER_WITH_SENDER) {
if iszero(
staticcall(
gas(), // Remaining gas.
MULTICALLER_WITH_SENDER, // The multicaller with sender.
0x00, // Start of calldata in memory.
0x00, // Length of calldata.
0x00, // Start of returndata in memory.
0x20 // Length of returndata.
)
) { revert(0, 0) } // For better gas estimation.
result := mul(mload(0x00), eq(returndatasize(), 0x20))
}
}
}
}
// 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 `leafs` exist in the Merkle tree with `root`,
/// given `proof` and `flags`.
function verifyMultiProof(
bytes32[] memory proof,
bytes32 root,
bytes32[] memory leafs,
bool[] memory flags
) internal pure returns (bool isValid) {
// Rebuilds the root by consuming and producing values on a queue.
// The queue starts with the `leafs` 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 leafsLength := mload(leafs)
let proofLength := mload(proof)
let flagsLength := mload(flags)
// Advance the pointers of the arrays to point to the data.
leafs := add(0x20, leafs)
proof := add(0x20, proof)
flags := add(0x20, flags)
// If the number of flags is correct.
for {} eq(add(leafsLength, proofLength), add(flagsLength, 1)) {} {
// For the case where `proof.length + leafs.length == 1`.
if iszero(flagsLength) {
// `isValid = (proof.length == 1 ? proof[0] : leafs[0]) == root`.
isValid := eq(mload(xor(leafs, mul(xor(proof, leafs), proofLength))), root)
break
}
// 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 leafs into the hashes.
// Sometimes, a little memory expansion costs less than branching.
// Should cost less, even with a high free memory offset of 0x7d00.
// Left shift by 5 is equivalent to multiplying by 0x20.
leafsLength := shl(5, leafsLength)
for { let i := 0 } iszero(eq(i, leafsLength)) { i := add(i, 0x20) } {
mstore(add(hashesFront, i), mload(add(leafs, i)))
}
// Compute the back of the hashes.
let hashesBack := add(hashesFront, leafsLength)
// This is the end of the memory for the queue.
// We recycle `flagsLength` to save on stack variables
// (this trick may not always 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 }
}
// Checks if the last value in the queue is same as the root.
isValid := eq(mload(sub(hashesBack, 0x20)), root)
break
}
}
}
/// @dev Returns whether all `leafs` exist in the Merkle tree with `root`,
/// given `proof` and `flags`.
function verifyMultiProofCalldata(
bytes32[] calldata proof,
bytes32 root,
bytes32[] calldata leafs,
bool[] calldata flags
) internal pure returns (bool isValid) {
// Rebuilds the root by consuming and producing values on a queue.
// The queue starts with the `leafs` 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(leafs.length, proof.length), add(flags.length, 1)) {} {
// For the case where `proof.length + leafs.length == 1`.
if iszero(flags.length) {
// `isValid = (proof.length == 1 ? proof[0] : leafs[0]) == root`.
// forgefmt: disable-next-item
isValid := eq(
calldataload(
xor(leafs.offset, mul(xor(proof.offset, leafs.offset), proof.length))
),
root
)
break
}
// 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 leafs into the hashes.
// Sometimes, a little memory expansion costs less than branching.
// Should cost less, even with a high free memory offset of 0x7d00.
// Left shift by 5 is equivalent to multiplying by 0x20.
calldatacopy(hashesFront, leafs.offset, shl(5, leafs.length))
// Compute the back of the hashes.
let hashesBack := add(hashesFront, shl(5, leafs.length))
// This is the end of the memory for the queue.
// We recycle `flags.length` to save on stack variables
// (this trick may not always 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 }
}
// Checks if the last value in the queue is same as the root.
isValid := eq(mload(sub(hashesBack, 0x20)), root)
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 emptyLeafs() internal pure returns (bytes32[] calldata leafs) {
/// @solidity memory-safe-assembly
assembly {
leafs.length := 0
}
}
/// @dev Returns an empty calldata bool array.
function emptyFlags() internal pure returns (bool[] calldata flags) {
/// @solidity memory-safe-assembly
assembly {
flags.length := 0
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
/// @dev While the ownable portion follows [EIP-173](https://eips.ethereum.org/EIPS/eip-173)
/// for compatibility, the nomenclature for the 2-step ownership handover
/// may be unique to this codebase.
abstract contract Ownable {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The caller is not authorized to call the function.
error Unauthorized();
/// @dev The `newOwner` cannot be the zero address.
error NewOwnerIsZeroAddress();
/// @dev The `pendingOwner` does not have a valid handover request.
error NoHandoverRequest();
/// @dev `bytes4(keccak256(bytes("Unauthorized()")))`.
uint256 private constant _UNAUTHORIZED_ERROR_SELECTOR = 0x82b42900;
/// @dev `bytes4(keccak256(bytes("NewOwnerIsZeroAddress()")))`.
uint256 private constant _NEW_OWNER_IS_ZERO_ADDRESS_ERROR_SELECTOR = 0x7448fbae;
/// @dev `bytes4(keccak256(bytes("NoHandoverRequest()")))`.
uint256 private constant _NO_HANDOVER_REQUEST_ERROR_SELECTOR = 0x6f5e8818;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ownership is transferred from `oldOwner` to `newOwner`.
/// This event is intentionally kept the same as OpenZeppelin's Ownable to be
/// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
/// despite it not being as lightweight as a single argument event.
event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);
/// @dev An ownership handover to `pendingOwner` has been requested.
event OwnershipHandoverRequested(address indexed pendingOwner);
/// @dev The ownership handover to `pendingOwner` has been canceled.
event OwnershipHandoverCanceled(address indexed pendingOwner);
/// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
/// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
/// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The owner slot is given by: `not(_OWNER_SLOT_NOT)`.
/// It is intentionally choosen to be a high value
/// to avoid collision with lower slots.
/// The choice of manual storage layout is to enable compatibility
/// with both regular and upgradeable contracts.
uint256 private constant _OWNER_SLOT_NOT = 0x8b78c6d8;
/// The ownership handover slot of `newOwner` is given by:
/// ```
/// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
/// let handoverSlot := keccak256(0x00, 0x20)
/// ```
/// It stores the expiry timestamp of the two-step ownership handover.
uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Initializes the owner directly without authorization guard.
/// This function must be called upon initialization,
/// regardless of whether the contract is upgradeable or not.
/// This is to enable generalization to both regular and upgradeable contracts,
/// and to save gas in case the initial owner is not the caller.
/// For performance reasons, this function will not check if there
/// is an existing owner.
function _initializeOwner(address newOwner) internal virtual {
/// @solidity memory-safe-assembly
assembly {
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Store the new value.
sstore(not(_OWNER_SLOT_NOT), newOwner)
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
}
/// @dev Sets the owner directly without authorization guard.
function _setOwner(address newOwner) internal virtual {
/// @solidity memory-safe-assembly
assembly {
let ownerSlot := not(_OWNER_SLOT_NOT)
// Clean the upper 96 bits.
newOwner := shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.
log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.
sstore(ownerSlot, newOwner)
}
}
/// @dev Throws if the sender is not the owner.
function _checkOwner() internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// If the caller is not the stored owner, revert.
if iszero(eq(caller(), sload(not(_OWNER_SLOT_NOT)))) {
mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR)
revert(0x1c, 0x04)
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC UPDATE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Allows the owner to transfer the ownership to `newOwner`.
function transferOwnership(address newOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
if iszero(shl(96, newOwner)) {
mstore(0x00, _NEW_OWNER_IS_ZERO_ADDRESS_ERROR_SELECTOR)
revert(0x1c, 0x04)
}
}
_setOwner(newOwner);
}
/// @dev Allows the owner to renounce their ownership.
function renounceOwnership() public payable virtual onlyOwner {
_setOwner(address(0));
}
/// @dev Request a two-step ownership handover to the caller.
/// The request will be automatically expire in 48 hours (172800 seconds) by default.
function requestOwnershipHandover() public payable virtual {
unchecked {
uint256 expires = block.timestamp + ownershipHandoverValidFor();
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to `expires`.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), expires)
// Emit the {OwnershipHandoverRequested} event.
log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
}
}
}
/// @dev Cancels the two-step ownership handover to the caller, if any.
function cancelOwnershipHandover() public payable virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), 0)
// Emit the {OwnershipHandoverCanceled} event.
log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
}
}
/// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
/// Reverts if there is no existing ownership handover requested by `pendingOwner`.
function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
/// @solidity memory-safe-assembly
assembly {
// Compute and set the handover slot to 0.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
let handoverSlot := keccak256(0x0c, 0x20)
// If the handover does not exist, or has expired.
if gt(timestamp(), sload(handoverSlot)) {
mstore(0x00, _NO_HANDOVER_REQUEST_ERROR_SELECTOR)
revert(0x1c, 0x04)
}
// Set the handover slot to 0.
sstore(handoverSlot, 0)
}
_setOwner(pendingOwner);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC READ FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns the owner of the contract.
function owner() public view virtual returns (address result) {
/// @solidity memory-safe-assembly
assembly {
result := sload(not(_OWNER_SLOT_NOT))
}
}
/// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
function ownershipHandoverExpiresAt(address pendingOwner)
public
view
virtual
returns (uint256 result)
{
/// @solidity memory-safe-assembly
assembly {
// Compute the handover slot.
mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
// Load the handover slot.
result := sload(keccak256(0x0c, 0x20))
}
}
/// @dev Returns how long a two-step ownership handover is valid for in seconds.
function ownershipHandoverValidFor() public view virtual returns (uint64) {
return 48 * 3600;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MODIFIERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Marks a function as only callable by the owner.
modifier onlyOwner() virtual {
_checkOwner();
_;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "./Ownable.sol";
/// @notice Simple single owner and multiroles authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
/// @dev While the ownable portion follows [EIP-173](https://eips.ethereum.org/EIPS/eip-173)
/// for compatibility, the nomenclature for the 2-step ownership handover and roles
/// may be unique to this codebase.
abstract contract OwnableRoles is Ownable {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev `bytes4(keccak256(bytes("Unauthorized()")))`.
uint256 private constant _UNAUTHORIZED_ERROR_SELECTOR = 0x82b42900;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* EVENTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The `user`'s roles is updated to `roles`.
/// Each bit of `roles` represents whether the role is set.
event RolesUpdated(address indexed user, uint256 indexed roles);
/// @dev `keccak256(bytes("RolesUpdated(address,uint256)"))`.
uint256 private constant _ROLES_UPDATED_EVENT_SIGNATURE =
0x715ad5ce61fc9595c7b415289d59cf203f23a94fa06f04af7e489a0a76e1fe26;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* STORAGE */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The role slot of `user` is given by:
/// ```
/// mstore(0x00, or(shl(96, user), _ROLE_SLOT_SEED))
/// let roleSlot := keccak256(0x00, 0x20)
/// ```
/// This automatically ignores the upper bits of the `user` in case
/// they are not clean, as well as keep the `keccak256` under 32-bytes.
///
/// Note: This is equal to `_OWNER_SLOT_NOT` in for gas efficiency.
uint256 private constant _ROLE_SLOT_SEED = 0x8b78c6d8;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Grants the roles directly without authorization guard.
/// Each bit of `roles` represents the role to turn on.
function _grantRoles(address user, uint256 roles) internal virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute the role slot.
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, user)
let roleSlot := keccak256(0x0c, 0x20)
// Load the current value and `or` it with `roles`.
roles := or(sload(roleSlot), roles)
// Store the new value.
sstore(roleSlot, roles)
// Emit the {RolesUpdated} event.
log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), roles)
}
}
/// @dev Removes the roles directly without authorization guard.
/// Each bit of `roles` represents the role to turn off.
function _removeRoles(address user, uint256 roles) internal virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute the role slot.
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, user)
let roleSlot := keccak256(0x0c, 0x20)
// Load the current value.
let currentRoles := sload(roleSlot)
// Use `and` to compute the intersection of `currentRoles` and `roles`,
// `xor` it with `currentRoles` to flip the bits in the intersection.
roles := xor(currentRoles, and(currentRoles, roles))
// Then, store the new value.
sstore(roleSlot, roles)
// Emit the {RolesUpdated} event.
log3(0, 0, _ROLES_UPDATED_EVENT_SIGNATURE, shr(96, mload(0x0c)), roles)
}
}
/// @dev Throws if the sender does not have any of the `roles`.
function _checkRoles(uint256 roles) internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute the role slot.
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, caller())
// Load the stored value, and if the `and` intersection
// of the value and `roles` is zero, revert.
if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR)
revert(0x1c, 0x04)
}
}
}
/// @dev Throws if the sender is not the owner,
/// and does not have any of the `roles`.
/// Checks for ownership first, then lazily checks for roles.
function _checkOwnerOrRoles(uint256 roles) internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// If the caller is not the stored owner.
// Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`.
if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) {
// Compute the role slot.
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, caller())
// Load the stored value, and if the `and` intersection
// of the value and `roles` is zero, revert.
if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR)
revert(0x1c, 0x04)
}
}
}
}
/// @dev Throws if the sender does not have any of the `roles`,
/// and is not the owner.
/// Checks for roles first, then lazily checks for ownership.
function _checkRolesOrOwner(uint256 roles) internal view virtual {
/// @solidity memory-safe-assembly
assembly {
// Compute the role slot.
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, caller())
// Load the stored value, and if the `and` intersection
// of the value and `roles` is zero, revert.
if iszero(and(sload(keccak256(0x0c, 0x20)), roles)) {
// If the caller is not the stored owner.
// Note: `_ROLE_SLOT_SEED` is equal to `_OWNER_SLOT_NOT`.
if iszero(eq(caller(), sload(not(_ROLE_SLOT_SEED)))) {
mstore(0x00, _UNAUTHORIZED_ERROR_SELECTOR)
revert(0x1c, 0x04)
}
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC UPDATE FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Allows the owner to grant `user` `roles`.
/// If the `user` already has a role, then it will be an no-op for the role.
function grantRoles(address user, uint256 roles) public payable virtual onlyOwner {
_grantRoles(user, roles);
}
/// @dev Allows the owner to remove `user` `roles`.
/// If the `user` does not have a role, then it will be an no-op for the role.
function revokeRoles(address user, uint256 roles) public payable virtual onlyOwner {
_removeRoles(user, roles);
}
/// @dev Allow the caller to remove their own roles.
/// If the caller does not have a role, then it will be an no-op for the role.
function renounceRoles(uint256 roles) public payable virtual {
_removeRoles(msg.sender, roles);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PUBLIC READ FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns whether `user` has any of `roles`.
function hasAnyRole(address user, uint256 roles) public view virtual returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
// Compute the role slot.
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, user)
// Load the stored value, and set the result to whether the
// `and` intersection of the value and `roles` is not zero.
result := iszero(iszero(and(sload(keccak256(0x0c, 0x20)), roles)))
}
}
/// @dev Returns whether `user` has all of `roles`.
function hasAllRoles(address user, uint256 roles) public view virtual returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
// Compute the role slot.
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, user)
// Whether the stored value is contains all the set bits in `roles`.
result := eq(and(sload(keccak256(0x0c, 0x20)), roles), roles)
}
}
/// @dev Returns the roles of `user`.
function rolesOf(address user) public view virtual returns (uint256 roles) {
/// @solidity memory-safe-assembly
assembly {
// Compute the role slot.
mstore(0x0c, _ROLE_SLOT_SEED)
mstore(0x00, user)
// Load the stored value.
roles := sload(keccak256(0x0c, 0x20))
}
}
/// @dev Convenience function to return a `roles` bitmap from an array of `ordinals`.
/// This is meant for frontends like Etherscan, and is therefore not fully optimized.
/// Not recommended to be called on-chain.
function rolesFromOrdinals(uint8[] memory ordinals) public pure returns (uint256 roles) {
/// @solidity memory-safe-assembly
assembly {
for { let i := shl(5, mload(ordinals)) } i { i := sub(i, 0x20) } {
// We don't need to mask the values of `ordinals`, as Solidity
// cleans dirty upper bits when storing variables into memory.
roles := or(shl(mload(add(ordinals, i)), 1), roles)
}
}
}
/// @dev Convenience function to return an array of `ordinals` from the `roles` bitmap.
/// This is meant for frontends like Etherscan, and is therefore not fully optimized.
/// Not recommended to be called on-chain.
function ordinalsFromRoles(uint256 roles) public pure returns (uint8[] memory ordinals) {
/// @solidity memory-safe-assembly
assembly {
// Grab the pointer to the free memory.
ordinals := mload(0x40)
let ptr := add(ordinals, 0x20)
let o := 0
// The absence of lookup tables, De Bruijn, etc., here is intentional for
// smaller bytecode, as this function is not meant to be called on-chain.
for { let t := roles } 1 {} {
mstore(ptr, o)
// `shr` 5 is equivalent to multiplying by 0x20.
// Push back into the ordinals array if the bit is set.
ptr := add(ptr, shl(5, and(t, 1)))
o := add(o, 1)
t := shr(o, roles)
if iszero(t) { break }
}
// Store the length of `ordinals`.
mstore(ordinals, shr(5, sub(ptr, add(ordinals, 0x20))))
// Allocate the memory.
mstore(0x40, ptr)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MODIFIERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Marks a function as only callable by an account with `roles`.
modifier onlyRoles(uint256 roles) virtual {
_checkRoles(roles);
_;
}
/// @dev Marks a function as only callable by the owner or by an account
/// with `roles`. Checks for ownership first, then lazily checks for roles.
modifier onlyOwnerOrRoles(uint256 roles) virtual {
_checkOwnerOrRoles(roles);
_;
}
/// @dev Marks a function as only callable by an account with `roles`
/// or the owner. Checks for roles first, then lazily checks for ownership.
modifier onlyRolesOrOwner(uint256 roles) virtual {
_checkRolesOrOwner(roles);
_;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ROLE CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// IYKYK
uint256 internal constant _ROLE_0 = 1 << 0;
uint256 internal constant _ROLE_1 = 1 << 1;
uint256 internal constant _ROLE_2 = 1 << 2;
uint256 internal constant _ROLE_3 = 1 << 3;
uint256 internal constant _ROLE_4 = 1 << 4;
uint256 internal constant _ROLE_5 = 1 << 5;
uint256 internal constant _ROLE_6 = 1 << 6;
uint256 internal constant _ROLE_7 = 1 << 7;
uint256 internal constant _ROLE_8 = 1 << 8;
uint256 internal constant _ROLE_9 = 1 << 9;
uint256 internal constant _ROLE_10 = 1 << 10;
uint256 internal constant _ROLE_11 = 1 << 11;
uint256 internal constant _ROLE_12 = 1 << 12;
uint256 internal constant _ROLE_13 = 1 << 13;
uint256 internal constant _ROLE_14 = 1 << 14;
uint256 internal constant _ROLE_15 = 1 << 15;
uint256 internal constant _ROLE_16 = 1 << 16;
uint256 internal constant _ROLE_17 = 1 << 17;
uint256 internal constant _ROLE_18 = 1 << 18;
uint256 internal constant _ROLE_19 = 1 << 19;
uint256 internal constant _ROLE_20 = 1 << 20;
uint256 internal constant _ROLE_21 = 1 << 21;
uint256 internal constant _ROLE_22 = 1 << 22;
uint256 internal constant _ROLE_23 = 1 << 23;
uint256 internal constant _ROLE_24 = 1 << 24;
uint256 internal constant _ROLE_25 = 1 << 25;
uint256 internal constant _ROLE_26 = 1 << 26;
uint256 internal constant _ROLE_27 = 1 << 27;
uint256 internal constant _ROLE_28 = 1 << 28;
uint256 internal constant _ROLE_29 = 1 << 29;
uint256 internal constant _ROLE_30 = 1 << 30;
uint256 internal constant _ROLE_31 = 1 << 31;
uint256 internal constant _ROLE_32 = 1 << 32;
uint256 internal constant _ROLE_33 = 1 << 33;
uint256 internal constant _ROLE_34 = 1 << 34;
uint256 internal constant _ROLE_35 = 1 << 35;
uint256 internal constant _ROLE_36 = 1 << 36;
uint256 internal constant _ROLE_37 = 1 << 37;
uint256 internal constant _ROLE_38 = 1 << 38;
uint256 internal constant _ROLE_39 = 1 << 39;
uint256 internal constant _ROLE_40 = 1 << 40;
uint256 internal constant _ROLE_41 = 1 << 41;
uint256 internal constant _ROLE_42 = 1 << 42;
uint256 internal constant _ROLE_43 = 1 << 43;
uint256 internal constant _ROLE_44 = 1 << 44;
uint256 internal constant _ROLE_45 = 1 << 45;
uint256 internal constant _ROLE_46 = 1 << 46;
uint256 internal constant _ROLE_47 = 1 << 47;
uint256 internal constant _ROLE_48 = 1 << 48;
uint256 internal constant _ROLE_49 = 1 << 49;
uint256 internal constant _ROLE_50 = 1 << 50;
uint256 internal constant _ROLE_51 = 1 << 51;
uint256 internal constant _ROLE_52 = 1 << 52;
uint256 internal constant _ROLE_53 = 1 << 53;
uint256 internal constant _ROLE_54 = 1 << 54;
uint256 internal constant _ROLE_55 = 1 << 55;
uint256 internal constant _ROLE_56 = 1 << 56;
uint256 internal constant _ROLE_57 = 1 << 57;
uint256 internal constant _ROLE_58 = 1 << 58;
uint256 internal constant _ROLE_59 = 1 << 59;
uint256 internal constant _ROLE_60 = 1 << 60;
uint256 internal constant _ROLE_61 = 1 << 61;
uint256 internal constant _ROLE_62 = 1 << 62;
uint256 internal constant _ROLE_63 = 1 << 63;
uint256 internal constant _ROLE_64 = 1 << 64;
uint256 internal constant _ROLE_65 = 1 << 65;
uint256 internal constant _ROLE_66 = 1 << 66;
uint256 internal constant _ROLE_67 = 1 << 67;
uint256 internal constant _ROLE_68 = 1 << 68;
uint256 internal constant _ROLE_69 = 1 << 69;
uint256 internal constant _ROLE_70 = 1 << 70;
uint256 internal constant _ROLE_71 = 1 << 71;
uint256 internal constant _ROLE_72 = 1 << 72;
uint256 internal constant _ROLE_73 = 1 << 73;
uint256 internal constant _ROLE_74 = 1 << 74;
uint256 internal constant _ROLE_75 = 1 << 75;
uint256 internal constant _ROLE_76 = 1 << 76;
uint256 internal constant _ROLE_77 = 1 << 77;
uint256 internal constant _ROLE_78 = 1 << 78;
uint256 internal constant _ROLE_79 = 1 << 79;
uint256 internal constant _ROLE_80 = 1 << 80;
uint256 internal constant _ROLE_81 = 1 << 81;
uint256 internal constant _ROLE_82 = 1 << 82;
uint256 internal constant _ROLE_83 = 1 << 83;
uint256 internal constant _ROLE_84 = 1 << 84;
uint256 internal constant _ROLE_85 = 1 << 85;
uint256 internal constant _ROLE_86 = 1 << 86;
uint256 internal constant _ROLE_87 = 1 << 87;
uint256 internal constant _ROLE_88 = 1 << 88;
uint256 internal constant _ROLE_89 = 1 << 89;
uint256 internal constant _ROLE_90 = 1 << 90;
uint256 internal constant _ROLE_91 = 1 << 91;
uint256 internal constant _ROLE_92 = 1 << 92;
uint256 internal constant _ROLE_93 = 1 << 93;
uint256 internal constant _ROLE_94 = 1 << 94;
uint256 internal constant _ROLE_95 = 1 << 95;
uint256 internal constant _ROLE_96 = 1 << 96;
uint256 internal constant _ROLE_97 = 1 << 97;
uint256 internal constant _ROLE_98 = 1 << 98;
uint256 internal constant _ROLE_99 = 1 << 99;
uint256 internal constant _ROLE_100 = 1 << 100;
uint256 internal constant _ROLE_101 = 1 << 101;
uint256 internal constant _ROLE_102 = 1 << 102;
uint256 internal constant _ROLE_103 = 1 << 103;
uint256 internal constant _ROLE_104 = 1 << 104;
uint256 internal constant _ROLE_105 = 1 << 105;
uint256 internal constant _ROLE_106 = 1 << 106;
uint256 internal constant _ROLE_107 = 1 << 107;
uint256 internal constant _ROLE_108 = 1 << 108;
uint256 internal constant _ROLE_109 = 1 << 109;
uint256 internal constant _ROLE_110 = 1 << 110;
uint256 internal constant _ROLE_111 = 1 << 111;
uint256 internal constant _ROLE_112 = 1 << 112;
uint256 internal constant _ROLE_113 = 1 << 113;
uint256 internal constant _ROLE_114 = 1 << 114;
uint256 internal constant _ROLE_115 = 1 << 115;
uint256 internal constant _ROLE_116 = 1 << 116;
uint256 internal constant _ROLE_117 = 1 << 117;
uint256 internal constant _ROLE_118 = 1 << 118;
uint256 internal constant _ROLE_119 = 1 << 119;
uint256 internal constant _ROLE_120 = 1 << 120;
uint256 internal constant _ROLE_121 = 1 << 121;
uint256 internal constant _ROLE_122 = 1 << 122;
uint256 internal constant _ROLE_123 = 1 << 123;
uint256 internal constant _ROLE_124 = 1 << 124;
uint256 internal constant _ROLE_125 = 1 << 125;
uint256 internal constant _ROLE_126 = 1 << 126;
uint256 internal constant _ROLE_127 = 1 << 127;
uint256 internal constant _ROLE_128 = 1 << 128;
uint256 internal constant _ROLE_129 = 1 << 129;
uint256 internal constant _ROLE_130 = 1 << 130;
uint256 internal constant _ROLE_131 = 1 << 131;
uint256 internal constant _ROLE_132 = 1 << 132;
uint256 internal constant _ROLE_133 = 1 << 133;
uint256 internal constant _ROLE_134 = 1 << 134;
uint256 internal constant _ROLE_135 = 1 << 135;
uint256 internal constant _ROLE_136 = 1 << 136;
uint256 internal constant _ROLE_137 = 1 << 137;
uint256 internal constant _ROLE_138 = 1 << 138;
uint256 internal constant _ROLE_139 = 1 << 139;
uint256 internal constant _ROLE_140 = 1 << 140;
uint256 internal constant _ROLE_141 = 1 << 141;
uint256 internal constant _ROLE_142 = 1 << 142;
uint256 internal constant _ROLE_143 = 1 << 143;
uint256 internal constant _ROLE_144 = 1 << 144;
uint256 internal constant _ROLE_145 = 1 << 145;
uint256 internal constant _ROLE_146 = 1 << 146;
uint256 internal constant _ROLE_147 = 1 << 147;
uint256 internal constant _ROLE_148 = 1 << 148;
uint256 internal constant _ROLE_149 = 1 << 149;
uint256 internal constant _ROLE_150 = 1 << 150;
uint256 internal constant _ROLE_151 = 1 << 151;
uint256 internal constant _ROLE_152 = 1 << 152;
uint256 internal constant _ROLE_153 = 1 << 153;
uint256 internal constant _ROLE_154 = 1 << 154;
uint256 internal constant _ROLE_155 = 1 << 155;
uint256 internal constant _ROLE_156 = 1 << 156;
uint256 internal constant _ROLE_157 = 1 << 157;
uint256 internal constant _ROLE_158 = 1 << 158;
uint256 internal constant _ROLE_159 = 1 << 159;
uint256 internal constant _ROLE_160 = 1 << 160;
uint256 internal constant _ROLE_161 = 1 << 161;
uint256 internal constant _ROLE_162 = 1 << 162;
uint256 internal constant _ROLE_163 = 1 << 163;
uint256 internal constant _ROLE_164 = 1 << 164;
uint256 internal constant _ROLE_165 = 1 << 165;
uint256 internal constant _ROLE_166 = 1 << 166;
uint256 internal constant _ROLE_167 = 1 << 167;
uint256 internal constant _ROLE_168 = 1 << 168;
uint256 internal constant _ROLE_169 = 1 << 169;
uint256 internal constant _ROLE_170 = 1 << 170;
uint256 internal constant _ROLE_171 = 1 << 171;
uint256 internal constant _ROLE_172 = 1 << 172;
uint256 internal constant _ROLE_173 = 1 << 173;
uint256 internal constant _ROLE_174 = 1 << 174;
uint256 internal constant _ROLE_175 = 1 << 175;
uint256 internal constant _ROLE_176 = 1 << 176;
uint256 internal constant _ROLE_177 = 1 << 177;
uint256 internal constant _ROLE_178 = 1 << 178;
uint256 internal constant _ROLE_179 = 1 << 179;
uint256 internal constant _ROLE_180 = 1 << 180;
uint256 internal constant _ROLE_181 = 1 << 181;
uint256 internal constant _ROLE_182 = 1 << 182;
uint256 internal constant _ROLE_183 = 1 << 183;
uint256 internal constant _ROLE_184 = 1 << 184;
uint256 internal constant _ROLE_185 = 1 << 185;
uint256 internal constant _ROLE_186 = 1 << 186;
uint256 internal constant _ROLE_187 = 1 << 187;
uint256 internal constant _ROLE_188 = 1 << 188;
uint256 internal constant _ROLE_189 = 1 << 189;
uint256 internal constant _ROLE_190 = 1 << 190;
uint256 internal constant _ROLE_191 = 1 << 191;
uint256 internal constant _ROLE_192 = 1 << 192;
uint256 internal constant _ROLE_193 = 1 << 193;
uint256 internal constant _ROLE_194 = 1 << 194;
uint256 internal constant _ROLE_195 = 1 << 195;
uint256 internal constant _ROLE_196 = 1 << 196;
uint256 internal constant _ROLE_197 = 1 << 197;
uint256 internal constant _ROLE_198 = 1 << 198;
uint256 internal constant _ROLE_199 = 1 << 199;
uint256 internal constant _ROLE_200 = 1 << 200;
uint256 internal constant _ROLE_201 = 1 << 201;
uint256 internal constant _ROLE_202 = 1 << 202;
uint256 internal constant _ROLE_203 = 1 << 203;
uint256 internal constant _ROLE_204 = 1 << 204;
uint256 internal constant _ROLE_205 = 1 << 205;
uint256 internal constant _ROLE_206 = 1 << 206;
uint256 internal constant _ROLE_207 = 1 << 207;
uint256 internal constant _ROLE_208 = 1 << 208;
uint256 internal constant _ROLE_209 = 1 << 209;
uint256 internal constant _ROLE_210 = 1 << 210;
uint256 internal constant _ROLE_211 = 1 << 211;
uint256 internal constant _ROLE_212 = 1 << 212;
uint256 internal constant _ROLE_213 = 1 << 213;
uint256 internal constant _ROLE_214 = 1 << 214;
uint256 internal constant _ROLE_215 = 1 << 215;
uint256 internal constant _ROLE_216 = 1 << 216;
uint256 internal constant _ROLE_217 = 1 << 217;
uint256 internal constant _ROLE_218 = 1 << 218;
uint256 internal constant _ROLE_219 = 1 << 219;
uint256 internal constant _ROLE_220 = 1 << 220;
uint256 internal constant _ROLE_221 = 1 << 221;
uint256 internal constant _ROLE_222 = 1 << 222;
uint256 internal constant _ROLE_223 = 1 << 223;
uint256 internal constant _ROLE_224 = 1 << 224;
uint256 internal constant _ROLE_225 = 1 << 225;
uint256 internal constant _ROLE_226 = 1 << 226;
uint256 internal constant _ROLE_227 = 1 << 227;
uint256 internal constant _ROLE_228 = 1 << 228;
uint256 internal constant _ROLE_229 = 1 << 229;
uint256 internal constant _ROLE_230 = 1 << 230;
uint256 internal constant _ROLE_231 = 1 << 231;
uint256 internal constant _ROLE_232 = 1 << 232;
uint256 internal constant _ROLE_233 = 1 << 233;
uint256 internal constant _ROLE_234 = 1 << 234;
uint256 internal constant _ROLE_235 = 1 << 235;
uint256 internal constant _ROLE_236 = 1 << 236;
uint256 internal constant _ROLE_237 = 1 << 237;
uint256 internal constant _ROLE_238 = 1 << 238;
uint256 internal constant _ROLE_239 = 1 << 239;
uint256 internal constant _ROLE_240 = 1 << 240;
uint256 internal constant _ROLE_241 = 1 << 241;
uint256 internal constant _ROLE_242 = 1 << 242;
uint256 internal constant _ROLE_243 = 1 << 243;
uint256 internal constant _ROLE_244 = 1 << 244;
uint256 internal constant _ROLE_245 = 1 << 245;
uint256 internal constant _ROLE_246 = 1 << 246;
uint256 internal constant _ROLE_247 = 1 << 247;
uint256 internal constant _ROLE_248 = 1 << 248;
uint256 internal constant _ROLE_249 = 1 << 249;
uint256 internal constant _ROLE_250 = 1 << 250;
uint256 internal constant _ROLE_251 = 1 << 251;
uint256 internal constant _ROLE_252 = 1 << 252;
uint256 internal constant _ROLE_253 = 1 << 253;
uint256 internal constant _ROLE_254 = 1 << 254;
uint256 internal constant _ROLE_255 = 1 << 255;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.16;
import { MerkleProofLib } from "solady/utils/MerkleProofLib.sol";
import { IERC165 } from "openzeppelin/utils/introspection/IERC165.sol";
import { IRangeEditionMinterV2_1, EditionMintData, MintInfo } from "./interfaces/IRangeEditionMinterV2_1.sol";
import { BaseMinterV2_1 } from "./BaseMinterV2_1.sol";
import { IMinterModuleV2_1 } from "@core/interfaces/IMinterModuleV2_1.sol";
import { ISoundEditionV1 } from "@core/interfaces/ISoundEditionV1.sol";
/*
* @title RangeEditionMinterV2_1
* @notice Module for range edition mints of Sound editions.
* @author Sound.xyz
*/
contract RangeEditionMinterV2_1 is IRangeEditionMinterV2_1, BaseMinterV2_1 {
// =============================================================
// STORAGE
// =============================================================
/**
* @dev Edition mint data.
* `_baseDataSlot(_getBaseData(edition, mintId))` => value.
*/
mapping(bytes32 => EditionMintData) internal _editionMintData;
// =============================================================
// PUBLIC / EXTERNAL WRITE FUNCTIONS
// =============================================================
/**
* @inheritdoc IRangeEditionMinterV2_1
*/
function createEditionMint(
address edition,
uint96 price,
uint32 startTime,
uint32 cutoffTime,
uint32 endTime,
uint16 affiliateFeeBPS,
uint32 maxMintableLower,
uint32 maxMintableUpper,
uint32 maxMintablePerAccount
) public returns (uint128 mintId) {
_requireValidCombinedTimeRange(startTime, cutoffTime, endTime);
if (maxMintableLower > maxMintableUpper) revert InvalidMaxMintableRange();
if (maxMintablePerAccount == 0) revert MaxMintablePerAccountIsZero();
mintId = _createEditionMint(edition, startTime, endTime, affiliateFeeBPS);
BaseData storage baseData = _getBaseDataUnchecked(edition, mintId);
baseData.price = price;
baseData.maxMintablePerAccount = maxMintablePerAccount;
EditionMintData storage data = _editionMintData[_baseDataSlot(baseData)];
data.cutoffTime = cutoffTime;
data.maxMintableLower = maxMintableLower;
data.maxMintableUpper = maxMintableUpper;
// prettier-ignore
emit RangeEditionMintCreated(
edition,
mintId,
price,
startTime,
cutoffTime,
endTime,
affiliateFeeBPS,
maxMintableLower,
maxMintableUpper,
maxMintablePerAccount
);
}
/**
* @inheritdoc IRangeEditionMinterV2_1
*/
function mintTo(
address edition,
uint128 mintId,
address to,
uint32 quantity,
address affiliate,
bytes32[] calldata affiliateProof,
uint256 attributionId
) public payable {
BaseData storage baseData = _getBaseData(edition, mintId);
EditionMintData storage data = _editionMintData[_baseDataSlot(baseData)];
uint32 _maxMintable = _getMaxMintable(data);
// Increase `totalMinted` by `quantity`.
// Require that the increased value does not exceed `maxMintable`.
data.totalMinted = _incrementTotalMinted(data.totalMinted, quantity, _maxMintable);
unchecked {
// Check the additional `requestedQuantity` does not exceed the maximum mintable per account.
uint256 numberMinted = ISoundEditionV1(edition).numberMinted(to);
// Won't overflow. The total number of tokens minted in `edition` won't exceed `type(uint32).max`,
// and `quantity` has 32 bits.
if (numberMinted + quantity > baseData.maxMintablePerAccount) revert ExceedsMaxPerAccount();
}
_mintTo(edition, mintId, to, quantity, affiliate, affiliateProof, attributionId);
}
/**
* @inheritdoc IRangeEditionMinterV2_1
*/
function mint(
address edition,
uint128 mintId,
uint32 quantity,
address affiliate
) public payable {
mintTo(edition, mintId, msg.sender, quantity, affiliate, MerkleProofLib.emptyProof(), 0);
}
/**
* @inheritdoc IRangeEditionMinterV2_1
*/
function setTimeRange(
address edition,
uint128 mintId,
uint32 startTime,
uint32 cutoffTime,
uint32 endTime
) public onlyEditionOwnerOrAdmin(edition) {
_requireValidCombinedTimeRange(startTime, cutoffTime, endTime);
BaseData storage baseData = _getBaseData(edition, mintId);
EditionMintData storage data = _editionMintData[_baseDataSlot(baseData)];
data.cutoffTime = cutoffTime;
baseData.startTime = startTime;
baseData.endTime = endTime;
emit CutoffTimeSet(edition, mintId, cutoffTime);
emit TimeRangeSet(edition, mintId, startTime, endTime);
}
/**
* @inheritdoc BaseMinterV2_1
*/
function setTimeRange(
address edition,
uint128 mintId,
uint32 startTime,
uint32 endTime
) public override(BaseMinterV2_1, IMinterModuleV2_1) onlyEditionOwnerOrAdmin(edition) {
BaseData storage baseData = _getBaseData(edition, mintId);
EditionMintData storage data = _editionMintData[_baseDataSlot(baseData)];
_requireValidCombinedTimeRange(startTime, data.cutoffTime, endTime);
baseData.startTime = startTime;
baseData.endTime = endTime;
emit TimeRangeSet(edition, mintId, startTime, endTime);
}
/**
* @inheritdoc IRangeEditionMinterV2_1
*/
function setMaxMintableRange(
address edition,
uint128 mintId,
uint32 maxMintableLower,
uint32 maxMintableUpper
) public onlyEditionOwnerOrAdmin(edition) {
if (maxMintableLower > maxMintableUpper) revert InvalidMaxMintableRange();
EditionMintData storage data = _editionMintData[_baseDataSlot(_getBaseData(edition, mintId))];
data.maxMintableLower = maxMintableLower;
data.maxMintableUpper = maxMintableUpper;
emit MaxMintableRangeSet(edition, mintId, maxMintableLower, maxMintableUpper);
}
/**
* @inheritdoc IRangeEditionMinterV2_1
*/
function setPrice(
address edition,
uint128 mintId,
uint96 price
) public onlyEditionOwnerOrAdmin(edition) {
_getBaseData(edition, mintId).price = price;
emit PriceSet(edition, mintId, price);
}
/**
* @inheritdoc IRangeEditionMinterV2_1
*/
function setMaxMintablePerAccount(
address edition,
uint128 mintId,
uint32 maxMintablePerAccount
) public onlyEditionOwnerOrAdmin(edition) {
if (maxMintablePerAccount == 0) revert MaxMintablePerAccountIsZero();
_getBaseData(edition, mintId).maxMintablePerAccount = maxMintablePerAccount;
emit MaxMintablePerAccountSet(edition, mintId, maxMintablePerAccount);
}
// =============================================================
// PUBLIC / EXTERNAL VIEW FUNCTIONS
// =============================================================
/**
* @inheritdoc IRangeEditionMinterV2_1
*/
function mintInfo(address edition, uint128 mintId) external view returns (MintInfo memory info) {
BaseData storage baseData = _getBaseData(edition, mintId);
EditionMintData storage mintData = _editionMintData[_baseDataSlot(baseData)];
info.startTime = baseData.startTime;
info.endTime = baseData.endTime;
info.affiliateFeeBPS = baseData.affiliateFeeBPS;
info.mintPaused = baseData.mintPaused;
info.price = baseData.price;
info.maxMintableUpper = mintData.maxMintableUpper;
info.maxMintableLower = mintData.maxMintableLower;
info.maxMintablePerAccount = baseData.maxMintablePerAccount;
info.totalMinted = mintData.totalMinted;
info.cutoffTime = mintData.cutoffTime;
info.affiliateMerkleRoot = baseData.affiliateMerkleRoot;
info.platformFeeBPS = platformFeeBPS;
info.platformFlatFee = platformFlatFee;
info.platformPerTxFlatFee = platformPerTxFlatFee;
}
/**
* @inheritdoc IERC165
*/
function supportsInterface(bytes4 interfaceId) public view override(IERC165, BaseMinterV2_1) returns (bool) {
return
BaseMinterV2_1.supportsInterface(interfaceId) || interfaceId == type(IRangeEditionMinterV2_1).interfaceId;
}
/**
* @inheritdoc IMinterModuleV2_1
*/
function moduleInterfaceId() public pure returns (bytes4) {
return type(IRangeEditionMinterV2_1).interfaceId;
}
/**
* @inheritdoc IRangeEditionMinterV2_1
*/
function isV2_1() external pure override returns (bool) {
return true;
}
// =============================================================
// INTERNAL / PRIVATE HELPERS
// =============================================================
/**
* @dev Restricts the `startTime` to be less than `cutoffTime`,
* and `cutoffTime` to be less than `endTime`.
* @param startTime The start unix timestamp of the mint.
* @param cutoffTime The cutoff unix timestamp of the mint.
* @param endTime The end unix timestamp of the mint.
*/
function _requireValidCombinedTimeRange(
uint32 startTime,
uint32 cutoffTime,
uint32 endTime
) internal pure {
if (!(startTime < cutoffTime && cutoffTime < endTime)) revert InvalidTimeRange();
}
/**
* @dev Gets the current maximum mintable quantity.
* @param data The edition mint data.
* @return The computed value.
*/
function _getMaxMintable(EditionMintData storage data) internal view returns (uint32) {
uint32 _maxMintable;
if (block.timestamp < data.cutoffTime) {
_maxMintable = data.maxMintableUpper;
} else {
_maxMintable = data.maxMintableLower;
}
return _maxMintable;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Safe integer casting library that reverts on overflow.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeCastLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeCast.sol)
library SafeCastLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
error Overflow();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* UNSIGNED INTEGER SAFE CASTING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function toUint8(uint256 x) internal pure returns (uint8) {
if (x >= 1 << 8) _revertOverflow();
return uint8(x);
}
function toUint16(uint256 x) internal pure returns (uint16) {
if (x >= 1 << 16) _revertOverflow();
return uint16(x);
}
function toUint24(uint256 x) internal pure returns (uint24) {
if (x >= 1 << 24) _revertOverflow();
return uint24(x);
}
function toUint32(uint256 x) internal pure returns (uint32) {
if (x >= 1 << 32) _revertOverflow();
return uint32(x);
}
function toUint40(uint256 x) internal pure returns (uint40) {
if (x >= 1 << 40) _revertOverflow();
return uint40(x);
}
function toUint48(uint256 x) internal pure returns (uint48) {
if (x >= 1 << 48) _revertOverflow();
return uint48(x);
}
function toUint56(uint256 x) internal pure returns (uint56) {
if (x >= 1 << 56) _revertOverflow();
return uint56(x);
}
function toUint64(uint256 x) internal pure returns (uint64) {
if (x >= 1 << 64) _revertOverflow();
return uint64(x);
}
function toUint72(uint256 x) internal pure returns (uint72) {
if (x >= 1 << 72) _revertOverflow();
return uint72(x);
}
function toUint80(uint256 x) internal pure returns (uint80) {
if (x >= 1 << 80) _revertOverflow();
return uint80(x);
}
function toUint88(uint256 x) internal pure returns (uint88) {
if (x >= 1 << 88) _revertOverflow();
return uint88(x);
}
function toUint96(uint256 x) internal pure returns (uint96) {
if (x >= 1 << 96) _revertOverflow();
return uint96(x);
}
function toUint104(uint256 x) internal pure returns (uint104) {
if (x >= 1 << 104) _revertOverflow();
return uint104(x);
}
function toUint112(uint256 x) internal pure returns (uint112) {
if (x >= 1 << 112) _revertOverflow();
return uint112(x);
}
function toUint120(uint256 x) internal pure returns (uint120) {
if (x >= 1 << 120) _revertOverflow();
return uint120(x);
}
function toUint128(uint256 x) internal pure returns (uint128) {
if (x >= 1 << 128) _revertOverflow();
return uint128(x);
}
function toUint136(uint256 x) internal pure returns (uint136) {
if (x >= 1 << 136) _revertOverflow();
return uint136(x);
}
function toUint144(uint256 x) internal pure returns (uint144) {
if (x >= 1 << 144) _revertOverflow();
return uint144(x);
}
function toUint152(uint256 x) internal pure returns (uint152) {
if (x >= 1 << 152) _revertOverflow();
return uint152(x);
}
function toUint160(uint256 x) internal pure returns (uint160) {
if (x >= 1 << 160) _revertOverflow();
return uint160(x);
}
function toUint168(uint256 x) internal pure returns (uint168) {
if (x >= 1 << 168) _revertOverflow();
return uint168(x);
}
function toUint176(uint256 x) internal pure returns (uint176) {
if (x >= 1 << 176) _revertOverflow();
return uint176(x);
}
function toUint184(uint256 x) internal pure returns (uint184) {
if (x >= 1 << 184) _revertOverflow();
return uint184(x);
}
function toUint192(uint256 x) internal pure returns (uint192) {
if (x >= 1 << 192) _revertOverflow();
return uint192(x);
}
function toUint200(uint256 x) internal pure returns (uint200) {
if (x >= 1 << 200) _revertOverflow();
return uint200(x);
}
function toUint208(uint256 x) internal pure returns (uint208) {
if (x >= 1 << 208) _revertOverflow();
return uint208(x);
}
function toUint216(uint256 x) internal pure returns (uint216) {
if (x >= 1 << 216) _revertOverflow();
return uint216(x);
}
function toUint224(uint256 x) internal pure returns (uint224) {
if (x >= 1 << 224) _revertOverflow();
return uint224(x);
}
function toUint232(uint256 x) internal pure returns (uint232) {
if (x >= 1 << 232) _revertOverflow();
return uint232(x);
}
function toUint240(uint256 x) internal pure returns (uint240) {
if (x >= 1 << 240) _revertOverflow();
return uint240(x);
}
function toUint248(uint256 x) internal pure returns (uint248) {
if (x >= 1 << 248) _revertOverflow();
return uint248(x);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* SIGNED INTEGER SAFE CASTING OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function toInt8(int256 x) internal pure returns (int8) {
int8 y = int8(x);
if (x != y) _revertOverflow();
return y;
}
function toInt16(int256 x) internal pure returns (int16) {
int16 y = int16(x);
if (x != y) _revertOverflow();
return y;
}
function toInt24(int256 x) internal pure returns (int24) {
int24 y = int24(x);
if (x != y) _revertOverflow();
return y;
}
function toInt32(int256 x) internal pure returns (int32) {
int32 y = int32(x);
if (x != y) _revertOverflow();
return y;
}
function toInt40(int256 x) internal pure returns (int40) {
int40 y = int40(x);
if (x != y) _revertOverflow();
return y;
}
function toInt48(int256 x) internal pure returns (int48) {
int48 y = int48(x);
if (x != y) _revertOverflow();
return y;
}
function toInt56(int256 x) internal pure returns (int56) {
int56 y = int56(x);
if (x != y) _revertOverflow();
return y;
}
function toInt64(int256 x) internal pure returns (int64) {
int64 y = int64(x);
if (x != y) _revertOverflow();
return y;
}
function toInt72(int256 x) internal pure returns (int72) {
int72 y = int72(x);
if (x != y) _revertOverflow();
return y;
}
function toInt80(int256 x) internal pure returns (int80) {
int80 y = int80(x);
if (x != y) _revertOverflow();
return y;
}
function toInt88(int256 x) internal pure returns (int88) {
int88 y = int88(x);
if (x != y) _revertOverflow();
return y;
}
function toInt96(int256 x) internal pure returns (int96) {
int96 y = int96(x);
if (x != y) _revertOverflow();
return y;
}
function toInt104(int256 x) internal pure returns (int104) {
int104 y = int104(x);
if (x != y) _revertOverflow();
return y;
}
function toInt112(int256 x) internal pure returns (int112) {
int112 y = int112(x);
if (x != y) _revertOverflow();
return y;
}
function toInt120(int256 x) internal pure returns (int120) {
int120 y = int120(x);
if (x != y) _revertOverflow();
return y;
}
function toInt128(int256 x) internal pure returns (int128) {
int128 y = int128(x);
if (x != y) _revertOverflow();
return y;
}
function toInt136(int256 x) internal pure returns (int136) {
int136 y = int136(x);
if (x != y) _revertOverflow();
return y;
}
function toInt144(int256 x) internal pure returns (int144) {
int144 y = int144(x);
if (x != y) _revertOverflow();
return y;
}
function toInt152(int256 x) internal pure returns (int152) {
int152 y = int152(x);
if (x != y) _revertOverflow();
return y;
}
function toInt160(int256 x) internal pure returns (int160) {
int160 y = int160(x);
if (x != y) _revertOverflow();
return y;
}
function toInt168(int256 x) internal pure returns (int168) {
int168 y = int168(x);
if (x != y) _revertOverflow();
return y;
}
function toInt176(int256 x) internal pure returns (int176) {
int176 y = int176(x);
if (x != y) _revertOverflow();
return y;
}
function toInt184(int256 x) internal pure returns (int184) {
int184 y = int184(x);
if (x != y) _revertOverflow();
return y;
}
function toInt192(int256 x) internal pure returns (int192) {
int192 y = int192(x);
if (x != y) _revertOverflow();
return y;
}
function toInt200(int256 x) internal pure returns (int200) {
int200 y = int200(x);
if (x != y) _revertOverflow();
return y;
}
function toInt208(int256 x) internal pure returns (int208) {
int208 y = int208(x);
if (x != y) _revertOverflow();
return y;
}
function toInt216(int256 x) internal pure returns (int216) {
int216 y = int216(x);
if (x != y) _revertOverflow();
return y;
}
function toInt224(int256 x) internal pure returns (int224) {
int224 y = int224(x);
if (x != y) _revertOverflow();
return y;
}
function toInt232(int256 x) internal pure returns (int232) {
int232 y = int232(x);
if (x != y) _revertOverflow();
return y;
}
function toInt240(int256 x) internal pure returns (int240) {
int240 y = int240(x);
if (x != y) _revertOverflow();
return y;
}
function toInt248(int256 x) internal pure returns (int248) {
int248 y = int248(x);
if (x != y) _revertOverflow();
return y;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PRIVATE HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function _revertOverflow() private pure {
/// @solidity memory-safe-assembly
assembly {
// Store the function selector of `Overflow()`.
mstore(0x00, 0x35278d12)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Caution! This library won't check that a token has code, responsibility is delegated to the caller.
library SafeTransferLib {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The ETH transfer has failed.
error ETHTransferFailed();
/// @dev The ERC20 `transferFrom` has failed.
error TransferFromFailed();
/// @dev The ERC20 `transfer` has failed.
error TransferFailed();
/// @dev The ERC20 `approve` has failed.
error ApproveFailed();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Suggested gas stipend for contract receiving ETH
/// that disallows any storage writes.
uint256 internal constant _GAS_STIPEND_NO_STORAGE_WRITES = 2300;
/// @dev Suggested gas stipend for contract receiving ETH to perform a few
/// storage reads and writes, but low enough to prevent griefing.
/// Multiply by a small constant (e.g. 2), if needed.
uint256 internal constant _GAS_STIPEND_NO_GRIEF = 100000;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ETH OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Sends `amount` (in wei) ETH to `to`.
/// Reverts upon failure.
function safeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
// Transfer the ETH and check if it succeeded or not.
if iszero(call(gas(), to, amount, 0, 0, 0, 0)) {
// Store the function selector of `ETHTransferFailed()`.
mstore(0x00, 0xb12d13eb)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
/// The `gasStipend` can be set to a low enough value to prevent
/// storage writes or gas griefing.
///
/// If sending via the normal procedure fails, force sends the ETH by
/// creating a temporary contract which uses `SELFDESTRUCT` to force send the ETH.
///
/// Reverts if the current contract has insufficient balance.
function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
/// @solidity memory-safe-assembly
assembly {
// If insufficient balance, revert.
if lt(selfbalance(), amount) {
// Store the function selector of `ETHTransferFailed()`.
mstore(0x00, 0xb12d13eb)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// Transfer the ETH and check if it succeeded or not.
if iszero(call(gasStipend, to, amount, 0, 0, 0, 0)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
// We can directly use `SELFDESTRUCT` in the contract creation.
// Compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758
pop(create(amount, 0x0b, 0x16))
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with a gas stipend
/// equal to `_GAS_STIPEND_NO_GRIEF`. This gas stipend is a reasonable default
/// for 99% of cases and can be overriden with the three-argument version of this
/// function if necessary.
///
/// If sending via the normal procedure fails, force sends the ETH by
/// creating a temporary contract which uses `SELFDESTRUCT` to force send the ETH.
///
/// Reverts if the current contract has insufficient balance.
function forceSafeTransferETH(address to, uint256 amount) internal {
// Manually inlined because the compiler doesn't inline functions with branches.
/// @solidity memory-safe-assembly
assembly {
// If insufficient balance, revert.
if lt(selfbalance(), amount) {
// Store the function selector of `ETHTransferFailed()`.
mstore(0x00, 0xb12d13eb)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// Transfer the ETH and check if it succeeded or not.
if iszero(call(_GAS_STIPEND_NO_GRIEF, to, amount, 0, 0, 0, 0)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
// We can directly use `SELFDESTRUCT` in the contract creation.
// Compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758
pop(create(amount, 0x0b, 0x16))
}
}
}
/// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
/// The `gasStipend` can be set to a low enough value to prevent
/// storage writes or gas griefing.
///
/// Simply use `gasleft()` for `gasStipend` if you don't need a gas stipend.
///
/// Note: Does NOT revert upon failure.
/// Returns whether the transfer of ETH is successful instead.
function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
// Transfer the ETH and check if it succeeded or not.
success := call(gasStipend, to, amount, 0, 0, 0, 0)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC20 OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have at least `amount` approved for
/// the current contract to manage.
function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
// Store the function selector of `transferFrom(address,address,uint256)`.
mstore(0x00, 0x23b872dd)
mstore(0x20, from) // Store the `from` argument.
mstore(0x40, to) // Store the `to` argument.
mstore(0x60, amount) // Store the `amount` argument.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(eq(mload(0x00), 1), iszero(returndatasize())),
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
) {
// Store the function selector of `TransferFromFailed()`.
mstore(0x00, 0x7939f424)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends all of ERC20 `token` from `from` to `to`.
/// Reverts upon failure.
///
/// The `from` account must have at least `amount` approved for
/// the current contract to manage.
function safeTransferAllFrom(address token, address from, address to)
internal
returns (uint256 amount)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
mstore(0x20, from) // Store the `from` argument.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
)
) {
// Store the function selector of `TransferFromFailed()`.
mstore(0x00, 0x7939f424)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// Store the function selector of `transferFrom(address,address,uint256)`.
mstore(0x00, 0x23b872dd)
mstore(0x40, to) // Store the `to` argument.
// The `amount` argument is already written to the memory word at 0x6a.
amount := mload(0x60)
if iszero(
and( // The arguments of `and` are evaluated from right to left.
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(eq(mload(0x00), 1), iszero(returndatasize())),
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
) {
// Store the function selector of `TransferFromFailed()`.
mstore(0x00, 0x7939f424)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot to zero.
mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransfer(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x1a, to) // Store the `to` argument.
mstore(0x3a, amount) // Store the `amount` argument.
// Store the function selector of `transfer(address,uint256)`,
// left by 6 bytes (enough for 8tb of memory represented by the free memory pointer).
// We waste 6-3 = 3 bytes to save on 6 runtime gas (PUSH1 0x224 SHL).
mstore(0x00, 0xa9059cbb000000000000)
if iszero(
and( // The arguments of `and` are evaluated from right to left.
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(eq(mload(0x00), 1), iszero(returndatasize())),
call(gas(), token, 0, 0x16, 0x44, 0x00, 0x20)
)
) {
// Store the function selector of `TransferFailed()`.
mstore(0x00, 0x90b8ec18)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// Restore the part of the free memory pointer that was overwritten,
// which is guaranteed to be zero, if less than 8tb of memory is used.
mstore(0x3a, 0)
}
}
/// @dev Sends all of ERC20 `token` from the current contract to `to`.
/// Reverts upon failure.
function safeTransferAll(address token, address to) internal returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
mstore(0x20, address()) // Store the address of the current contract.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x3a, 0x20)
)
) {
// Store the function selector of `TransferFailed()`.
mstore(0x00, 0x90b8ec18)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
mstore(0x1a, to) // Store the `to` argument.
// The `amount` argument is already written to the memory word at 0x3a.
amount := mload(0x3a)
// Store the function selector of `transfer(address,uint256)`,
// left by 6 bytes (enough for 8tb of memory represented by the free memory pointer).
// We waste 6-3 = 3 bytes to save on 6 runtime gas (PUSH1 0x224 SHL).
mstore(0x00, 0xa9059cbb000000000000)
if iszero(
and( // The arguments of `and` are evaluated from right to left.
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(eq(mload(0x00), 1), iszero(returndatasize())),
call(gas(), token, 0, 0x16, 0x44, 0x00, 0x20)
)
) {
// Store the function selector of `TransferFailed()`.
mstore(0x00, 0x90b8ec18)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// Restore the part of the free memory pointer that was overwritten,
// which is guaranteed to be zero, if less than 8tb of memory is used.
mstore(0x3a, 0)
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
/// Reverts upon failure.
function safeApprove(address token, address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x1a, to) // Store the `to` argument.
mstore(0x3a, amount) // Store the `amount` argument.
// Store the function selector of `approve(address,uint256)`,
// left by 6 bytes (enough for 8tb of memory represented by the free memory pointer).
// We waste 6-3 = 3 bytes to save on 6 runtime gas (PUSH1 0x224 SHL).
mstore(0x00, 0x095ea7b3000000000000)
if iszero(
and( // The arguments of `and` are evaluated from right to left.
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(eq(mload(0x00), 1), iszero(returndatasize())),
call(gas(), token, 0, 0x16, 0x44, 0x00, 0x20)
)
) {
// Store the function selector of `ApproveFailed()`.
mstore(0x00, 0x3e3f8f73)
// Revert with (offset, size).
revert(0x1c, 0x04)
}
// Restore the part of the free memory pointer that was overwritten,
// which is guaranteed to be zero, if less than 8tb of memory is used.
mstore(0x3a, 0)
}
}
/// @dev Returns the amount of ERC20 `token` owned by `account`.
/// Returns zero if the `token` does not exist.
function balanceOf(address token, address account) internal view returns (uint256 amount) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
mstore(0x20, account) // Store the `account` argument.
amount :=
mul(
mload(0x20),
and( // The arguments of `and` are evaluated from right to left.
gt(returndatasize(), 0x1f), // At least 32 bytes returned.
staticcall(gas(), token, 0x1c, 0x24, 0x20, 0x20)
)
)
}
}
}
{
"compilationTarget": {
"contracts/modules/RangeEditionMinterV2_1.sol": "RangeEditionMinterV2_1"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 1000
},
"remappings": [
":@core/=contracts/core/",
":@modules/=contracts/modules/",
":ERC721A-Upgradeable/=lib/ERC721A-Upgradeable/contracts/",
":chiru-labs/ERC721A-Upgradeable/=lib/ERC721A-Upgradeable/contracts/",
":closedsea/=lib/closedsea/src/",
":ds-test/=lib/solady/lib/ds-test/src/",
":erc4626-tests/=lib/closedsea/lib/openzeppelin-contracts/lib/erc4626-tests/",
":erc721a-upgradeable/=lib/closedsea/lib/erc721a-upgradeable/contracts/",
":erc721a/=lib/closedsea/lib/erc721a/contracts/",
":forge-std/=lib/forge-std/src/",
":multicaller/=lib/multicaller/src/",
":murky/=lib/murky/src/",
":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
":openzeppelin-contracts/=lib/openzeppelin-contracts/",
":openzeppelin-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
":openzeppelin/=lib/openzeppelin-contracts/contracts/",
":operator-filter-registry/=lib/closedsea/lib/operator-filter-registry/",
":preapprove/=lib/preapprove/src/",
":solady/=lib/solady/src/",
":solmate/=lib/solady/lib/solmate/src/"
]
}
[{"inputs":[{"internalType":"uint32","name":"available","type":"uint32"}],"name":"ExceedsAvailableSupply","type":"error"},{"inputs":[],"name":"ExceedsMaxPerAccount","type":"error"},{"inputs":[],"name":"InvalidAffiliate","type":"error"},{"inputs":[],"name":"InvalidAffiliateFeeBPS","type":"error"},{"inputs":[],"name":"InvalidMaxMintableRange","type":"error"},{"inputs":[],"name":"InvalidPlatformFeeBPS","type":"error"},{"inputs":[],"name":"InvalidPlatformFlatFee","type":"error"},{"inputs":[],"name":"InvalidPlatformPerTxFlatFee","type":"error"},{"inputs":[],"name":"InvalidTimeRange","type":"error"},{"inputs":[],"name":"MaxMintablePerAccountIsZero","type":"error"},{"inputs":[],"name":"MintDoesNotExist","type":"error"},{"inputs":[{"internalType":"uint256","name":"blockTimestamp","type":"uint256"},{"internalType":"uint32","name":"startTime","type":"uint32"},{"internalType":"uint32","name":"endTime","type":"uint32"}],"name":"MintNotOpen","type":"error"},{"inputs":[],"name":"MintPaused","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"PlatformFeeAddressIsZero","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[{"internalType":"uint256","name":"paid","type":"uint256"},{"internalType":"uint256","name":"required","type":"uint256"}],"name":"Underpaid","type":"error"},{"inputs":[{"internalType":"uint256","name":"paid","type":"uint256"},{"internalType":"uint256","name":"required","type":"uint256"}],"name":"WrongPayment","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"edition","type":"address"},{"indexed":false,"internalType":"uint128","name":"mintId","type":"uint128"},{"indexed":false,"internalType":"uint16","name":"bps","type":"uint16"}],"name":"AffiliateFeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"affiliate","type":"address"},{"indexed":false,"internalType":"uint256","name":"accrued","type":"uint256"}],"name":"AffiliateFeesWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"edition","type":"address"},{"indexed":false,"internalType":"uint128","name":"mintId","type":"uint128"},{"indexed":false,"internalType":"bytes32","name":"root","type":"bytes32"}],"name":"AffiliateMerkleRootSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"edition","type":"address"},{"indexed":false,"internalType":"uint128","name":"mintId","type":"uint128"},{"indexed":false,"internalType":"uint32","name":"cutoffTime","type":"uint32"}],"name":"CutoffTimeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"edition","type":"address"},{"indexed":false,"internalType":"uint128","name":"mintId","type":"uint128"},{"indexed":false,"internalType":"uint32","name":"maxMintablePerAccount","type":"uint32"}],"name":"MaxMintablePerAccountSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"edition","type":"address"},{"indexed":false,"internalType":"uint128","name":"mintId","type":"uint128"},{"indexed":false,"internalType":"uint32","name":"maxMintableLower","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"maxMintableUpper","type":"uint32"}],"name":"MaxMintableRangeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"edition","type":"address"},{"indexed":true,"internalType":"address","name":"creator","type":"address"},{"indexed":false,"internalType":"uint128","name":"mintId","type":"uint128"},{"indexed":false,"internalType":"uint32","name":"startTime","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"endTime","type":"uint32"},{"indexed":false,"internalType":"uint16","name":"affiliateFeeBPS","type":"uint16"}],"name":"MintConfigCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"edition","type":"address"},{"indexed":false,"internalType":"uint128","name":"mintId","type":"uint128"},{"indexed":false,"internalType":"bool","name":"paused","type":"bool"}],"name":"MintPausedSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"edition","type":"address"},{"indexed":false,"internalType":"uint128","name":"mintId","type":"uint128"},{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint32","name":"fromTokenId","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"quantity","type":"uint32"},{"indexed":false,"internalType":"uint128","name":"requiredEtherValue","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"platformFee","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"affiliateFee","type":"uint128"},{"indexed":false,"internalType":"address","name":"affiliate","type":"address"},{"indexed":false,"internalType":"bool","name":"affiliated","type":"bool"},{"indexed":true,"internalType":"uint256","name":"attributionId","type":"uint256"}],"name":"Minted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"addr","type":"address"}],"name":"PlatformFeeAddressSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"bps","type":"uint16"}],"name":"PlatformFeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint128","name":"accrued","type":"uint128"}],"name":"PlatformFeesWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint96","name":"flatFee","type":"uint96"}],"name":"PlatformFlatFeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint96","name":"perTxFlatFee","type":"uint96"}],"name":"PlatformPerTxFlatFeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"edition","type":"address"},{"indexed":false,"internalType":"uint128","name":"mintId","type":"uint128"},{"indexed":false,"internalType":"uint96","name":"price","type":"uint96"}],"name":"PriceSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"edition","type":"address"},{"indexed":false,"internalType":"uint128","name":"mintId","type":"uint128"},{"indexed":false,"internalType":"uint96","name":"price","type":"uint96"},{"indexed":false,"internalType":"uint32","name":"startTime","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"cutoffTime","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"endTime","type":"uint32"},{"indexed":false,"internalType":"uint16","name":"affiliateFeeBPS","type":"uint16"},{"indexed":false,"internalType":"uint32","name":"maxMintableLower","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"maxMintableUpper","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"maxMintablePerAccount","type":"uint32"}],"name":"RangeEditionMintCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"edition","type":"address"},{"indexed":false,"internalType":"uint128","name":"mintId","type":"uint128"},{"indexed":false,"internalType":"uint32","name":"startTime","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"endTime","type":"uint32"}],"name":"TimeRangeSet","type":"event"},{"inputs":[],"name":"BPS_DENOMINATOR","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_AFFILIATE_FEE_BPS","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_PLATFORM_FEE_BPS","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_PLATFORM_FLAT_FEE","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_PLATFORM_PER_TX_FLAT_FEE","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"affiliateFeesAccrued","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"edition","type":"address"},{"internalType":"uint128","name":"mintId","type":"uint128"}],"name":"affiliateMerkleRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"edition","type":"address"},{"internalType":"uint96","name":"price","type":"uint96"},{"internalType":"uint32","name":"startTime","type":"uint32"},{"internalType":"uint32","name":"cutoffTime","type":"uint32"},{"internalType":"uint32","name":"endTime","type":"uint32"},{"internalType":"uint16","name":"affiliateFeeBPS","type":"uint16"},{"internalType":"uint32","name":"maxMintableLower","type":"uint32"},{"internalType":"uint32","name":"maxMintableUpper","type":"uint32"},{"internalType":"uint32","name":"maxMintablePerAccount","type":"uint32"}],"name":"createEditionMint","outputs":[{"internalType":"uint128","name":"mintId","type":"uint128"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"edition","type":"address"},{"internalType":"uint128","name":"mintId","type":"uint128"},{"internalType":"address","name":"affiliate","type":"address"}],"name":"isAffiliated","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"edition","type":"address"},{"internalType":"uint128","name":"mintId","type":"uint128"},{"internalType":"address","name":"affiliate","type":"address"},{"internalType":"bytes32[]","name":"affiliateProof","type":"bytes32[]"}],"name":"isAffiliatedWithProof","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isV2_1","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"edition","type":"address"},{"internalType":"uint128","name":"mintId","type":"uint128"},{"internalType":"uint32","name":"quantity","type":"uint32"},{"internalType":"address","name":"affiliate","type":"address"}],"name":"mint","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"edition","type":"address"},{"internalType":"uint128","name":"mintId","type":"uint128"}],"name":"mintInfo","outputs":[{"components":[{"internalType":"uint32","name":"startTime","type":"uint32"},{"internalType":"uint32","name":"endTime","type":"uint32"},{"internalType":"uint16","name":"affiliateFeeBPS","type":"uint16"},{"internalType":"bool","name":"mintPaused","type":"bool"},{"internalType":"uint96","name":"price","type":"uint96"},{"internalType":"uint32","name":"maxMintableUpper","type":"uint32"},{"internalType":"uint32","name":"maxMintableLower","type":"uint32"},{"internalType":"uint32","name":"maxMintablePerAccount","type":"uint32"},{"internalType":"uint32","name":"totalMinted","type":"uint32"},{"internalType":"uint32","name":"cutoffTime","type":"uint32"},{"internalType":"bytes32","name":"affiliateMerkleRoot","type":"bytes32"},{"internalType":"uint16","name":"platformFeeBPS","type":"uint16"},{"internalType":"uint96","name":"platformFlatFee","type":"uint96"},{"internalType":"uint96","name":"platformPerTxFlatFee","type":"uint96"}],"internalType":"struct MintInfo","name":"info","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"edition","type":"address"},{"internalType":"uint128","name":"mintId","type":"uint128"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint32","name":"quantity","type":"uint32"},{"internalType":"address","name":"affiliate","type":"address"},{"internalType":"bytes32[]","name":"affiliateProof","type":"bytes32[]"},{"internalType":"uint256","name":"attributionId","type":"uint256"}],"name":"mintTo","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"moduleInterfaceId","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"nextMintId","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ownershipHandoverValidFor","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"platformFeeAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"platformFeeBPS","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"platformFeesAccrued","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"platformFlatFee","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"platformPerTxFlatFee","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"edition","type":"address"},{"internalType":"uint128","name":"mintId","type":"uint128"},{"internalType":"uint16","name":"bps","type":"uint16"}],"name":"setAffiliateFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"edition","type":"address"},{"internalType":"uint128","name":"mintId","type":"uint128"},{"internalType":"bytes32","name":"root","type":"bytes32"}],"name":"setAffiliateMerkleRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"edition","type":"address"},{"internalType":"uint128","name":"mintId","type":"uint128"},{"internalType":"bool","name":"paused","type":"bool"}],"name":"setEditionMintPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"edition","type":"address"},{"internalType":"uint128","name":"mintId","type":"uint128"},{"internalType":"uint32","name":"maxMintablePerAccount","type":"uint32"}],"name":"setMaxMintablePerAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"edition","type":"address"},{"internalType":"uint128","name":"mintId","type":"uint128"},{"internalType":"uint32","name":"maxMintableLower","type":"uint32"},{"internalType":"uint32","name":"maxMintableUpper","type":"uint32"}],"name":"setMaxMintableRange","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"bps","type":"uint16"}],"name":"setPlatformFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"setPlatformFeeAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint96","name":"flatFee","type":"uint96"}],"name":"setPlatformFlatFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint96","name":"perTxFlatFee","type":"uint96"}],"name":"setPlatformPerTxFlatFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"edition","type":"address"},{"internalType":"uint128","name":"mintId","type":"uint128"},{"internalType":"uint96","name":"price","type":"uint96"}],"name":"setPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"edition","type":"address"},{"internalType":"uint128","name":"mintId","type":"uint128"},{"internalType":"uint32","name":"startTime","type":"uint32"},{"internalType":"uint32","name":"endTime","type":"uint32"}],"name":"setTimeRange","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"edition","type":"address"},{"internalType":"uint128","name":"mintId","type":"uint128"},{"internalType":"uint32","name":"startTime","type":"uint32"},{"internalType":"uint32","name":"cutoffTime","type":"uint32"},{"internalType":"uint32","name":"endTime","type":"uint32"}],"name":"setTimeRange","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"edition","type":"address"},{"internalType":"uint128","name":"mintId","type":"uint128"},{"internalType":"uint32","name":"quantity","type":"uint32"}],"name":"totalPriceAndFees","outputs":[{"internalType":"uint256","name":"total","type":"uint256"},{"internalType":"uint256","name":"subTotal","type":"uint256"},{"internalType":"uint256","name":"platformFlatFeeTotal","type":"uint256"},{"internalType":"uint256","name":"platformFee","type":"uint256"},{"internalType":"uint256","name":"affiliateFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"affiliate","type":"address"}],"name":"withdrawForAffiliate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawForPlatform","outputs":[],"stateMutability":"nonpayable","type":"function"}]