// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.2) (utils/Base64.sol)pragmasolidity ^0.8.20;/**
* @dev Provides a set of functions to operate with Base64 strings.
*/libraryBase64{
/**
* @dev Base64 Encoding/Decoding Table
*/stringinternalconstant _TABLE ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/**
* @dev Converts a `bytes` to its Bytes64 `string` representation.
*/functionencode(bytesmemory data) internalpurereturns (stringmemory) {
/**
* Inspired by Brecht Devos (Brechtpd) implementation - MIT licence
* https://github.com/Brechtpd/base64/blob/e78d9fd951e7b0977ddca77d92dc85183770daf4/base64.sol
*/if (data.length==0) return"";
// Loads the table into memorystringmemory table = _TABLE;
// Encoding takes 3 bytes chunks of binary data from `bytes` data parameter// and split into 4 numbers of 6 bits.// The final Base64 length should be `bytes` data length multiplied by 4/3 rounded up// - `data.length + 2` -> Round up// - `/ 3` -> Number of 3-bytes chunks// - `4 *` -> 4 characters for each chunkstringmemory result =newstring(4* ((data.length+2) /3));
/// @solidity memory-safe-assemblyassembly {
// Prepare the lookup table (skip the first "length" byte)let tablePtr :=add(table, 1)
// Prepare result pointer, jump over lengthlet resultPtr :=add(result, 0x20)
let dataPtr := data
let endPtr :=add(data, mload(data))
// In some cases, the last iteration will read bytes after the end of the data. We cache the value, and// set it to zero to make sure no dirty bytes are read in that section.let afterPtr :=add(endPtr, 0x20)
let afterCache :=mload(afterPtr)
mstore(afterPtr, 0x00)
// Run over the input, 3 bytes at a timefor {
} lt(dataPtr, endPtr) {
} {
// Advance 3 bytes
dataPtr :=add(dataPtr, 3)
let input :=mload(dataPtr)
// To write each character, shift the 3 byte (24 bits) chunk// 4 times in blocks of 6 bits for each character (18, 12, 6, 0)// and apply logical AND with 0x3F to bitmask the least significant 6 bits.// Use this as an index into the lookup table, mload an entire word// so the desired character is in the least significant byte, and// mstore8 this least significant byte into the result and continue.mstore8(resultPtr, mload(add(tablePtr, and(shr(18, input), 0x3F))))
resultPtr :=add(resultPtr, 1) // Advancemstore8(resultPtr, mload(add(tablePtr, and(shr(12, input), 0x3F))))
resultPtr :=add(resultPtr, 1) // Advancemstore8(resultPtr, mload(add(tablePtr, and(shr(6, input), 0x3F))))
resultPtr :=add(resultPtr, 1) // Advancemstore8(resultPtr, mload(add(tablePtr, and(input, 0x3F))))
resultPtr :=add(resultPtr, 1) // Advance
}
// Reset the value that was cachedmstore(afterPtr, afterCache)
// When data `bytes` is not exactly 3 bytes long// it is padded with `=` characters at the endswitchmod(mload(data), 3)
case1 {
mstore8(sub(resultPtr, 1), 0x3d)
mstore8(sub(resultPtr, 2), 0x3d)
}
case2 {
mstore8(sub(resultPtr, 1), 0x3d)
}
}
return result;
}
}
Contract Source Code
File 2 of 28: Clones.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (proxy/Clones.sol)pragmasolidity ^0.8.20;/**
* @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
* deploying minimal proxy contracts, also known as "clones".
*
* > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
* > a minimal bytecode implementation that delegates all calls to a known, fixed address.
*
* The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
* (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
* deterministic method.
*/libraryClones{
/**
* @dev A clone instance deployment failed.
*/errorERC1167FailedCreateClone();
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
*
* This function uses the create opcode, which should never revert.
*/functionclone(address implementation) internalreturns (address instance) {
/// @solidity memory-safe-assemblyassembly {
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes// of the `implementation` address with the bytecode before the address.mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address.mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
instance :=create(0, 0x09, 0x37)
}
if (instance ==address(0)) {
revert ERC1167FailedCreateClone();
}
}
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
*
* This function uses the create2 opcode and a `salt` to deterministically deploy
* the clone. Using the same `implementation` and `salt` multiple time will revert, since
* the clones cannot be deployed twice at the same address.
*/functioncloneDeterministic(address implementation, bytes32 salt) internalreturns (address instance) {
/// @solidity memory-safe-assemblyassembly {
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes// of the `implementation` address with the bytecode before the address.mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address.mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
instance :=create2(0, 0x09, 0x37, salt)
}
if (instance ==address(0)) {
revert ERC1167FailedCreateClone();
}
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/functionpredictDeterministicAddress(address implementation,
bytes32 salt,
address deployer
) internalpurereturns (address predicted) {
/// @solidity memory-safe-assemblyassembly {
let ptr :=mload(0x40)
mstore(add(ptr, 0x38), deployer)
mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
mstore(add(ptr, 0x14), implementation)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
mstore(add(ptr, 0x58), salt)
mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
predicted :=keccak256(add(ptr, 0x43), 0x55)
}
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/functionpredictDeterministicAddress(address implementation,
bytes32 salt
) internalviewreturns (address predicted) {
return predictDeterministicAddress(implementation, salt, address(this));
}
}
Contract Source Code
File 3 of 28: Context.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)pragmasolidity ^0.8.20;/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/abstractcontractContext{
function_msgSender() internalviewvirtualreturns (address) {
returnmsg.sender;
}
function_msgData() internalviewvirtualreturns (bytescalldata) {
returnmsg.data;
}
function_contextSuffixLength() internalviewvirtualreturns (uint256) {
return0;
}
}
Contract Source Code
File 4 of 28: ContextUpgradeable.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)pragmasolidity ^0.8.20;import {Initializable} from"../proxy/utils/Initializable.sol";
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/abstractcontractContextUpgradeableisInitializable{
function__Context_init() internalonlyInitializing{
}
function__Context_init_unchained() internalonlyInitializing{
}
function_msgSender() internalviewvirtualreturns (address) {
returnmsg.sender;
}
function_msgData() internalviewvirtualreturns (bytescalldata) {
returnmsg.data;
}
function_contextSuffixLength() internalviewvirtualreturns (uint256) {
return0;
}
}
Contract Source Code
File 5 of 28: ECDSA.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)pragmasolidity ^0.8.20;/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/libraryECDSA{
enumRecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS
}
/**
* @dev The signature derives the `address(0)`.
*/errorECDSAInvalidSignature();
/**
* @dev The signature has an invalid length.
*/errorECDSAInvalidSignatureLength(uint256 length);
/**
* @dev The signature has an S value that is in the upper half order.
*/errorECDSAInvalidSignatureS(bytes32 s);
/**
* @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
* return address(0) without also returning an error description. Errors are documented using an enum (error type)
* and a bytes32 providing additional information about the error.
*
* If no error is returned, then the address can be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*/functiontryRecover(bytes32 hash, bytesmemory signature) internalpurereturns (address, RecoverError, bytes32) {
if (signature.length==65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them// currently is to use assembly./// @solidity memory-safe-assemblyassembly {
r :=mload(add(signature, 0x20))
s :=mload(add(signature, 0x40))
v :=byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*/functionrecover(bytes32 hash, bytesmemory signature) internalpurereturns (address) {
(address recovered, RecoverError error, bytes32errorArg) = tryRecover(hash, signature);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*/functiontryRecover(bytes32 hash, bytes32 r, bytes32 vs) internalpurereturns (address, RecoverError, bytes32) {
unchecked {
bytes32 s = vs &bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
// We do not check for an overflow here since the shift operation results in 0 or 1.uint8 v =uint8((uint256(vs) >>255) +27);
return tryRecover(hash, v, r, s);
}
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*/functionrecover(bytes32 hash, bytes32 r, bytes32 vs) internalpurereturns (address) {
(address recovered, RecoverError error, bytes32errorArg) = tryRecover(hash, r, vs);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*/functiontryRecover(bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internalpurereturns (address, RecoverError, bytes32) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most// signatures from current libraries generate a unique signature with an s-value in the lower half order.//// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept// these malleable signatures as well.if (uint256(s) >0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS, s);
}
// If the signature is valid (and not malleable), return the signer addressaddress signer =ecrecover(hash, v, r, s);
if (signer ==address(0)) {
return (address(0), RecoverError.InvalidSignature, bytes32(0));
}
return (signer, RecoverError.NoError, bytes32(0));
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/functionrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internalpurereturns (address) {
(address recovered, RecoverError error, bytes32errorArg) = tryRecover(hash, v, r, s);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
*/function_throwError(RecoverError error, bytes32 errorArg) privatepure{
if (error == RecoverError.NoError) {
return; // no error: do nothing
} elseif (error == RecoverError.InvalidSignature) {
revert ECDSAInvalidSignature();
} elseif (error == RecoverError.InvalidSignatureLength) {
revert ECDSAInvalidSignatureLength(uint256(errorArg));
} elseif (error == RecoverError.InvalidSignatureS) {
revert ECDSAInvalidSignatureS(errorArg);
}
}
}
Contract Source Code
File 6 of 28: ERC721.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;/// @notice Modern, minimalist, and gas efficient ERC-721 implementation./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)abstractcontractERC721{
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/eventTransfer(addressindexedfrom, addressindexed to, uint256indexed id);
eventApproval(addressindexed owner, addressindexed spender, uint256indexed id);
eventApprovalForAll(addressindexed owner, addressindexed operator, bool approved);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE/LOGIC
//////////////////////////////////////////////////////////////*/stringpublic name;
stringpublic symbol;
functiontokenURI(uint256 id) publicviewvirtualreturns (stringmemory);
/*//////////////////////////////////////////////////////////////
ERC721 BALANCE/OWNER STORAGE
//////////////////////////////////////////////////////////////*/mapping(uint256=>address) internal _ownerOf;
mapping(address=>uint256) internal _balanceOf;
functionownerOf(uint256 id) publicviewvirtualreturns (address owner) {
require((owner = _ownerOf[id]) !=address(0), "NOT_MINTED");
}
functionbalanceOf(address owner) publicviewvirtualreturns (uint256) {
require(owner !=address(0), "ZERO_ADDRESS");
return _balanceOf[owner];
}
/*//////////////////////////////////////////////////////////////
ERC721 APPROVAL STORAGE
//////////////////////////////////////////////////////////////*/mapping(uint256=>address) public getApproved;
mapping(address=>mapping(address=>bool)) public isApprovedForAll;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/constructor(stringmemory _name, stringmemory _symbol) {
name = _name;
symbol = _symbol;
}
/*//////////////////////////////////////////////////////////////
ERC721 LOGIC
//////////////////////////////////////////////////////////////*/functionapprove(address spender, uint256 id) publicvirtual{
address owner = _ownerOf[id];
require(msg.sender== owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");
getApproved[id] = spender;
emit Approval(owner, spender, id);
}
functionsetApprovalForAll(address operator, bool approved) publicvirtual{
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
functiontransferFrom(addressfrom,
address to,
uint256 id
) publicvirtual{
require(from== _ownerOf[id], "WRONG_FROM");
require(to !=address(0), "INVALID_RECIPIENT");
require(
msg.sender==from|| isApprovedForAll[from][msg.sender] ||msg.sender== getApproved[id],
"NOT_AUTHORIZED"
);
// Underflow of the sender's balance is impossible because we check for// ownership above and the recipient's balance can't realistically overflow.unchecked {
_balanceOf[from]--;
_balanceOf[to]++;
}
_ownerOf[id] = to;
delete getApproved[id];
emit Transfer(from, to, id);
}
functionsafeTransferFrom(addressfrom,
address to,
uint256 id
) publicvirtual{
transferFrom(from, to, id);
require(
to.code.length==0||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
functionsafeTransferFrom(addressfrom,
address to,
uint256 id,
bytescalldata data
) publicvirtual{
transferFrom(from, to, id);
require(
to.code.length==0||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
/*//////////////////////////////////////////////////////////////
ERC165 LOGIC
//////////////////////////////////////////////////////////////*/functionsupportsInterface(bytes4 interfaceId) publicviewvirtualreturns (bool) {
return
interfaceId ==0x01ffc9a7||// ERC165 Interface ID for ERC165
interfaceId ==0x80ac58cd||// ERC165 Interface ID for ERC721
interfaceId ==0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/function_mint(address to, uint256 id) internalvirtual{
require(to !=address(0), "INVALID_RECIPIENT");
require(_ownerOf[id] ==address(0), "ALREADY_MINTED");
// Counter overflow is incredibly unrealistic.unchecked {
_balanceOf[to]++;
}
_ownerOf[id] = to;
emit Transfer(address(0), to, id);
}
function_burn(uint256 id) internalvirtual{
address owner = _ownerOf[id];
require(owner !=address(0), "NOT_MINTED");
// Ownership check above ensures no underflow.unchecked {
_balanceOf[owner]--;
}
delete _ownerOf[id];
delete getApproved[id];
emit Transfer(owner, address(0), id);
}
/*//////////////////////////////////////////////////////////////
INTERNAL SAFE MINT LOGIC
//////////////////////////////////////////////////////////////*/function_safeMint(address to, uint256 id) internalvirtual{
_mint(to, id);
require(
to.code.length==0||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function_safeMint(address to,
uint256 id,
bytesmemory data
) internalvirtual{
_mint(to, id);
require(
to.code.length==0||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
}
/// @notice A generic interface for a contract which properly accepts ERC721 tokens./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)abstractcontractERC721TokenReceiver{
functiononERC721Received(address,
address,
uint256,
bytescalldata) externalvirtualreturns (bytes4) {
return ERC721TokenReceiver.onERC721Received.selector;
}
}
Contract Source Code
File 7 of 28: IERC1271.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1271.sol)pragmasolidity ^0.8.20;/**
* @dev Interface of the ERC1271 standard signature validation method for
* contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
*/interfaceIERC1271{
/**
* @dev Should return whether the signature provided is valid for the provided data
* @param hash Hash of the data to be signed
* @param signature Signature byte array associated with _data
*/functionisValidSignature(bytes32 hash, bytesmemory signature) externalviewreturns (bytes4 magicValue);
}
Contract Source Code
File 8 of 28: IErrors.sol
pragmasolidity ^0.8.22;/// @dev library of common errors across our Mint + Factory contractslibraryIErrors{
// Factory ErrorserrorInvalidSignature();
errorSignatureInvalidated();
errorSignatureUsed();
errorInvalidMintType();
errorInvalidZeroAddress();
errorInvalidContractAddress();
errorNotCollectionCreator();
errorCreationFailed();
errorContractExists();
// Minting ErrorserrorIncorrectETHAmount(uint256 sent, uint256 expected);
errorTokenDoesNotExist(uint256 tokenId);
errorOutOfSupply();
errorFailedToSendEth();
errorMintingNotStarted();
errorMintingClosed();
errorOverClaimLimit();
errorInvalidMintQuantity();
}
Contract Source Code
File 9 of 28: IMintFactory.sol
pragmasolidity ^0.8.22;import"./ISharedConstructs.sol";
/// @dev Shared interface for the Mint factory.interfaceIMintFactory{
functiongetMintingFee(address addr) externalviewreturns (MintingFee memory);
}
/// @dev Shared interface for all Mints.interfaceIMint{
functionmint(address to, uint256 quantity) externalpayable;
functionmintWithComment(address to, uint256 quantity, stringcalldata comment) externalpayable;
functioninitialize(CollectionCreationRequest memory request, address _mintingContract) external;
}
Contract Source Code
File 10 of 28: ISharedConstructs.sol
// SPDX-License-Identifier: UNLICENSEDpragmasolidity ^0.8.22;/// @dev Minting Fee for a given contractstructMintingFee {
bool hasOverride;
address addr;
uint256 fee;
}
structRoyalty {
address to;
uint256 amt; // amt in ETH
}
/// @dev Collection request struct which defines a common set of fields needed to create a new NFT collection.structCollectionCreationRequest {
address creator;
// Collection Informationstring name;
string description;
string symbol;
string image;
string animation_url;
string mintType;
//TODO: support collection attributes?// Claim Conditionsuint128 maxSupply;
uint128 maxPerWallet;
uint256 cost;
// Start + End Datesuint256 startTime;
uint256 endTime;
// royalties
Royalty[] royalties;
uint256 nonce; // Nonce added to support duplicate generations
}
Contract Source Code
File 11 of 28: Initializable.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)pragmasolidity ^0.8.20;/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/abstractcontractInitializable{
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/structInitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))bytes32privateconstant INITIALIZABLE_STORAGE =0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/errorInvalidInitialization();
/**
* @dev The contract is not initializing.
*/errorNotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/eventInitialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/modifierinitializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloadsbool isTopLevelCall =!$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:// - initialSetup: the contract is not in the initializing state and no previous version was// initialized// - construction: the contract is initialized at version 1 (no reininitialization) and the// current contract is just being deployedbool initialSetup = initialized ==0&& isTopLevelCall;
bool construction = initialized ==1&&address(this).code.length==0;
if (!initialSetup &&!construction) {
revert InvalidInitialization();
}
$._initialized =1;
if (isTopLevelCall) {
$._initializing =true;
}
_;
if (isTopLevelCall) {
$._initializing =false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/modifierreinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing =true;
_;
$._initializing =false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/modifieronlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/function_checkInitializing() internalviewvirtual{
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/function_disableInitializers() internalvirtual{
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized !=type(uint64).max) {
$._initialized =type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/function_getInitializedVersion() internalviewreturns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/function_isInitializing() internalviewreturns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/// solhint-disable-next-line var-name-mixedcasefunction_getInitializableStorage() privatepurereturns (InitializableStorage storage $) {
assembly {
$.slot:= INITIALIZABLE_STORAGE
}
}
}
Contract Source Code
File 12 of 28: Json.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.22;import {LibString} from"solmate/utils/LibString.sol";
/**
* Credit goes to emo.eth / OpenSea, extracted portions of their JSON library for building the OpenEdition
* JSON metadata.
* ref: https://github.com/ProjectOpenSea/shipyard-core/blob/main/src/onchain/json.sol
*//**
* @title JSON
* @author emo.eth
* @notice TODO: overrides for common types that automatically stringify
*/libraryJson{
stringprivateconstant NULL ="";
usingLibStringforstring;
/**
* @notice enclose a string in {braces}
* Note: does not escape quotes in value
* @param value string to enclose in braces
* @return string of {value}
*/functionobject(stringmemory value) internalpurereturns (stringmemory) {
returnstring.concat("{", value, "}");
}
/**
* @notice enclose a string in [brackets]
* Note: does not escape quotes in value
* @param value string to enclose in brackets
* @return string of [value]
*/functionarray(stringmemory value) internalpurereturns (stringmemory) {
returnstring.concat("[", value, "]");
}
/**
* @notice enclose name and value with quotes, and place a colon "between":"them".
* Note: escapes quotes in name and value
* @param name name of property
* @param value value of property
* @return string of "name":"value"
*/functionproperty(stringmemory name, stringmemory value) internalpurereturns (stringmemory) {
returnstring.concat('"', escapeJSON(name, false), '":"', escapeJSON(value, false), '"');
}
functionintProperty(stringmemory name, stringmemory value) internalpurereturns (stringmemory) {
returnstring.concat('"', escapeJSON(name, false), '":', escapeJSON(value, false), "");
}
/**
* @notice enclose name with quotes, but not rawValue, and place a colon "between":them
* Note: escapes quotes in name, but not value (which may itself be a JSON object, array, etc)
* @param name name of property
* @param rawValue raw value of property, which will not be enclosed in quotes
* @return string of "name":value
*/functionrawProperty(stringmemory name, stringmemory rawValue) internalpurereturns (stringmemory) {
returnstring.concat('"', escapeJSON(name, false), '":', rawValue);
}
/**
* @notice comma-join an array of properties and {"enclose":"them","in":"braces"}
* Note: does not escape quotes in properties, as it assumes they are already escaped
* @param properties array of '"name":"value"' properties to join
* @return string of {"name":"value","name":"value",...}
*/functionobjectOf(string[] memory properties) internalpurereturns (stringmemory) {
return object(_commaJoin(properties));
}
/**
* @notice comma-join an array of strings
* @param values array of strings to join
* @return string of value,value,...
*/function_commaJoin(string[] memory values) internalpurereturns (stringmemory) {
return _join(values, ",");
}
/**
* @notice join an array of strings with a specified separator
* @param values array of strings to join
* @param separator separator to join with
* @return string of value<separator>value<separator>...
*/function_join(string[] memory values, stringmemory separator) internalpurereturns (stringmemory) {
if (values.length==0) {
return NULL;
}
stringmemory result = values[0];
for (uint256 i =1; i < values.length; ++i) {
result =string.concat(result, separator, values[i]);
}
return result;
}
/**
* @dev Extracted from solady/utils/LibString.sol
*/functionescapeJSON(stringmemory s, bool addDoubleQuotes) internalpurereturns (stringmemory result) {
/// @solidity memory-safe-assemblyassembly {
let end :=add(s, mload(s))
result :=add(mload(0x40), 0x20)
if addDoubleQuotes {
mstore8(result, 34)
result :=add(1, result)
}
// Store "\\u0000" in scratch space.// Store "0123456789abcdef" in scratch space.// Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`.// into the scratch space.mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672)
// Bitmask for detecting `["\"","\\"]`.let e :=or(shl(0x22, 1), shl(0x5c, 1))
for {} iszero(eq(s, end)) {} {
s :=add(s, 1)
let c :=and(mload(s), 0xff)
ifiszero(lt(c, 0x20)) {
ifiszero(and(shl(c, 1), e)) {
// Not in `["\"","\\"]`.mstore8(result, c)
result :=add(result, 1)
continue
}
mstore8(result, 0x5c) // "\\".mstore8(add(result, 1), c)
result :=add(result, 2)
continue
}
ifiszero(and(shl(c, 1), 0x3700)) {
// Not in `["\b","\t","\n","\f","\d"]`.mstore8(0x1d, mload(shr(4, c))) // Hex value.mstore8(0x1e, mload(and(c, 15))) // Hex value.mstore(result, mload(0x19)) // "\\u00XX".
result :=add(result, 6)
continue
}
mstore8(result, 0x5c) // "\\".mstore8(add(result, 1), mload(add(c, 8)))
result :=add(result, 2)
}
if addDoubleQuotes {
mstore8(result, 34)
result :=add(1, result)
}
let last := result
mstore(last, 0) // Zeroize the slot after the string.
result :=mload(0x40)
mstore(result, sub(last, add(result, 0x20))) // Store the length.mstore(0x40, add(last, 0x20)) // Allocate the memory.
}
}
}
Contract Source Code
File 13 of 28: LibString.sol
// SPDX-License-Identifier: MITpragmasolidity >=0.8.0;/// @notice Efficient library for creating string representations of integers./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol)/// @author Modified from Solady (https://github.com/Vectorized/solady/blob/main/src/utils/LibString.sol)libraryLibString{
functiontoString(int256 value) internalpurereturns (stringmemory str) {
if (value >=0) return toString(uint256(value));
unchecked {
str = toString(uint256(-value));
/// @solidity memory-safe-assemblyassembly {
// Note: This is only safe because we over-allocate memory// and write the string from right to left in toString(uint256),// and thus can be sure that sub(str, 1) is an unused memory location.let length :=mload(str) // Load the string length.// Put the - character at the start of the string contents.mstore(str, 45) // 45 is the ASCII code for the - character.
str :=sub(str, 1) // Move back the string pointer by a byte.mstore(str, add(length, 1)) // Update the string length.
}
}
}
functiontoString(uint256 value) internalpurereturns (stringmemory str) {
/// @solidity memory-safe-assemblyassembly {
// The maximum value of a uint256 contains 78 digits (1 byte per digit), but we allocate 160 bytes// to keep the free memory pointer word aligned. We'll need 1 word for the length, 1 word for the// trailing zeros padding, and 3 other words for a max of 78 digits. In total: 5 * 32 = 160 bytes.let newFreeMemoryPointer :=add(mload(0x40), 160)
// Update the free memory pointer to avoid overriding our string.mstore(0x40, newFreeMemoryPointer)
// Assign str to the end of the zone of newly allocated memory.
str :=sub(newFreeMemoryPointer, 32)
// Clean the last word of memory it may not be overwritten.mstore(str, 0)
// Cache the end of the memory to calculate the length later.let end := str
// We write the string from rightmost digit to leftmost digit.// The following is essentially a do-while loop that also handles the zero case.// prettier-ignorefor { let temp := value } 1 {} {
// Move the pointer 1 byte to the left.
str :=sub(str, 1)
// Write the character to the pointer.// The ASCII index of the '0' character is 48.mstore8(str, add(48, mod(temp, 10)))
// Keep dividing temp until zero.
temp :=div(temp, 10)
// prettier-ignoreifiszero(temp) { break }
}
// Compute and cache the final total length of the string.let length :=sub(end, str)
// Move the pointer 32 bytes leftwards to make room for the length.
str :=sub(str, 32)
// Store the string's length at the start of memory allocated for our string.mstore(str, length)
}
}
}
Contract Source Code
File 14 of 28: Math.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)pragmasolidity ^0.8.20;/**
* @dev Standard math utilities missing in the Solidity language.
*/libraryMath{
/**
* @dev Muldiv operation overflow.
*/errorMathOverflowedMulDiv();
enumRounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/functiontryAdd(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/functiontrySub(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/functiontryMul(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the// benefit is lost if 'b' is also tested.// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522if (a ==0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/functiontryDiv(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
unchecked {
if (b ==0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/functiontryMod(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
unchecked {
if (b ==0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/functionmax(uint256 a, uint256 b) internalpurereturns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/functionmin(uint256 a, uint256 b) internalpurereturns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/functionaverage(uint256 a, uint256 b) internalpurereturns (uint256) {
// (a + b) / 2 can overflow.return (a & b) + (a ^ b) /2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/functionceilDiv(uint256 a, uint256 b) internalpurereturns (uint256) {
if (b ==0) {
// Guarantee the same behavior as in a regular Solidity division.return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.return a ==0 ? 0 : (a -1) / b +1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/functionmulDiv(uint256 x, uint256 y, uint256 denominator) internalpurereturns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use// 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.uint256 prod0 = x * y; // Least significant 256 bits of the productuint256 prod1; // Most significant 256 bits of the productassembly {
let mm :=mulmod(x, y, not(0))
prod1 :=sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.if (prod1 ==0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.// The surrounding unchecked block does not change this fact.// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////// 512 by 256 division.///////////////////////////////////////////////// Make division exact by subtracting the remainder from [prod1 prod0].uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder :=mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 :=sub(prod1, gt(remainder, prod0))
prod0 :=sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.uint256 twos = denominator & (0- denominator);
assembly {
// Divide denominator by twos.
denominator :=div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 :=div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos :=add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for// four bits. That is, denominator * inv = 1 mod 2^4.uint256 inverse = (3* denominator) ^2;
// Use the 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.
inverse *=2- denominator * inverse; // inverse mod 2^8
inverse *=2- denominator * inverse; // inverse mod 2^16
inverse *=2- denominator * inverse; // inverse mod 2^32
inverse *=2- denominator * inverse; // inverse mod 2^64
inverse *=2- denominator * inverse; // inverse mod 2^128
inverse *=2- denominator * inverse; // inverse mod 2^256// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/functionmulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internalpurereturns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) &&mulmod(x, y, denominator) >0) {
result +=1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/functionsqrt(uint256 a) internalpurereturns (uint256) {
if (a ==0) {
return0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.//// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.//// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`//// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.uint256 result =1<< (log2(a) >>1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision// into the expected uint128 result.unchecked {
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
result = (result + a / result) >>1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/functionsqrt(uint256 a, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/functionlog2(uint256 value) internalpurereturns (uint256) {
uint256 result =0;
unchecked {
if (value >>128>0) {
value >>=128;
result +=128;
}
if (value >>64>0) {
value >>=64;
result +=64;
}
if (value >>32>0) {
value >>=32;
result +=32;
}
if (value >>16>0) {
value >>=16;
result +=16;
}
if (value >>8>0) {
value >>=8;
result +=8;
}
if (value >>4>0) {
value >>=4;
result +=4;
}
if (value >>2>0) {
value >>=2;
result +=2;
}
if (value >>1>0) {
result +=1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/functionlog2(uint256 value, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result =log2(value);
return result + (unsignedRoundsUp(rounding) &&1<< result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/functionlog10(uint256 value) internalpurereturns (uint256) {
uint256 result =0;
unchecked {
if (value >=10**64) {
value /=10**64;
result +=64;
}
if (value >=10**32) {
value /=10**32;
result +=32;
}
if (value >=10**16) {
value /=10**16;
result +=16;
}
if (value >=10**8) {
value /=10**8;
result +=8;
}
if (value >=10**4) {
value /=10**4;
result +=4;
}
if (value >=10**2) {
value /=10**2;
result +=2;
}
if (value >=10**1) {
result +=1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/functionlog10(uint256 value, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) &&10** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/functionlog256(uint256 value) internalpurereturns (uint256) {
uint256 result =0;
unchecked {
if (value >>128>0) {
value >>=128;
result +=16;
}
if (value >>64>0) {
value >>=64;
result +=8;
}
if (value >>32>0) {
value >>=32;
result +=4;
}
if (value >>16>0) {
value >>=16;
result +=2;
}
if (value >>8>0) {
result +=1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/functionlog256(uint256 value, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) &&1<< (result <<3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/functionunsignedRoundsUp(Rounding rounding) internalpurereturns (bool) {
returnuint8(rounding) %2==1;
}
}
Contract Source Code
File 15 of 28: MintFactory.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.22;import"../mint-contracts/OpenEdition721Mint.sol";
import {CollectionCreationRequest} from"../interfaces/ISharedConstructs.sol";
import {MintUtil} from"../../utils/MintUtil.sol";
import"../interfaces/IErrors.sol";
import"@openzeppelin/contracts/proxy/Clones.sol";
import {Pausable} from"@openzeppelin/contracts/utils/Pausable.sol";
import {Ownable2Step, Ownable} from"@openzeppelin/contracts/access/Ownable2Step.sol";
import {ReentrancyGuard} from"@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {SignatureChecker} from"@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
/*
████████╗ ██████╗ ██╗ ██╗███████╗███╗ ██╗███████╗ ██████╗ ██████╗ ██████╗ ███████╗
╚══██╔══╝██╔═══██╗██║ ██╔╝██╔════╝████╗ ██║██╔════╝██╔═══██╗██╔══██╗██╔════╝ ██╔════╝
██║ ██║ ██║█████╔╝ █████╗ ██╔██╗ ██║█████╗ ██║ ██║██████╔╝██║ ███╗█████╗
██║ ██║ ██║██╔═██╗ ██╔══╝ ██║╚██╗██║██╔══╝ ██║ ██║██╔══██╗██║ ██║██╔══╝
██║ ╚██████╔╝██║ ██╗███████╗██║ ╚████║██║ ╚██████╔╝██║ ██║╚██████╔╝███████╗
╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝
*//// @title Generic Mint Factory which will be used to create premint NFT contracts as cheap as possible./// @author Coinbase - @polak.ethcontractMintFactoryisIMintFactory, Pausable, Ownable2Step, ReentrancyGuard{
/// @dev Store a mapping from sig -> address to prevent duplicates / collisionsmapping(address=>bool) public contracts;
/// @dev Allow a creator to cancel their signature.mapping(bytes=>bool) public cancelledSignatures;
/// @dev Track which signatures have been used.mapping(bytes=>bool) public usedSignatures;
/// @dev The default minting fee if there is not an override in feeOverride
MintingFee public mintingFee;
/// @dev Fee overrides so we can change either the amount or destination of the fees.mapping(address=> MintingFee) public feeOverrides;
/// @dev Mapping of our mint implementations ready to be cloned.mapping(string=>address) public mintImplementation;
/// @dev EIP-712 Domain definitionbytes32constant DOMAIN_TYPEHASH =keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
/// @dev EIP-712 CollectionCreation request typebytes32constant COLLECTION_CREATION_REQUEST_TYPEHASH =keccak256(
"CollectionCreationRequest(address creator,string name,string description,string symbol,string image,string animation_url,string mintType,uint128 maxSupply,uint128 maxPerWallet,uint256 cost,uint256 startTime,uint256 endTime,Royalty[] royalties,uint256 nonce)Royalty(address to,uint256 amt)"
);
bytes32constant ROYALTIES_REQUEST_TYPEHASH =keccak256("Royalty(address to,uint256 amt)");
/// @dev EIP-712 Domain Separator, initialized in the constructor.bytes32publicimmutable domainSeparator;
/// @dev Event That is Emitted when a contract is createdeventContractCreated(addressindexed contractAddress, addressindexed minter);
eventSignatureInvalidated(bytesindexed signature, addressindexed minter);
/// @dev Default constructor which takes an owner and a location for platform royalties to be sent.constructor(address initialOwner, address platformFeeAddress) Ownable(initialOwner) {
domainSeparator =keccak256(
abi.encode(
DOMAIN_TYPEHASH,
keccak256(bytes("MintFactory")), // Namekeccak256(bytes("1")), // Versionblock.chainid,
address(this)
)
);
// Set default fee of 0.0005 eth / mint
mintingFee = MintingFee({hasOverride: false, addr: platformFeeAddress, fee: 0.0001ether});
// Default contracts
mintImplementation["OPEN_EDITION_721"] =address(new OpenEdition721Mint());
}
/**
* ------------ External ------------
*//// @dev Creates a new collection based on the Creators initial vision. Leverages signature validation/// to ensure a collection can only be created in the same shape as the creators minting signature.functioncreateCollection(
CollectionCreationRequest calldata request,
bytesmemory signature,
uint256 mintQuantity,
stringcalldata comment
) externalpayablenonReentrantwhenNotPausedreturns (address) {
// Check if the contract is already deployed and call mintif (contracts[getMintingAddress(request)]) {
address existingMintAddr = getMintingAddress(request);
IMint existingMint = IMint(existingMintAddr);
existingMint.mintWithComment{value: msg.value}(msg.sender, mintQuantity, comment);
return existingMintAddr;
}
// Check valid signatureif (!verifySignature(request, signature)) {
revert IErrors.InvalidSignature();
}
MintUtil.validateCollectionCreationRequest(request);
// Build and deploy the new Mint Contractbytes32 salt = getSalt(request);
address contractAddr = _getMintImplementationAddr(request.mintType);
address mintAddress = Clones.cloneDeterministic(contractAddr, salt);
// Mark the signature + contract as deployed, so it cannot be used again with a new version of the minting contract.
contracts[mintAddress] =true;
usedSignatures[signature] =true;
// Initialize the Mint with the creators specifications
IMint newMint = IMint(mintAddress);
newMint.initialize(request, address(this));
if (mintQuantity >0) {
// Mint will confirm the sender is only sending the exact ETH amount
newMint.mintWithComment{value: msg.value}(msg.sender, mintQuantity, comment);
}
// Emit contract creationemit ContractCreated(mintAddress, msg.sender);
return mintAddress;
}
/// @dev Allows a creator to cancel a signature if they do not want a collection to be minted./// @param signature The signature to be cancelledfunctioncancelSignature(CollectionCreationRequest calldata request, bytescalldata signature) external{
if (!verifySignature(request, signature)) {
revert IErrors.InvalidSignature();
}
// Ensure the caller is the creator so arbitrary wallets cannot cancel other creators signatures.if (request.creator !=msg.sender) {
revert IErrors.NotCollectionCreator();
}
// Emit an event which will let our BE know to cancel signatures.emit SignatureInvalidated(signature, msg.sender);
cancelledSignatures[signature] =true;
}
/// @dev support a mint function at the Factory layer as a convenience./// @param contractAddress The address of the contract you want to mint./// @param to The address the mint should be sent to./// @param quantity of NFTs you want to mint.functionmint(address contractAddress, address to, uint256 quantity, stringcalldata comment) externalpayable{
// Only allow minting from contracts we have deployedif (!contracts[contractAddress]) {
revert IErrors.InvalidContractAddress();
}
IMint newMint = IMint(contractAddress);
newMint.mintWithComment{value: msg.value}(to, quantity, comment);
}
/// @dev Get the minting fee for a collection, will return an override or the default./// @notice This will return a default fee if not overridden./// @param contractAddress you want to get a fee for.functiongetMintingFee(address contractAddress) externalviewreturns (MintingFee memory) {
MintingFee memory feeOverride = feeOverrides[contractAddress];
if (feeOverride.hasOverride) {
return feeOverride;
}
// Use default feereturn mintingFee;
}
/**
* ------------ OnlyOwner ------------
*//// @dev Allows an owner to set the minting contract for an given type. This will both new contracts/// & upgrades to existing mint types.functionsetMintContract(stringcalldata contractType, address addr) externalonlyOwner{
mintImplementation[contractType] =address(addr);
}
/// @dev Allows an Owner to update the default minting fee for all contracts.functionsetMintingFee(MintingFee calldata fee) externalonlyOwner{
mintingFee = fee;
}
/// @dev Allows an owner to override the fee for a specific contract addressfunctionsetMintingFeeOverride(address addr, MintingFee calldata fee) externalonlyOwner{
feeOverrides[addr] = fee;
}
/// @dev Allows an owner to pause a contract.functionpause() externalonlyOwnerwhenNotPaused{
_pause();
}
/// @dev Allows an owner to unpause a paused contract.functionunpause() externalonlyOwnerwhenPaused{
_unpause();
}
/**
* ------------ Public ------------
*////@dev Helper method to get the bytes needed to be signed for an account to create a free mint. This implements/// EIP-712 signatures so a creator can see exactly what their signing.///@param request Collection creation requestfunctiongetBytesToSign(CollectionCreationRequest memory request) publicviewreturns (bytes32) {
returnkeccak256(abi.encodePacked("\x19\x01", domainSeparator, getItemHash(request)));
}
///@dev Helped function for hashing the creation struct. Leaving as public as I could see some value for/// the frontend to call.functiongetItemHash(CollectionCreationRequest memory request) publicpurereturns (bytes32) {
bytes32[] memory royaltyHashes =newbytes32[](request.royalties.length);
for (uint256 i =0; i < request.royalties.length; i++) {
royaltyHashes[i] =keccak256(abi.encode(ROYALTIES_REQUEST_TYPEHASH, request.royalties[i].to, request.royalties[i].amt));
}
// Needed to break up the hash into two parts to avoid nested depth issues (no IR plz)bytesmemory pt1 =abi.encode(
COLLECTION_CREATION_REQUEST_TYPEHASH,
request.creator,
keccak256(bytes(request.name)),
keccak256(bytes(request.description)),
keccak256(bytes(request.symbol)),
keccak256(bytes(request.image)),
keccak256(bytes(request.animation_url)),
keccak256(bytes(request.mintType))
);
returnkeccak256(
abi.encodePacked(
pt1,
abi.encode(
request.maxSupply,
request.maxPerWallet,
request.cost,
request.startTime,
request.endTime,
keccak256(abi.encodePacked(royaltyHashes)),
request.nonce
)
)
);
}
/// @dev Gets the predicted minting address for a creation payload.functiongetMintingAddress(CollectionCreationRequest memory request) publicviewreturns (address) {
bytes32 salt = getSalt(request);
address implementation = _getMintImplementationAddr(request.mintType);
return Clones.predictDeterministicAddress(implementation, salt, address(this));
}
/// @dev Verifies a signature is valid, implements EIP-712/// @notice There is a reentrancy risk with isValidSignatureNow as it makes external calls for smart contracts.functionverifySignature(CollectionCreationRequest memory request, bytesmemory signature)
publicviewreturns (bool)
{
// Check if the signature was invalidated.if (cancelledSignatures[signature]) {
revert IErrors.SignatureInvalidated();
}
// Check if the signature was already used.if (usedSignatures[signature]) {
revert IErrors.SignatureUsed();
}
bytes32 digest = getBytesToSign(request);
return SignatureChecker.isValidSignatureNow(request.creator, digest, signature);
}
/// @dev Gets a contract creation salt to ensure contracts are unique based on their request. There is a/// nonce that is appended to the request to ensure uniqueness.functiongetSalt(CollectionCreationRequest memory request) publicviewreturns (bytes32) {
// WARNING: Write explicit ordering here and ensure solidity includes all values. This salt needs to be unique// otherwise there will be a contract creation collision.returnkeccak256(abi.encode(request, address(this)));
}
/**
* ------------ Internal ------------
*//// @dev Gets the mint implementation address to be used by Clonefunction_getMintImplementationAddr(stringmemory mintType) internalviewreturns (address) {
address mintImpl = mintImplementation[mintType];
if (mintImpl ==address(0)) {
revert IErrors.InvalidMintType();
}
return mintImpl;
}
}
Contract Source Code
File 16 of 28: MintUtil.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.22;import"@openzeppelin/contracts/utils/Strings.sol";
import"@openzeppelin/contracts/utils/Base64.sol";
import"../tokenforge/interfaces/ISharedConstructs.sol";
import"./Json.sol";
import {Solarray} from"solarray/Solarray.sol";
/// @title Generic Mint utility for common uses across many different mint types./// @author polak.ethlibraryMintUtil{
errorInvalidCollectionRequest(string message);
functioncontractURI(CollectionCreationRequest memory metadata, uint256 cost, bool encode)
internalpurereturns (stringmemory)
{
stringmemory jsonMetadata = Json.objectOf(
Solarray.strings(
Json.property("name", metadata.name),
Json.property("description", metadata.description),
Json.property("symbol", metadata.symbol),
Json.property("image", metadata.image),
Json.property("animation_url", metadata.animation_url),
Json.rawProperty("mintConfig", _mintConfig(metadata, cost))
)
);
if (encode) {
return encodeJsonToBase64(jsonMetadata);
} else {
return jsonMetadata;
}
}
/// @dev MintConfig as defined by Reservoirs standard/// @notice See Reference: https://github.com/reservoirprotocol/indexer/tree/main/packages/mint-interfacefunction_mintConfig(CollectionCreationRequest memory metadata, uint256 cost)
internalpurereturns (stringmemory)
{
// Construct the mintConfig JSONreturn Json.objectOf(
Solarray.strings(
Json.intProperty("maxSupply", Strings.toString(metadata.maxSupply)),
Json.rawProperty("phases", Json.array(_encodePhases(metadata, cost)))
)
);
}
/// @dev Formats for an OpenEdition which is an auto-incrementing name (e.g. NFT #1, NFT #2 ...).functiongetOpenEditionUri(CollectionCreationRequest memory metadata, uint256 tokenId, bool encode)
internalpurereturns (stringmemory)
{
// Generates a name with edition .. e.g. NFT => NFT #1029stringmemory nameWithTokenId =string(abi.encodePacked(metadata.name, " #", Strings.toString(tokenId)));
stringmemory jsonMetadata = Json.objectOf(
Solarray.strings(
Json.property("name", nameWithTokenId),
Json.property("description", metadata.description),
Json.property("symbol", metadata.symbol),
Json.property("image", metadata.image),
Json.property("animation_url", metadata.animation_url)
)
);
if (encode) {
return encodeJsonToBase64(jsonMetadata);
} else {
return jsonMetadata;
}
}
/// @dev Phases are required for the Reservoir minting integration. Reservoir will read this configuration/// from the ContractURI() function.function_encodePhases(CollectionCreationRequest memory metadata, uint256 cost)
internalpurereturns (stringmemory)
{
stringmemory maxPerWalletStr = Strings.toString(metadata.maxPerWallet);
stringmemory addrParam ='{"name": "recipient","abiType": "address","kind": "RECIPIENT"}';
stringmemory qtyParam ='{"name": "quantity", "abiType": "uint256", "kind": "QUANTITY"}';
// Params used to call the mint functionstringmemory params =string(abi.encodePacked(addrParam, ",", qtyParam));
// Define the method that needs to be calledstringmemory txnData = Json.objectOf(
Solarray.strings(Json.property("method", "0x40c10f19"), Json.rawProperty("params", Json.array(params)))
);
// Mint phases (we only support one phase right now)return Json.objectOf(
Solarray.strings(
Json.intProperty("maxMintsPerWallet", maxPerWalletStr),
Json.intProperty("startTime", Strings.toString(metadata.startTime)),
Json.intProperty("endTime", Strings.toString(metadata.endTime)),
Json.intProperty("price", Strings.toString(cost)),
Json.rawProperty("tx", txnData)
)
);
}
functionencodeJsonToBase64(stringmemory str) internalpurereturns (stringmemory) {
returnstring.concat("data:application/json;base64,", Base64.encode(abi.encodePacked(str)));
}
/// @dev Validates the CollectionCreationRequest for common logical errors./// @param metadata The collection creation request to validate.functionvalidateCollectionCreationRequest(CollectionCreationRequest memory metadata) internalpure{
// Check start and end timesif (metadata.startTime > metadata.endTime && metadata.endTime !=0) {
revert InvalidCollectionRequest("End time must be greater than or equal to start time.");
}
// Check that royalty amounts add up to mint cost.uint256 totalAmt =0;
for (uint256 i =0; i < metadata.royalties.length; i++) {
if (metadata.royalties[i].to ==address(0)) {
revert InvalidCollectionRequest("Invalid Address");
}
totalAmt += metadata.royalties[i].amt;
}
if (totalAmt != metadata.cost) {
revert InvalidCollectionRequest("Total royalties must equal cost");
}
if (metadata.royalties.length>5) {
revert InvalidCollectionRequest("Cannot have more than 5 royalty addresses");
}
// Check for valid max supply and per wallet limitsif (metadata.maxSupply ==0) {
revert InvalidCollectionRequest("Max supply must be greater than zero.");
}
if (metadata.maxPerWallet ==0) {
revert InvalidCollectionRequest("Max per wallet must be greater than zero.");
}
// Check for non-empty essential stringsif (isEmptyString(metadata.name) || isEmptyString(metadata.symbol)) {
revert InvalidCollectionRequest("Name & symbol cannot be empty.");
}
if (isEmptyString(metadata.image) && isEmptyString(metadata.animation_url)) {
revert InvalidCollectionRequest("Must have an image or animation_url.");
}
}
/// @dev Helper function to check if a string is empty./// @param value The string to check./// @return bool Whether the string is empty.functionisEmptyString(stringmemory value) internalpurereturns (bool) {
returnbytes(value).length==0;
}
}
Contract Source Code
File 17 of 28: OpenEdition721Mint.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.22;import {ERC721} from"solmate/tokens/ERC721.sol";
import {Initializable} from"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {Ownable2StepUpgradeable} from"@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
import {ReentrancyGuardUpgradeable} from"@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import {CollectionCreationRequest} from"../interfaces/ISharedConstructs.sol";
import"../interfaces/IErrors.sol";
import {IMintFactory, IMint, Royalty, MintingFee} from"../interfaces/IMintFactory.sol";
import {MintUtil} from"../../utils/MintUtil.sol";
/*
████████╗ ██████╗ ██╗ ██╗███████╗███╗ ██╗███████╗ ██████╗ ██████╗ ██████╗ ███████╗
╚══██╔══╝██╔═══██╗██║ ██╔╝██╔════╝████╗ ██║██╔════╝██╔═══██╗██╔══██╗██╔════╝ ██╔════╝
██║ ██║ ██║█████╔╝ █████╗ ██╔██╗ ██║█████╗ ██║ ██║██████╔╝██║ ███╗█████╗
██║ ██║ ██║██╔═██╗ ██╔══╝ ██║╚██╗██║██╔══╝ ██║ ██║██╔══██╗██║ ██║██╔══╝
██║ ╚██████╔╝██║ ██╗███████╗██║ ╚████║██║ ╚██████╔╝██║ ██║╚██████╔╝███████╗
╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝
*//// @title Open Edition 721 NFT Collection/// @dev NFT Collection which supports an Open Edition style with shared metadata between all tokens./// @author Coinbase - @polak.ethcontractOpenEdition721MintisIMint,
ERC721("", ""),
Initializable,
Ownable2StepUpgradeable,
ReentrancyGuardUpgradeable{
/// @dev Current Token Id matches the token id type of Solmateuint256public currentTokenId;
/// @dev Metadata for the minting contract
CollectionCreationRequest public metadata;
/// @dev Mint Factory reference for feesaddresspublic mintingContract;
/// @dev MintConfigChanged is required for Reservoir Minting ingestion/// @notice See Reference: https://github.com/reservoirprotocol/indexer/tree/main/packages/mint-interfaceeventMintConfigChanged();
/// @dev Withdrawn is emitted if an owner has withdrawn an accidental funds transfer to the contract.eventWithdrawn(addressindexed owner, uint256 amount);
/// @dev CommentEvent is emitted when a creator or minter attaches a comment with their mint.eventTokenForgeMintComment(addressindexed to, uint256 quantity, string comment);
/// @dev Event emitted when a token forge mint occurseventTokenForgeMint(addressindexed to, uint256 quantity);
/// @custom:oz-upgrades-unsafe-allow constructorconstructor() {
_disableInitializers(); // disabled initializers so they cannot be called again.
}
/// @dev Initializer which creates a new NFT collection based on the request./// @notice This is Initializable because we are using clones to create.functioninitialize(CollectionCreationRequest memory request, address mintingContract_) publicinitializer{
if (mintingContract_ ==address(0)) {
revert IErrors.InvalidContractAddress();
}
// Make sure the creator gets ownership of the contract
__Ownable2Step_init();
_transferOwnership(request.creator);
__ReentrancyGuard_init();
// Set super params
name = request.name;
symbol = request.symbol;
// Contract Params
_setMetadata(request);
metadata.mintType = request.mintType; //Only allowed to set on init
mintingContract = mintingContract_;
currentTokenId =1; // Set current to 1 so the first NFT is #1 and not #0emit MintConfigChanged();
}
/**
* ------------ External ------------
*/// @dev standard mint functionfunctionmint(address to, uint256 quantity) externalpayablenonReentrant{
if (quantity ==0) {
revert IErrors.InvalidMintQuantity();
}
// Check we don't exceed max supply, add one since we start at tokenId = 1.if (currentTokenId + quantity > metadata.maxSupply +1) {
revert IErrors.OutOfSupply();
}
// Check the mint has started or ended.if (metadata.startTime >0&& metadata.startTime >block.timestamp) {
revert IErrors.MintingNotStarted();
}
if (metadata.endTime >0&& metadata.endTime <block.timestamp) {
revert IErrors.MintingClosed();
}
// Check if we are over wallet limitsif (balanceOf(to) + quantity > metadata.maxPerWallet) {
revert IErrors.OverClaimLimit();
}
// Check the correct amount of ETH sent.uint256 creatorFee;
uint256 platformFee;
address platformFeeAddr;
(creatorFee, platformFee, platformFeeAddr) = _getFees(quantity);
// Verify the exact amount, don't want to deal with refunding ETH.if (msg.value!= creatorFee + platformFee) {
revert IErrors.IncorrectETHAmount(msg.value, creatorFee + platformFee);
}
// Increment OEfor (uint64 i =0; i < quantity; i++) {
_safeMint(to, currentTokenId++);
}
uint256 paid =0;
paid += platformFee;
// Pay Platform Fee
(bool payPlatformFee,) = platformFeeAddr.call{value: platformFee}("");
if (!payPlatformFee) {
revert IErrors.FailedToSendEth();
}
// Pay Creator Royaltiesfor (uint256 i =0; i < metadata.royalties.length; i++) {
Royalty memory royalty = metadata.royalties[i];
uint256 royaltyAmount = quantity * royalty.amt;
paid += royaltyAmount;
(bool payRoyaltyResult,) = royalty.to.call{value: royaltyAmount}("");
if (!payRoyaltyResult) {
revert IErrors.FailedToSendEth();
}
}
if (paid != creatorFee + platformFee) {
revert IErrors.IncorrectETHAmount(paid, creatorFee + platformFee);
}
emit TokenForgeMint(to, quantity);
}
functionmintWithComment(address to, uint256 quantity, stringcalldata comment) externalpayable{
this.mint{value: msg.value}(to, quantity);
if (bytes(comment).length>0) {
emit TokenForgeMintComment(to, quantity, comment);
}
}
/// @dev Allows an owner to extract a eth balance from the contract.functionwithdraw() externalonlyOwner{
uint256 balance =address(this).balance;
(bool payOwnerResult,) = owner().call{value: balance}("");
if (!payOwnerResult) {
revert IErrors.FailedToSendEth();
}
emit Withdrawn(owner(), balance);
}
/// @dev Allows an owner to update the metadata of the collection./// @notice This will allow and owner to change claim conditions owners should burn collections that should not change.functionsetMetadata(CollectionCreationRequest memory metadata_) externalonlyOwner{
_setMetadata(metadata_);
emit MintConfigChanged();
}
functioncontractURI() externalviewreturns (stringmemory) {
uint256 creatorFee;
uint256 platformFee;
(creatorFee, platformFee,) = _getFees(1);
return MintUtil.contractURI(metadata, creatorFee + platformFee, true);
}
/// @dev get the total minted quantity./// @notice have to offset because we are starting at #1 vs #0functiontotalSupply() externalviewreturns (uint256) {
return currentTokenId -1;
}
/**
* ------------ Public ------------
*/functiontokenURI(uint256 tokenId) publicviewoverride(ERC721) returns (stringmemory) {
if (tokenId > currentTokenId || tokenId ==0) {
revert IErrors.TokenDoesNotExist({tokenId: tokenId});
}
return MintUtil.getOpenEditionUri(metadata, tokenId, true);
}
functiongetMetadata() publicviewreturns (CollectionCreationRequest memory) {
return metadata;
}
/// @dev Gets the total cost to mint this collection./// @notice We call the mint factory to determine platform costs.functioncost(uint256 quantity) publicviewreturns (uint256) {
uint256 creatorFee;
uint256 platformFee;
(creatorFee, platformFee,) = _getFees(quantity);
return creatorFee + platformFee;
}
/**
* ------------ Internal ------------
*//// @dev Gets the fees for this collection./// @notice We call the mint factory to determine platform costs.function_getFees(uint256 quantity) internalviewreturns (uint256, uint256, address) {
IMintFactory mintFactory = IMintFactory(mintingContract);
MintingFee memory mintingFee = mintFactory.getMintingFee(address(this));
uint256 creatorFee = (metadata.cost * quantity);
uint256 platformFee = mintingFee.fee * quantity;
return (creatorFee, platformFee, mintingFee.addr);
}
function_setMetadata(CollectionCreationRequest memory metadata_) internal{
MintUtil.validateCollectionCreationRequest(metadata_);
// Manually copy simple fields
metadata.creator = metadata_.creator;
metadata.name= metadata_.name;
metadata.description = metadata_.description;
metadata.symbol = metadata_.symbol;
metadata.image = metadata_.image;
metadata.animation_url = metadata_.animation_url;
metadata.maxSupply = metadata_.maxSupply;
metadata.maxPerWallet = metadata_.maxPerWallet;
metadata.cost = metadata_.cost;
metadata.startTime = metadata_.startTime;
metadata.endTime = metadata_.endTime;
// Make sure to remove old royaltiesdelete metadata.royalties;
// Manually copy array fieldsfor (uint256 i =0; i < metadata_.royalties.length; i++) {
metadata.royalties.push(Royalty({to: metadata_.royalties[i].to, amt: metadata_.royalties[i].amt}));
}
}
}
Contract Source Code
File 18 of 28: Ownable.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)pragmasolidity ^0.8.20;import {Context} from"../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/abstractcontractOwnableisContext{
addressprivate _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/errorOwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/errorOwnableInvalidOwner(address owner);
eventOwnershipTransferred(addressindexed previousOwner, addressindexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/constructor(address initialOwner) {
if (initialOwner ==address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/modifieronlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/functionowner() publicviewvirtualreturns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/function_checkOwner() internalviewvirtual{
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/functionrenounceOwnership() publicvirtualonlyOwner{
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/functiontransferOwnership(address newOwner) publicvirtualonlyOwner{
if (newOwner ==address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/function_transferOwnership(address newOwner) internalvirtual{
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
Contract Source Code
File 19 of 28: Ownable2Step.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)pragmasolidity ^0.8.20;import {Ownable} from"./Ownable.sol";
/**
* @dev Contract module which provides access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is specified at deployment time in the constructor for `Ownable`. This
* can later be changed with {transferOwnership} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/abstractcontractOwnable2StepisOwnable{
addressprivate _pendingOwner;
eventOwnershipTransferStarted(addressindexed previousOwner, addressindexed newOwner);
/**
* @dev Returns the address of the pending owner.
*/functionpendingOwner() publicviewvirtualreturns (address) {
return _pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*/functiontransferOwnership(address newOwner) publicvirtualoverrideonlyOwner{
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/function_transferOwnership(address newOwner) internalvirtualoverride{
delete _pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/functionacceptOwnership() publicvirtual{
address sender = _msgSender();
if (pendingOwner() != sender) {
revert OwnableUnauthorizedAccount(sender);
}
_transferOwnership(sender);
}
}
Contract Source Code
File 20 of 28: Ownable2StepUpgradeable.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)pragmasolidity ^0.8.20;import {OwnableUpgradeable} from"./OwnableUpgradeable.sol";
import {Initializable} from"../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is specified at deployment time in the constructor for `Ownable`. This
* can later be changed with {transferOwnership} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/abstractcontractOwnable2StepUpgradeableisInitializable, OwnableUpgradeable{
/// @custom:storage-location erc7201:openzeppelin.storage.Ownable2StepstructOwnable2StepStorage {
address _pendingOwner;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable2Step")) - 1)) & ~bytes32(uint256(0xff))bytes32privateconstant Ownable2StepStorageLocation =0x237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c00;
function_getOwnable2StepStorage() privatepurereturns (Ownable2StepStorage storage $) {
assembly {
$.slot:= Ownable2StepStorageLocation
}
}
eventOwnershipTransferStarted(addressindexed previousOwner, addressindexed newOwner);
function__Ownable2Step_init() internalonlyInitializing{
}
function__Ownable2Step_init_unchained() internalonlyInitializing{
}
/**
* @dev Returns the address of the pending owner.
*/functionpendingOwner() publicviewvirtualreturns (address) {
Ownable2StepStorage storage $ = _getOwnable2StepStorage();
return $._pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*/functiontransferOwnership(address newOwner) publicvirtualoverrideonlyOwner{
Ownable2StepStorage storage $ = _getOwnable2StepStorage();
$._pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/function_transferOwnership(address newOwner) internalvirtualoverride{
Ownable2StepStorage storage $ = _getOwnable2StepStorage();
delete $._pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/functionacceptOwnership() publicvirtual{
address sender = _msgSender();
if (pendingOwner() != sender) {
revert OwnableUnauthorizedAccount(sender);
}
_transferOwnership(sender);
}
}
Contract Source Code
File 21 of 28: OwnableUpgradeable.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)pragmasolidity ^0.8.20;import {ContextUpgradeable} from"../utils/ContextUpgradeable.sol";
import {Initializable} from"../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/abstractcontractOwnableUpgradeableisInitializable, ContextUpgradeable{
/// @custom:storage-location erc7201:openzeppelin.storage.OwnablestructOwnableStorage {
address _owner;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff))bytes32privateconstant OwnableStorageLocation =0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;
function_getOwnableStorage() privatepurereturns (OwnableStorage storage $) {
assembly {
$.slot:= OwnableStorageLocation
}
}
/**
* @dev The caller account is not authorized to perform an operation.
*/errorOwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/errorOwnableInvalidOwner(address owner);
eventOwnershipTransferred(addressindexed previousOwner, addressindexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/function__Ownable_init(address initialOwner) internalonlyInitializing{
__Ownable_init_unchained(initialOwner);
}
function__Ownable_init_unchained(address initialOwner) internalonlyInitializing{
if (initialOwner ==address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/modifieronlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/functionowner() publicviewvirtualreturns (address) {
OwnableStorage storage $ = _getOwnableStorage();
return $._owner;
}
/**
* @dev Throws if the sender is not the owner.
*/function_checkOwner() internalviewvirtual{
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/functionrenounceOwnership() publicvirtualonlyOwner{
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/functiontransferOwnership(address newOwner) publicvirtualonlyOwner{
if (newOwner ==address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/function_transferOwnership(address newOwner) internalvirtual{
OwnableStorage storage $ = _getOwnableStorage();
address oldOwner = $._owner;
$._owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
Contract Source Code
File 22 of 28: Pausable.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)pragmasolidity ^0.8.20;import {Context} from"../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/abstractcontractPausableisContext{
boolprivate _paused;
/**
* @dev Emitted when the pause is triggered by `account`.
*/eventPaused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/eventUnpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/errorEnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/errorExpectedPause();
/**
* @dev Initializes the contract in unpaused state.
*/constructor() {
_paused =false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/modifierwhenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/modifierwhenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/functionpaused() publicviewvirtualreturns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/function_requireNotPaused() internalviewvirtual{
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/function_requirePaused() internalviewvirtual{
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/function_pause() internalvirtualwhenNotPaused{
_paused =true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/function_unpause() internalvirtualwhenPaused{
_paused =false;
emit Unpaused(_msgSender());
}
}
Contract Source Code
File 23 of 28: ReentrancyGuard.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)pragmasolidity ^0.8.20;/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/abstractcontractReentrancyGuard{
// Booleans are more expensive than uint256 or any type that takes up a full// word because each write operation emits an extra SLOAD to first read the// slot's contents, replace the bits taken up by the boolean, and then write// back. This is the compiler's defense against contract upgrades and// pointer aliasing, and it cannot be disabled.// The values being non-zero value makes deployment a bit more expensive,// but in exchange the refund on every call to nonReentrant will be lower in// amount. Since refunds are capped to a percentage of the total// transaction's gas, it is best to keep them low in cases like this one, to// increase the likelihood of the full refund coming into effect.uint256privateconstant NOT_ENTERED =1;
uint256privateconstant ENTERED =2;
uint256private _status;
/**
* @dev Unauthorized reentrant call.
*/errorReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/modifiernonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function_nonReentrantBefore() private{
// On the first call to nonReentrant, _status will be NOT_ENTEREDif (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
_status = ENTERED;
}
function_nonReentrantAfter() private{
// By storing the original value once again, a refund is triggered (see// https://eips.ethereum.org/EIPS/eip-2200)
_status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/function_reentrancyGuardEntered() internalviewreturns (bool) {
return _status == ENTERED;
}
}
Contract Source Code
File 24 of 28: ReentrancyGuardUpgradeable.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)pragmasolidity ^0.8.20;import {Initializable} from"../proxy/utils/Initializable.sol";
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/abstractcontractReentrancyGuardUpgradeableisInitializable{
// Booleans are more expensive than uint256 or any type that takes up a full// word because each write operation emits an extra SLOAD to first read the// slot's contents, replace the bits taken up by the boolean, and then write// back. This is the compiler's defense against contract upgrades and// pointer aliasing, and it cannot be disabled.// The values being non-zero value makes deployment a bit more expensive,// but in exchange the refund on every call to nonReentrant will be lower in// amount. Since refunds are capped to a percentage of the total// transaction's gas, it is best to keep them low in cases like this one, to// increase the likelihood of the full refund coming into effect.uint256privateconstant NOT_ENTERED =1;
uint256privateconstant ENTERED =2;
/// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuardstructReentrancyGuardStorage {
uint256 _status;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))bytes32privateconstant ReentrancyGuardStorageLocation =0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
function_getReentrancyGuardStorage() privatepurereturns (ReentrancyGuardStorage storage $) {
assembly {
$.slot:= ReentrancyGuardStorageLocation
}
}
/**
* @dev Unauthorized reentrant call.
*/errorReentrancyGuardReentrantCall();
function__ReentrancyGuard_init() internalonlyInitializing{
__ReentrancyGuard_init_unchained();
}
function__ReentrancyGuard_init_unchained() internalonlyInitializing{
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
$._status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/modifiernonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function_nonReentrantBefore() private{
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
// On the first call to nonReentrant, _status will be NOT_ENTEREDif ($._status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
$._status = ENTERED;
}
function_nonReentrantAfter() private{
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
// By storing the original value once again, a refund is triggered (see// https://eips.ethereum.org/EIPS/eip-2200)
$._status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/function_reentrancyGuardEntered() internalviewreturns (bool) {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
return $._status == ENTERED;
}
}
Contract Source Code
File 25 of 28: SignatureChecker.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/SignatureChecker.sol)pragmasolidity ^0.8.20;import {ECDSA} from"./ECDSA.sol";
import {IERC1271} from"../../interfaces/IERC1271.sol";
/**
* @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
* signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like
* Argent and Safe Wallet (previously Gnosis Safe).
*/librarySignatureChecker{
/**
* @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
* signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`.
*
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
* change through time. It could return true at block N and false at block N+1 (or the opposite).
*/functionisValidSignatureNow(address signer, bytes32 hash, bytesmemory signature) internalviewreturns (bool) {
(address recovered, ECDSA.RecoverError error, ) = ECDSA.tryRecover(hash, signature);
return
(error == ECDSA.RecoverError.NoError && recovered == signer) ||
isValidERC1271SignatureNow(signer, hash, signature);
}
/**
* @dev Checks if a signature is valid for a given signer and data hash. The signature is validated
* against the signer smart contract using ERC1271.
*
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
* change through time. It could return true at block N and false at block N+1 (or the opposite).
*/functionisValidERC1271SignatureNow(address signer,
bytes32 hash,
bytesmemory signature
) internalviewreturns (bool) {
(bool success, bytesmemory result) = signer.staticcall(
abi.encodeCall(IERC1271.isValidSignature, (hash, signature))
);
return (success &&
result.length>=32&&abi.decode(result, (bytes32)) ==bytes32(IERC1271.isValidSignature.selector));
}
}
Contract Source Code
File 26 of 28: SignedMath.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)pragmasolidity ^0.8.20;/**
* @dev Standard signed math utilities missing in the Solidity language.
*/librarySignedMath{
/**
* @dev Returns the largest of two signed numbers.
*/functionmax(int256 a, int256 b) internalpurereturns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/functionmin(int256 a, int256 b) internalpurereturns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/functionaverage(int256 a, int256 b) internalpurereturns (int256) {
// Formula from the book "Hacker's Delight"int256 x = (a & b) + ((a ^ b) >>1);
return x + (int256(uint256(x) >>255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/functionabs(int256 n) internalpurereturns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`returnuint256(n >=0 ? n : -n);
}
}
}