// SPDX-License-Identifier: Apache-2.0pragmasolidity ^0.8.1;/// @author thirdweb, OpenZeppelin Contracts (v4.9.0)/**
* @dev Collection of functions related to the address type
*/libraryAddress{
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/functionisContract(address account) internalviewreturns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0// for contracts in construction, since the code is only stored at the end// of the constructor execution.return account.code.length>0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/functionsendValue(addresspayable recipient, uint256 amount) internal{
require(address(this).balance>= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/functionfunctionCall(address target, bytesmemory data) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/functionfunctionCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/functionfunctionCallWithValue(address target, bytesmemory data, uint256 value) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/functionfunctionCallWithValue(address target,
bytesmemory data,
uint256 value,
stringmemory errorMessage
) internalreturns (bytesmemory) {
require(address(this).balance>= value, "Address: insufficient balance for call");
(bool success, bytesmemory returndata) = target.call{ value: value }(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/functionfunctionStaticCall(address target, bytesmemory data) internalviewreturns (bytesmemory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/functionfunctionStaticCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalviewreturns (bytesmemory) {
(bool success, bytesmemory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/functionfunctionDelegateCall(address target, bytesmemory data) internalreturns (bytesmemory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/functionfunctionDelegateCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalreturns (bytesmemory) {
(bool success, bytesmemory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/functionverifyCallResultFromTarget(address target,
bool success,
bytesmemory returndata,
stringmemory errorMessage
) internalviewreturns (bytesmemory) {
if (success) {
if (returndata.length==0) {
// only check isContract if the call was successful and the return data is empty// otherwise we already know that it was a contractrequire(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/functionverifyCallResult(bool success,
bytesmemory returndata,
stringmemory errorMessage
) internalpurereturns (bytesmemory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function_revert(bytesmemory returndata, stringmemory errorMessage) privatepure{
// Look for revert reason and bubble it up if presentif (returndata.length>0) {
// The easiest way to bubble the revert reason is using memory via assembly/// @solidity memory-safe-assemblyassembly {
let returndata_size :=mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
Contract Source Code
File 2 of 44: BatchMintMetadata.sol
// SPDX-License-Identifier: Apache-2.0pragmasolidity ^0.8.0;/// @author thirdweb/**
* @title Batch-mint Metadata
* @notice The `BatchMintMetadata` is a contract extension for any base NFT contract. It lets the smart contract
* using this extension set metadata for `n` number of NFTs all at once. This is enabled by storing a single
* base URI for a batch of `n` NFTs, where the metadata for each NFT in a relevant batch is `baseURI/tokenId`.
*/contractBatchMintMetadata{
/// @dev Invalid index for batcherrorBatchMintInvalidBatchId(uint256 index);
/// @dev Invalid tokenerrorBatchMintInvalidTokenId(uint256 tokenId);
/// @dev Metadata frozenerrorBatchMintMetadataFrozen(uint256 batchId);
/// @dev Largest tokenId of each batch of tokens with the same baseURI + 1 {ex: batchId 100 at position 0 includes tokens 0-99}uint256[] private batchIds;
/// @dev Mapping from id of a batch of tokens => to base URI for the respective batch of tokens.mapping(uint256=>string) private baseURI;
/// @dev Mapping from id of a batch of tokens => to whether the base URI for the respective batch of tokens is frozen.mapping(uint256=>bool) public batchFrozen;
/// @dev This event emits when the metadata of all tokens are frozen./// While not currently supported by marketplaces, this event allows/// future indexing if desired.eventMetadataFrozen();
// @dev This event emits when the metadata of a range of tokens is updated./// So that the third-party platforms such as NFT market could/// timely update the images and related attributes of the NFTs.eventBatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
/**
* @notice Returns the count of batches of NFTs.
* @dev Each batch of tokens has an in ID and an associated `baseURI`.
* See {batchIds}.
*/functiongetBaseURICount() publicviewreturns (uint256) {
return batchIds.length;
}
/**
* @notice Returns the ID for the batch of tokens at the given index.
* @dev See {getBaseURICount}.
* @param _index Index of the desired batch in batchIds array.
*/functiongetBatchIdAtIndex(uint256 _index) publicviewreturns (uint256) {
if (_index >= getBaseURICount()) {
revert BatchMintInvalidBatchId(_index);
}
return batchIds[_index];
}
/// @dev Returns the id for the batch of tokens the given tokenId belongs to.function_getBatchId(uint256 _tokenId) internalviewreturns (uint256 batchId, uint256 index) {
uint256 numOfTokenBatches = getBaseURICount();
uint256[] memory indices = batchIds;
for (uint256 i =0; i < numOfTokenBatches; i +=1) {
if (_tokenId < indices[i]) {
index = i;
batchId = indices[i];
return (batchId, index);
}
}
revert BatchMintInvalidTokenId(_tokenId);
}
/// @dev Returns the baseURI for a token. The intended metadata URI for the token is baseURI + tokenId.function_getBaseURI(uint256 _tokenId) internalviewreturns (stringmemory) {
uint256 numOfTokenBatches = getBaseURICount();
uint256[] memory indices = batchIds;
for (uint256 i =0; i < numOfTokenBatches; i +=1) {
if (_tokenId < indices[i]) {
return baseURI[indices[i]];
}
}
revert BatchMintInvalidTokenId(_tokenId);
}
/// @dev returns the starting tokenId of a given batchId.function_getBatchStartId(uint256 _batchID) internalviewreturns (uint256) {
uint256 numOfTokenBatches = getBaseURICount();
uint256[] memory indices = batchIds;
for (uint256 i =0; i < numOfTokenBatches; i++) {
if (_batchID == indices[i]) {
if (i >0) {
return indices[i -1];
}
return0;
}
}
revert BatchMintInvalidBatchId(_batchID);
}
/// @dev Sets the base URI for the batch of tokens with the given batchId.function_setBaseURI(uint256 _batchId, stringmemory _baseURI) internal{
if (batchFrozen[_batchId]) {
revert BatchMintMetadataFrozen(_batchId);
}
baseURI[_batchId] = _baseURI;
emit BatchMetadataUpdate(_getBatchStartId(_batchId), _batchId);
}
/// @dev Freezes the base URI for the batch of tokens with the given batchId.function_freezeBaseURI(uint256 _batchId) internal{
stringmemory baseURIForBatch = baseURI[_batchId];
if (bytes(baseURIForBatch).length==0) {
revert BatchMintInvalidBatchId(_batchId);
}
batchFrozen[_batchId] =true;
emit MetadataFrozen();
}
/// @dev Mints a batch of tokenIds and associates a common baseURI to all those Ids.function_batchMintMetadata(uint256 _startId,
uint256 _amountToMint,
stringmemory _baseURIForTokens
) internalreturns (uint256 nextTokenIdToMint, uint256 batchId) {
batchId = _startId + _amountToMint;
nextTokenIdToMint = batchId;
batchIds.push(batchId);
baseURI[batchId] = _baseURIForTokens;
}
}
Contract Source Code
File 3 of 44: Context.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)pragmasolidity ^0.8.0;/**
* @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;
}
}
Contract Source Code
File 4 of 44: ContractMetadata.sol
// SPDX-License-Identifier: Apache-2.0pragmasolidity ^0.8.0;/// @author thirdwebimport"./interface/IContractMetadata.sol";
/**
* @title Contract Metadata
* @notice Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI
* for you contract.
* Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea.
*/abstractcontractContractMetadataisIContractMetadata{
/// @dev The sender is not authorized to perform the actionerrorContractMetadataUnauthorized();
/// @notice Returns the contract metadata URI.stringpublicoverride contractURI;
/**
* @notice Lets a contract admin set the URI for contract-level metadata.
* @dev Caller should be authorized to setup contractURI, e.g. contract admin.
* See {_canSetContractURI}.
* Emits {ContractURIUpdated Event}.
*
* @param _uri keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
*/functionsetContractURI(stringmemory _uri) externaloverride{
if (!_canSetContractURI()) {
revert ContractMetadataUnauthorized();
}
_setupContractURI(_uri);
}
/// @dev Lets a contract admin set the URI for contract-level metadata.function_setupContractURI(stringmemory _uri) internal{
stringmemory prevURI = contractURI;
contractURI = _uri;
emit ContractURIUpdated(prevURI, _uri);
}
/// @dev Returns whether contract metadata can be set in the given execution context.function_canSetContractURI() internalviewvirtualreturns (bool);
}
Contract Source Code
File 5 of 44: CurrencyTransferLib.sol
// SPDX-License-Identifier: Apache-2.0pragmasolidity ^0.8.0;/// @author thirdweb// Helper interfacesimport { IWETH } from"../infra/interface/IWETH.sol";
import { SafeERC20, IERC20 } from"../external-deps/openzeppelin/token/ERC20/utils/SafeERC20.sol";
libraryCurrencyTransferLib{
usingSafeERC20forIERC20;
errorCurrencyTransferLibMismatchedValue(uint256 expected, uint256 actual);
errorCurrencyTransferLibFailedNativeTransfer(address recipient, uint256 value);
/// @dev The address interpreted as native token of the chain.addresspublicconstant NATIVE_TOKEN =0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
/// @dev Transfers a given amount of currency.functiontransferCurrency(address _currency, address _from, address _to, uint256 _amount) internal{
if (_amount ==0) {
return;
}
if (_currency == NATIVE_TOKEN) {
safeTransferNativeToken(_to, _amount);
} else {
safeTransferERC20(_currency, _from, _to, _amount);
}
}
/// @dev Transfers a given amount of currency. (With native token wrapping)functiontransferCurrencyWithWrapper(address _currency,
address _from,
address _to,
uint256 _amount,
address _nativeTokenWrapper
) internal{
if (_amount ==0) {
return;
}
if (_currency == NATIVE_TOKEN) {
if (_from ==address(this)) {
// withdraw from weth then transfer withdrawn native token to recipient
IWETH(_nativeTokenWrapper).withdraw(_amount);
safeTransferNativeTokenWithWrapper(_to, _amount, _nativeTokenWrapper);
} elseif (_to ==address(this)) {
// store native currency in wethif (_amount !=msg.value) {
revert CurrencyTransferLibMismatchedValue(msg.value, _amount);
}
IWETH(_nativeTokenWrapper).deposit{ value: _amount }();
} else {
safeTransferNativeTokenWithWrapper(_to, _amount, _nativeTokenWrapper);
}
} else {
safeTransferERC20(_currency, _from, _to, _amount);
}
}
/// @dev Transfer `amount` of ERC20 token from `from` to `to`.functionsafeTransferERC20(address _currency, address _from, address _to, uint256 _amount) internal{
if (_from == _to) {
return;
}
if (_from ==address(this)) {
IERC20(_currency).safeTransfer(_to, _amount);
} else {
IERC20(_currency).safeTransferFrom(_from, _to, _amount);
}
}
/// @dev Transfers `amount` of native token to `to`.functionsafeTransferNativeToken(address to, uint256 value) internal{
// solhint-disable avoid-low-level-calls// slither-disable-next-line low-level-calls
(bool success, ) = to.call{ value: value }("");
if (!success) {
revert CurrencyTransferLibFailedNativeTransfer(to, value);
}
}
/// @dev Transfers `amount` of native token to `to`. (With native token wrapping)functionsafeTransferNativeTokenWithWrapper(address to, uint256 value, address _nativeTokenWrapper) internal{
// solhint-disable avoid-low-level-calls// slither-disable-next-line low-level-calls
(bool success, ) = to.call{ value: value }("");
if (!success) {
IWETH(_nativeTokenWrapper).deposit{ value: value }();
IERC20(_nativeTokenWrapper).safeTransfer(to, value);
}
}
}
Contract Source Code
File 6 of 44: DelayedReveal.sol
// SPDX-License-Identifier: Apache-2.0pragmasolidity ^0.8.0;/// @author thirdwebimport"./interface/IDelayedReveal.sol";
/**
* @title Delayed Reveal
* @notice Thirdweb's `DelayedReveal` is a contract extension for base NFT contracts. It lets you create batches of
* 'delayed-reveal' NFTs. You can learn more about the usage of delayed reveal NFTs here - https://blog.thirdweb.com/delayed-reveal-nfts
*/abstractcontractDelayedRevealisIDelayedReveal{
/// @dev The contract doesn't have any url to be delayed revealederrorDelayedRevealNothingToReveal();
/// @dev The result of the returned an incorrect hasherrorDelayedRevealIncorrectResultHash(bytes32 expected, bytes32 actual);
/// @dev Mapping from tokenId of a batch of tokens => to delayed reveal data.mapping(uint256=>bytes) public encryptedData;
/// @dev Sets the delayed reveal data for a batchId.function_setEncryptedData(uint256 _batchId, bytesmemory _encryptedData) internal{
encryptedData[_batchId] = _encryptedData;
}
/**
* @notice Returns revealed URI for a batch of NFTs.
* @dev Reveal encrypted base URI for `_batchId` with caller/admin's `_key` used for encryption.
* Reverts if there's no encrypted URI for `_batchId`.
* See {encryptDecrypt}.
*
* @param _batchId ID of the batch for which URI is being revealed.
* @param _key Secure key used by caller/admin for encryption of baseURI.
*
* @return revealedURI Decrypted base URI.
*/functiongetRevealURI(uint256 _batchId, bytescalldata _key) publicviewreturns (stringmemory revealedURI) {
bytesmemory data = encryptedData[_batchId];
if (data.length==0) {
revert DelayedRevealNothingToReveal();
}
(bytesmemory encryptedURI, bytes32 provenanceHash) =abi.decode(data, (bytes, bytes32));
revealedURI =string(encryptDecrypt(encryptedURI, _key));
if (keccak256(abi.encodePacked(revealedURI, _key, block.chainid)) != provenanceHash) {
revert DelayedRevealIncorrectResultHash(
provenanceHash,
keccak256(abi.encodePacked(revealedURI, _key, block.chainid))
);
}
}
/**
* @notice Encrypt/decrypt data on chain.
* @dev Encrypt/decrypt given `data` with `key`. Uses inline assembly.
* See: https://ethereum.stackexchange.com/questions/69825/decrypt-message-on-chain
*
* @param data Bytes of data to encrypt/decrypt.
* @param key Secure key used by caller for encryption/decryption.
*
* @return result Output after encryption/decryption of given data.
*/functionencryptDecrypt(bytesmemory data, bytescalldata key) publicpureoverridereturns (bytesmemory result) {
// Store data length on stack for later useuint256 length = data.length;
// solhint-disable-next-line no-inline-assemblyassembly {
// Set result to free memory pointer
result :=mload(0x40)
// Increase free memory pointer by lenght + 32mstore(0x40, add(add(result, length), 32))
// Set result lengthmstore(result, length)
}
// Iterate over the data stepping by 32 bytesfor (uint256 i =0; i < length; i +=32) {
// Generate hash of the key and offsetbytes32 hash =keccak256(abi.encodePacked(key, i));
bytes32 chunk;
// solhint-disable-next-line no-inline-assemblyassembly {
// Read 32-bytes data chunk
chunk :=mload(add(data, add(i, 32)))
}
// XOR the chunk with hash
chunk ^= hash;
// solhint-disable-next-line no-inline-assemblyassembly {
// Write 32-byte encrypted chunkmstore(add(result, add(i, 32)), chunk)
}
}
}
/**
* @notice Returns whether the relvant batch of NFTs is subject to a delayed reveal.
* @dev Returns `true` if `_batchId`'s base URI is encrypted.
* @param _batchId ID of a batch of NFTs.
*/functionisEncryptedBatch(uint256 _batchId) publicviewreturns (bool) {
return encryptedData[_batchId].length>0;
}
}
Contract Source Code
File 7 of 44: Drop.sol
// SPDX-License-Identifier: Apache-2.0pragmasolidity ^0.8.0;/// @author thirdwebimport"./interface/IDrop.sol";
import"../lib/MerkleProof.sol";
abstractcontractDropisIDrop{
/// @dev The sender is not authorized to perform the actionerrorDropUnauthorized();
/// @dev Exceeded the max token total supplyerrorDropExceedMaxSupply();
/// @dev No active claim conditionerrorDropNoActiveCondition();
/// @dev Claim condition invalid currency or priceerrorDropClaimInvalidTokenPrice(address expectedCurrency,
uint256 expectedPricePerToken,
address actualCurrency,
uint256 actualExpectedPricePerToken
);
/// @dev Claim condition exceeded limiterrorDropClaimExceedLimit(uint256 expected, uint256 actual);
/// @dev Claim condition exceeded max supplyerrorDropClaimExceedMaxSupply(uint256 expected, uint256 actual);
/// @dev Claim condition not started yeterrorDropClaimNotStarted(uint256 expected, uint256 actual);
/*///////////////////////////////////////////////////////////////
State variables
//////////////////////////////////////////////////////////////*//// @dev The active conditions for claiming tokens.
ClaimConditionList public claimCondition;
/*///////////////////////////////////////////////////////////////
Drop logic
//////////////////////////////////////////////////////////////*//// @dev Lets an account claim tokens.functionclaim(address _receiver,
uint256 _quantity,
address _currency,
uint256 _pricePerToken,
AllowlistProof calldata _allowlistProof,
bytesmemory _data
) publicpayablevirtualoverride{
_beforeClaim(_receiver, _quantity, _currency, _pricePerToken, _allowlistProof, _data);
uint256 activeConditionId = getActiveClaimConditionId();
verifyClaim(activeConditionId, _dropMsgSender(), _quantity, _currency, _pricePerToken, _allowlistProof);
// Update contract state.
claimCondition.conditions[activeConditionId].supplyClaimed += _quantity;
claimCondition.supplyClaimedByWallet[activeConditionId][_dropMsgSender()] += _quantity;
// If there's a price, collect price.
_collectPriceOnClaim(address(0), _quantity, _currency, _pricePerToken);
// Mint the relevant tokens to claimer.uint256 startTokenId = _transferTokensOnClaim(_receiver, _quantity);
emit TokensClaimed(activeConditionId, _dropMsgSender(), _receiver, startTokenId, _quantity);
_afterClaim(_receiver, _quantity, _currency, _pricePerToken, _allowlistProof, _data);
}
/// @dev Lets a contract admin set claim conditions.functionsetClaimConditions(
ClaimCondition[] calldata _conditions,
bool _resetClaimEligibility
) externalvirtualoverride{
if (!_canSetClaimConditions()) {
revert DropUnauthorized();
}
uint256 existingStartIndex = claimCondition.currentStartId;
uint256 existingPhaseCount = claimCondition.count;
/**
* The mapping `supplyClaimedByWallet` uses a claim condition's UID as a key.
*
* If `_resetClaimEligibility == true`, we assign completely new UIDs to the claim
* conditions in `_conditions`, effectively resetting the restrictions on claims expressed
* by `supplyClaimedByWallet`.
*/uint256 newStartIndex = existingStartIndex;
if (_resetClaimEligibility) {
newStartIndex = existingStartIndex + existingPhaseCount;
}
claimCondition.count = _conditions.length;
claimCondition.currentStartId = newStartIndex;
uint256 lastConditionStartTimestamp;
for (uint256 i =0; i < _conditions.length; i++) {
require(i ==0|| lastConditionStartTimestamp < _conditions[i].startTimestamp, "ST");
uint256 supplyClaimedAlready = claimCondition.conditions[newStartIndex + i].supplyClaimed;
if (supplyClaimedAlready > _conditions[i].maxClaimableSupply) {
revert DropExceedMaxSupply();
}
claimCondition.conditions[newStartIndex + i] = _conditions[i];
claimCondition.conditions[newStartIndex + i].supplyClaimed = supplyClaimedAlready;
lastConditionStartTimestamp = _conditions[i].startTimestamp;
}
/**
* Gas refunds (as much as possible)
*
* If `_resetClaimEligibility == true`, we assign completely new UIDs to the claim
* conditions in `_conditions`. So, we delete claim conditions with UID < `newStartIndex`.
*
* If `_resetClaimEligibility == false`, and there are more existing claim conditions
* than in `_conditions`, we delete the existing claim conditions that don't get replaced
* by the conditions in `_conditions`.
*/if (_resetClaimEligibility) {
for (uint256 i = existingStartIndex; i < newStartIndex; i++) {
delete claimCondition.conditions[i];
}
} else {
if (existingPhaseCount > _conditions.length) {
for (uint256 i = _conditions.length; i < existingPhaseCount; i++) {
delete claimCondition.conditions[newStartIndex + i];
}
}
}
emit ClaimConditionsUpdated(_conditions, _resetClaimEligibility);
}
/// @dev Checks a request to claim NFTs against the active claim condition's criteria.functionverifyClaim(uint256 _conditionId,
address _claimer,
uint256 _quantity,
address _currency,
uint256 _pricePerToken,
AllowlistProof calldata _allowlistProof
) publicviewvirtualreturns (bool isOverride) {
ClaimCondition memory currentClaimPhase = claimCondition.conditions[_conditionId];
uint256 claimLimit = currentClaimPhase.quantityLimitPerWallet;
uint256 claimPrice = currentClaimPhase.pricePerToken;
address claimCurrency = currentClaimPhase.currency;
/*
* Here `isOverride` implies that if the merkle proof verification fails,
* the claimer would claim through open claim limit instead of allowlisted limit.
*/if (currentClaimPhase.merkleRoot !=bytes32(0)) {
(isOverride, ) = MerkleProof.verify(
_allowlistProof.proof,
currentClaimPhase.merkleRoot,
keccak256(
abi.encodePacked(
_claimer,
_allowlistProof.quantityLimitPerWallet,
_allowlistProof.pricePerToken,
_allowlistProof.currency
)
)
);
}
if (isOverride) {
claimLimit = _allowlistProof.quantityLimitPerWallet !=0
? _allowlistProof.quantityLimitPerWallet
: claimLimit;
claimPrice = _allowlistProof.pricePerToken !=type(uint256).max
? _allowlistProof.pricePerToken
: claimPrice;
claimCurrency = _allowlistProof.pricePerToken !=type(uint256).max&& _allowlistProof.currency !=address(0)
? _allowlistProof.currency
: claimCurrency;
}
uint256 supplyClaimedByWallet = claimCondition.supplyClaimedByWallet[_conditionId][_claimer];
if (_currency != claimCurrency || _pricePerToken != claimPrice) {
revert DropClaimInvalidTokenPrice(_currency, _pricePerToken, claimCurrency, claimPrice);
}
if (_quantity ==0|| (_quantity + supplyClaimedByWallet > claimLimit)) {
revert DropClaimExceedLimit(claimLimit, _quantity + supplyClaimedByWallet);
}
if (currentClaimPhase.supplyClaimed + _quantity > currentClaimPhase.maxClaimableSupply) {
revert DropClaimExceedMaxSupply(
currentClaimPhase.maxClaimableSupply,
currentClaimPhase.supplyClaimed + _quantity
);
}
if (currentClaimPhase.startTimestamp >block.timestamp) {
revert DropClaimNotStarted(currentClaimPhase.startTimestamp, block.timestamp);
}
}
/// @dev At any given moment, returns the uid for the active claim condition.functiongetActiveClaimConditionId() publicviewreturns (uint256) {
for (uint256 i = claimCondition.currentStartId + claimCondition.count; i > claimCondition.currentStartId; i--) {
if (block.timestamp>= claimCondition.conditions[i -1].startTimestamp) {
return i -1;
}
}
revert DropNoActiveCondition();
}
/// @dev Returns the claim condition at the given uid.functiongetClaimConditionById(uint256 _conditionId) externalviewreturns (ClaimCondition memory condition) {
condition = claimCondition.conditions[_conditionId];
}
/// @dev Returns the supply claimed by claimer for a given conditionId.functiongetSupplyClaimedByWallet(uint256 _conditionId,
address _claimer
) publicviewreturns (uint256 supplyClaimedByWallet) {
supplyClaimedByWallet = claimCondition.supplyClaimedByWallet[_conditionId][_claimer];
}
/*////////////////////////////////////////////////////////////////////
Optional hooks that can be implemented in the derived contract
///////////////////////////////////////////////////////////////////*//// @dev Exposes the ability to override the msg sender.function_dropMsgSender() internalvirtualreturns (address) {
returnmsg.sender;
}
/// @dev Runs before every `claim` function call.function_beforeClaim(address _receiver,
uint256 _quantity,
address _currency,
uint256 _pricePerToken,
AllowlistProof calldata _allowlistProof,
bytesmemory _data
) internalvirtual{}
/// @dev Runs after every `claim` function call.function_afterClaim(address _receiver,
uint256 _quantity,
address _currency,
uint256 _pricePerToken,
AllowlistProof calldata _allowlistProof,
bytesmemory _data
) internalvirtual{}
/*///////////////////////////////////////////////////////////////
Virtual functions: to be implemented in derived contract
//////////////////////////////////////////////////////////////*//// @dev Collects and distributes the primary sale value of NFTs being claimed.function_collectPriceOnClaim(address _primarySaleRecipient,
uint256 _quantityToClaim,
address _currency,
uint256 _pricePerToken
) internalvirtual;
/// @dev Transfers the NFTs being claimed.function_transferTokensOnClaim(address _to,
uint256 _quantityBeingClaimed
) internalvirtualreturns (uint256 startTokenId);
/// @dev Determine what wallet can update claim conditionsfunction_canSetClaimConditions() internalviewvirtualreturns (bool);
}
Contract Source Code
File 8 of 44: ERC165.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)pragmasolidity ^0.8.0;import"./interface/IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/abstractcontractERC165isIERC165{
/**
* @dev See {IERC165-supportsInterface}.
*/functionsupportsInterface(bytes4 interfaceId) publicviewvirtualoverridereturns (bool) {
return interfaceId ==type(IERC165).interfaceId;
}
}
Contract Source Code
File 9 of 44: ERC721AQueryable.sol
// SPDX-License-Identifier: MIT// ERC721A Contracts v3.3.0// Creator: Chiru Labspragmasolidity ^0.8.4;import"./IERC721AQueryable.sol";
import"../ERC721AVirtualApprove.sol";
/**
* @title ERC721A Queryable
* @dev ERC721A subclass with convenience query functions.
*/abstractcontractERC721AQueryableisERC721A, IERC721AQueryable{
/**
* @dev Returns the `TokenOwnership` struct at `tokenId` without reverting.
*
* If the `tokenId` is out of bounds:
* - `addr` = `address(0)`
* - `startTimestamp` = `0`
* - `burned` = `false`
*
* If the `tokenId` is burned:
* - `addr` = `<Address of owner before token was burned>`
* - `startTimestamp` = `<Timestamp when token was burned>`
* - `burned = `true`
*
* Otherwise:
* - `addr` = `<Address of owner>`
* - `startTimestamp` = `<Timestamp of start of ownership>`
* - `burned = `false`
*/functionexplicitOwnershipOf(uint256 tokenId) publicviewoverridereturns (TokenOwnership memory) {
TokenOwnership memory ownership;
if (tokenId < _startTokenId() || tokenId >= _currentIndex) {
return ownership;
}
ownership = _ownerships[tokenId];
if (ownership.burned) {
return ownership;
}
return _ownershipOf(tokenId);
}
/**
* @dev Returns an array of `TokenOwnership` structs at `tokenIds` in order.
* See {ERC721AQueryable-explicitOwnershipOf}
*/functionexplicitOwnershipsOf(uint256[] memory tokenIds) externalviewoverridereturns (TokenOwnership[] memory) {
unchecked {
uint256 tokenIdsLength = tokenIds.length;
TokenOwnership[] memory ownerships =new TokenOwnership[](tokenIdsLength);
for (uint256 i; i != tokenIdsLength; ++i) {
ownerships[i] = explicitOwnershipOf(tokenIds[i]);
}
return ownerships;
}
}
/**
* @dev Returns an array of token IDs owned by `owner`,
* in the range [`start`, `stop`)
* (i.e. `start <= tokenId < stop`).
*
* This function allows for tokens to be queried if the collection
* grows too big for a single call of {ERC721AQueryable-tokensOfOwner}.
*
* Requirements:
*
* - `start` < `stop`
*//* solhint-disable*/functiontokensOfOwnerIn(address owner,
uint256 start,
uint256 stop
) externalviewoverridereturns (uint256[] memory) {
unchecked {
if (start >= stop) revert InvalidQueryRange();
uint256 tokenIdsIdx;
uint256 stopLimit = _currentIndex;
// Set `start = max(start, _startTokenId())`.if (start < _startTokenId()) {
start = _startTokenId();
}
// Set `stop = min(stop, _currentIndex)`.if (stop > stopLimit) {
stop = stopLimit;
}
uint256 tokenIdsMaxLength = balanceOf(owner);
// Set `tokenIdsMaxLength = min(balanceOf(owner), stop - start)`,// to cater for cases where `balanceOf(owner)` is too big.if (start < stop) {
uint256 rangeLength = stop - start;
if (rangeLength < tokenIdsMaxLength) {
tokenIdsMaxLength = rangeLength;
}
} else {
tokenIdsMaxLength =0;
}
uint256[] memory tokenIds =newuint256[](tokenIdsMaxLength);
if (tokenIdsMaxLength ==0) {
return tokenIds;
}
// We need to call `explicitOwnershipOf(start)`,// because the slot at `start` may not be initialized.
TokenOwnership memory ownership = explicitOwnershipOf(start);
address currOwnershipAddr;
// If the starting slot exists (i.e. not burned), initialize `currOwnershipAddr`.// `ownership.address` will not be zero, as `start` is clamped to the valid token ID range.if (!ownership.burned) {
currOwnershipAddr = ownership.addr;
}
for (uint256 i = start; i != stop && tokenIdsIdx != tokenIdsMaxLength; ++i) {
ownership = _ownerships[i];
if (ownership.burned) {
continue;
}
if (ownership.addr !=address(0)) {
currOwnershipAddr = ownership.addr;
}
if (currOwnershipAddr == owner) {
tokenIds[tokenIdsIdx++] = i;
}
}
// Downsize the array to fit.assembly {
mstore(tokenIds, tokenIdsIdx)
}
return tokenIds;
}
}
/* solhint-enable *//**
* @dev Returns an array of token IDs owned by `owner`.
*
* This function scans the ownership mapping and is O(totalSupply) in complexity.
* It is meant to be called off-chain.
*
* See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into
* multiple smaller scans if the collection is large enough to cause
* an out-of-gas error (10K pfp collections should be fine).
*/functiontokensOfOwner(address owner) externalviewoverridereturns (uint256[] memory) {
unchecked {
uint256 tokenIdsIdx;
address currOwnershipAddr;
uint256 tokenIdsLength = balanceOf(owner);
uint256[] memory tokenIds =newuint256[](tokenIdsLength);
TokenOwnership memory ownership;
for (uint256 i = _startTokenId(); tokenIdsIdx != tokenIdsLength; ++i) {
ownership = _ownerships[i];
if (ownership.burned) {
continue;
}
if (ownership.addr !=address(0)) {
currOwnershipAddr = ownership.addr;
}
if (currOwnershipAddr == owner) {
tokenIds[tokenIdsIdx++] = i;
}
}
return tokenIds;
}
}
}
Contract Source Code
File 10 of 44: ERC721AVirtualApprove.sol
// SPDX-License-Identifier: MIT// ERC721A Contracts v3.3.0// Creator: Chiru Labspragmasolidity ^0.8.4;////////// CHANGELOG: turn `approve` to virtual //////////import"./interface/IERC721A.sol";
import"./interface/IERC721Receiver.sol";
import"../lib/Address.sol";
import"../external-deps/openzeppelin/utils/Context.sol";
import"../lib/Strings.sol";
import"./ERC165.sol";
/**
* @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
* the Metadata extension. Built to optimize for lower gas during batch mints.
*
* Assumes serials are sequentially minted starting at _startTokenId() (defaults to 0, e.g. 0, 1, 2, 3..).
*
* Assumes that an owner cannot have more than 2**64 - 1 (max value of uint64) of supply.
*
* Assumes that the maximum token id cannot exceed 2**256 - 1 (max value of uint256).
*/contractERC721AisContext, ERC165, IERC721A{
usingAddressforaddress;
usingStringsforuint256;
// The tokenId of the next token to be minted.uint256internal _currentIndex;
// The number of tokens burned.uint256internal _burnCounter;
// Token namestringprivate _name;
// Token symbolstringprivate _symbol;
// Mapping from token ID to ownership details// An empty struct value does not necessarily mean the token is unowned. See _ownershipOf implementation for details.mapping(uint256=> TokenOwnership) internal _ownerships;
// Mapping owner address to address datamapping(address=> AddressData) private _addressData;
// Mapping from token ID to approved addressmapping(uint256=>address) private _tokenApprovals;
// Mapping from owner to operator approvalsmapping(address=>mapping(address=>bool)) private _operatorApprovals;
constructor(stringmemory name_, stringmemory symbol_) {
_name = name_;
_symbol = symbol_;
_currentIndex = _startTokenId();
}
/**
* To change the starting tokenId, please override this function.
*/function_startTokenId() internalviewvirtualreturns (uint256) {
return0;
}
/**
* @dev Burned tokens are calculated here, use _totalMinted() if you want to count just minted tokens.
*/functiontotalSupply() publicviewoverridereturns (uint256) {
// Counter underflow is impossible as _burnCounter cannot be incremented// more than _currentIndex - _startTokenId() timesunchecked {
return _currentIndex - _burnCounter - _startTokenId();
}
}
/**
* Returns the total amount of tokens minted in the contract.
*/function_totalMinted() internalviewreturns (uint256) {
// Counter underflow is impossible as _currentIndex does not decrement,// and it is initialized to _startTokenId()unchecked {
return _currentIndex - _startTokenId();
}
}
/**
* @dev See {IERC165-supportsInterface}.
*/functionsupportsInterface(bytes4 interfaceId) publicviewvirtualoverride(ERC165) returns (bool) {
return
interfaceId ==type(IERC721).interfaceId||
interfaceId ==type(IERC721Metadata).interfaceId||super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC721-balanceOf}.
*/functionbalanceOf(address owner) publicviewoverridereturns (uint256) {
if (owner ==address(0)) revert BalanceQueryForZeroAddress();
returnuint256(_addressData[owner].balance);
}
/**
* Returns the number of tokens minted by `owner`.
*/function_numberMinted(address owner) internalviewreturns (uint256) {
returnuint256(_addressData[owner].numberMinted);
}
/**
* Returns the number of tokens burned by or on behalf of `owner`.
*/function_numberBurned(address owner) internalviewreturns (uint256) {
returnuint256(_addressData[owner].numberBurned);
}
/**
* Returns the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
*/function_getAux(address owner) internalviewreturns (uint64) {
return _addressData[owner].aux;
}
/**
* Sets the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
* If there are multiple variables, please pack them into a uint64.
*/function_setAux(address owner, uint64 aux) internal{
_addressData[owner].aux = aux;
}
/**
* Gas spent here starts off proportional to the maximum mint batch size.
* It gradually moves to O(1) as tokens get transferred around in the collection over time.
*/function_ownershipOf(uint256 tokenId) internalviewreturns (TokenOwnership memory) {
uint256 curr = tokenId;
unchecked {
if (_startTokenId() <= curr)
if (curr < _currentIndex) {
TokenOwnership memory ownership = _ownerships[curr];
if (!ownership.burned) {
if (ownership.addr !=address(0)) {
return ownership;
}
// Invariant:// There will always be an ownership that has an address and is not burned// before an ownership that does not have an address and is not burned.// Hence, curr will not underflow.while (true) {
curr--;
ownership = _ownerships[curr];
if (ownership.addr !=address(0)) {
return ownership;
}
}
}
}
}
revert OwnerQueryForNonexistentToken();
}
/**
* @dev See {IERC721-ownerOf}.
*/functionownerOf(uint256 tokenId) publicviewoverridereturns (address) {
return _ownershipOf(tokenId).addr;
}
/**
* @dev See {IERC721Metadata-name}.
*/functionname() publicviewvirtualoverridereturns (stringmemory) {
return _name;
}
/**
* @dev See {IERC721Metadata-symbol}.
*/functionsymbol() publicviewvirtualoverridereturns (stringmemory) {
return _symbol;
}
/**
* @dev See {IERC721Metadata-tokenURI}.
*/functiontokenURI(uint256 tokenId) publicviewvirtualoverridereturns (stringmemory) {
if (!_exists(tokenId)) revert URIQueryForNonexistentToken();
stringmemory baseURI = _baseURI();
returnbytes(baseURI).length!=0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
}
/**
* @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
* token will be the concatenation of the `baseURI` and the `tokenId`. Empty
* by default, can be overriden in child contracts.
*/function_baseURI() internalviewvirtualreturns (stringmemory) {
return"";
}
/**
* @dev See {IERC721-approve}.
*/functionapprove(address to, uint256 tokenId) publicvirtualoverride{
address owner = ERC721A.ownerOf(tokenId);
if (to == owner) revert ApprovalToCurrentOwner();
if (_msgSender() != owner)
if (!isApprovedForAll(owner, _msgSender())) {
revert ApprovalCallerNotOwnerNorApproved();
}
_approve(to, tokenId, owner);
}
/**
* @dev See {IERC721-getApproved}.
*/functiongetApproved(uint256 tokenId) publicviewoverridereturns (address) {
if (!_exists(tokenId)) revert ApprovalQueryForNonexistentToken();
return _tokenApprovals[tokenId];
}
/**
* @dev See {IERC721-setApprovalForAll}.
*/functionsetApprovalForAll(address operator, bool approved) publicvirtualoverride{
if (operator == _msgSender()) revert ApproveToCaller();
_operatorApprovals[_msgSender()][operator] = approved;
emit ApprovalForAll(_msgSender(), operator, approved);
}
/**
* @dev See {IERC721-isApprovedForAll}.
*/functionisApprovedForAll(address owner, address operator) publicviewvirtualoverridereturns (bool) {
return _operatorApprovals[owner][operator];
}
/**
* @dev See {IERC721-transferFrom}.
*/functiontransferFrom(addressfrom, address to, uint256 tokenId) publicvirtualoverride{
_transfer(from, to, tokenId);
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/functionsafeTransferFrom(addressfrom, address to, uint256 tokenId) publicvirtualoverride{
safeTransferFrom(from, to, tokenId, "");
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/functionsafeTransferFrom(addressfrom, address to, uint256 tokenId, bytesmemory _data) publicvirtualoverride{
_transfer(from, to, tokenId);
if (to.isContract())
if (!_checkContractOnERC721Received(from, to, tokenId, _data)) {
revert TransferToNonERC721ReceiverImplementer();
}
}
/**
* @dev Returns whether `tokenId` exists.
*
* Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
*
* Tokens start existing when they are minted (`_mint`),
*/function_exists(uint256 tokenId) internalviewreturns (bool) {
return _startTokenId() <= tokenId && tokenId < _currentIndex &&!_ownerships[tokenId].burned;
}
/**
* @dev Equivalent to `_safeMint(to, quantity, '')`.
*/function_safeMint(address to, uint256 quantity) internal{
_safeMint(to, quantity, "");
}
/**
* @dev Safely mints `quantity` tokens and transfers them to `to`.
*
* Requirements:
*
* - If `to` refers to a smart contract, it must implement
* {IERC721Receiver-onERC721Received}, which is called for each safe transfer.
* - `quantity` must be greater than 0.
*
* Emits a {Transfer} event.
*/function_safeMint(address to, uint256 quantity, bytesmemory _data) internal{
uint256 startTokenId = _currentIndex;
if (to ==address(0)) revert MintToZeroAddress();
if (quantity ==0) revert MintZeroQuantity();
_beforeTokenTransfers(address(0), to, startTokenId, quantity);
// Overflows are incredibly unrealistic.// balance or numberMinted overflow if current value of either + quantity > 1.8e19 (2**64) - 1// updatedIndex overflows if _currentIndex + quantity > 1.2e77 (2**256) - 1unchecked {
_addressData[to].balance+=uint64(quantity);
_addressData[to].numberMinted +=uint64(quantity);
_ownerships[startTokenId].addr = to;
_ownerships[startTokenId].startTimestamp =uint64(block.timestamp);
uint256 updatedIndex = startTokenId;
uint256 end = updatedIndex + quantity;
if (to.isContract()) {
do {
emit Transfer(address(0), to, updatedIndex);
if (!_checkContractOnERC721Received(address(0), to, updatedIndex++, _data)) {
revert TransferToNonERC721ReceiverImplementer();
}
} while (updatedIndex < end);
// Reentrancy protectionif (_currentIndex != startTokenId) revert();
} else {
do {
emit Transfer(address(0), to, updatedIndex++);
} while (updatedIndex < end);
}
_currentIndex = updatedIndex;
}
_afterTokenTransfers(address(0), to, startTokenId, quantity);
}
/**
* @dev Mints `quantity` tokens and transfers them to `to`.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `quantity` must be greater than 0.
*
* Emits a {Transfer} event.
*/function_mint(address to, uint256 quantity) internal{
uint256 startTokenId = _currentIndex;
if (to ==address(0)) revert MintToZeroAddress();
if (quantity ==0) revert MintZeroQuantity();
_beforeTokenTransfers(address(0), to, startTokenId, quantity);
// Overflows are incredibly unrealistic.// balance or numberMinted overflow if current value of either + quantity > 1.8e19 (2**64) - 1// updatedIndex overflows if _currentIndex + quantity > 1.2e77 (2**256) - 1unchecked {
_addressData[to].balance+=uint64(quantity);
_addressData[to].numberMinted +=uint64(quantity);
_ownerships[startTokenId].addr = to;
_ownerships[startTokenId].startTimestamp =uint64(block.timestamp);
uint256 updatedIndex = startTokenId;
uint256 end = updatedIndex + quantity;
do {
emit Transfer(address(0), to, updatedIndex++);
} while (updatedIndex < end);
_currentIndex = updatedIndex;
}
_afterTokenTransfers(address(0), to, startTokenId, quantity);
}
/**
* @dev Transfers `tokenId` from `from` to `to`.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
*
* Emits a {Transfer} event.
*/function_transfer(addressfrom, address to, uint256 tokenId) private{
TokenOwnership memory prevOwnership = _ownershipOf(tokenId);
if (prevOwnership.addr !=from) revert TransferFromIncorrectOwner();
bool isApprovedOrOwner = (_msgSender() ==from||
isApprovedForAll(from, _msgSender()) ||
getApproved(tokenId) == _msgSender());
if (!isApprovedOrOwner) revert TransferCallerNotOwnerNorApproved();
if (to ==address(0)) revert TransferToZeroAddress();
_beforeTokenTransfers(from, to, tokenId, 1);
// Clear approvals from the previous owner
_approve(address(0), tokenId, from);
// Underflow of the sender's balance is impossible because we check for// ownership above and the recipient's balance can't realistically overflow.// Counter overflow is incredibly unrealistic as tokenId would have to be 2**256.unchecked {
_addressData[from].balance-=1;
_addressData[to].balance+=1;
TokenOwnership storage currSlot = _ownerships[tokenId];
currSlot.addr = to;
currSlot.startTimestamp =uint64(block.timestamp);
// If the ownership slot of tokenId+1 is not explicitly set, that means the transfer initiator owns it.// Set the slot of tokenId+1 explicitly in storage to maintain correctness for ownerOf(tokenId+1) calls.uint256 nextTokenId = tokenId +1;
TokenOwnership storage nextSlot = _ownerships[nextTokenId];
if (nextSlot.addr ==address(0)) {
// This will suffice for checking _exists(nextTokenId),// as a burned slot cannot contain the zero address.if (nextTokenId != _currentIndex) {
nextSlot.addr =from;
nextSlot.startTimestamp = prevOwnership.startTimestamp;
}
}
}
emit Transfer(from, to, tokenId);
_afterTokenTransfers(from, to, tokenId, 1);
}
/**
* @dev Equivalent to `_burn(tokenId, false)`.
*/function_burn(uint256 tokenId) internalvirtual{
_burn(tokenId, false);
}
/**
* @dev Destroys `tokenId`.
* The approval is cleared when the token is burned.
*
* Requirements:
*
* - `tokenId` must exist.
*
* Emits a {Transfer} event.
*/function_burn(uint256 tokenId, bool approvalCheck) internalvirtual{
TokenOwnership memory prevOwnership = _ownershipOf(tokenId);
addressfrom= prevOwnership.addr;
if (approvalCheck) {
bool isApprovedOrOwner = (_msgSender() ==from||
isApprovedForAll(from, _msgSender()) ||
getApproved(tokenId) == _msgSender());
if (!isApprovedOrOwner) revert TransferCallerNotOwnerNorApproved();
}
_beforeTokenTransfers(from, address(0), tokenId, 1);
// Clear approvals from the previous owner
_approve(address(0), tokenId, from);
// Underflow of the sender's balance is impossible because we check for// ownership above and the recipient's balance can't realistically overflow.// Counter overflow is incredibly unrealistic as tokenId would have to be 2**256.unchecked {
AddressData storage addressData = _addressData[from];
addressData.balance-=1;
addressData.numberBurned +=1;
// Keep track of who burned the token, and the timestamp of burning.
TokenOwnership storage currSlot = _ownerships[tokenId];
currSlot.addr =from;
currSlot.startTimestamp =uint64(block.timestamp);
currSlot.burned =true;
// If the ownership slot of tokenId+1 is not explicitly set, that means the burn initiator owns it.// Set the slot of tokenId+1 explicitly in storage to maintain correctness for ownerOf(tokenId+1) calls.uint256 nextTokenId = tokenId +1;
TokenOwnership storage nextSlot = _ownerships[nextTokenId];
if (nextSlot.addr ==address(0)) {
// This will suffice for checking _exists(nextTokenId),// as a burned slot cannot contain the zero address.if (nextTokenId != _currentIndex) {
nextSlot.addr =from;
nextSlot.startTimestamp = prevOwnership.startTimestamp;
}
}
}
emit Transfer(from, address(0), tokenId);
_afterTokenTransfers(from, address(0), tokenId, 1);
// Overflow not possible, as _burnCounter cannot be exceed _currentIndex times.unchecked {
_burnCounter++;
}
}
/**
* @dev Approve `to` to operate on `tokenId`
*
* Emits a {Approval} event.
*/function_approve(address to, uint256 tokenId, address owner) private{
_tokenApprovals[tokenId] = to;
emit Approval(owner, to, tokenId);
}
/**
* @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target contract.
*
* @param from address representing the previous owner of the given token ID
* @param to target address that will receive the tokens
* @param tokenId uint256 ID of the token to be transferred
* @param _data bytes optional data to send along with the call
* @return bool whether the call correctly returned the expected magic value
*/function_checkContractOnERC721Received(addressfrom,
address to,
uint256 tokenId,
bytesmemory _data
) privatereturns (bool) {
try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) {
return retval == IERC721Receiver(to).onERC721Received.selector;
} catch (bytesmemory reason) {
if (reason.length==0) {
revert TransferToNonERC721ReceiverImplementer();
} else {
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
/**
* @dev Hook that is called before a set of serially-ordered token ids are about to be transferred. This includes minting.
* And also called before burning one token.
*
* startTokenId - the first token id to be transferred
* quantity - the amount to be transferred
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
* transferred to `to`.
* - When `from` is zero, `tokenId` will be minted for `to`.
* - When `to` is zero, `tokenId` will be burned by `from`.
* - `from` and `to` are never both zero.
*/function_beforeTokenTransfers(addressfrom, address to, uint256 startTokenId, uint256 quantity) internalvirtual{}
/**
* @dev Hook that is called after a set of serially-ordered token ids have been transferred. This includes
* minting.
* And also called after one token has been burned.
*
* startTokenId - the first token id to be transferred
* quantity - the amount to be transferred
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, `from`'s `tokenId` has been
* transferred to `to`.
* - When `from` is zero, `tokenId` has been minted for `to`.
* - When `to` is zero, `tokenId` has been burned by `from`.
* - `from` and `to` are never both zero.
*/function_afterTokenTransfers(addressfrom, address to, uint256 startTokenId, uint256 quantity) internalvirtual{}
}
Contract Source Code
File 11 of 44: IClaimCondition.sol
// SPDX-License-Identifier: Apache-2.0pragmasolidity ^0.8.0;/// @author thirdweb/**
* The interface `IClaimCondition` is written for thirdweb's 'Drop' contracts, which are distribution mechanisms for tokens.
*
* A claim condition defines criteria under which accounts can mint tokens. Claim conditions can be overwritten
* or added to by the contract admin. At any moment, there is only one active claim condition.
*/interfaceIClaimCondition{
/**
* @notice The criteria that make up a claim condition.
*
* @param startTimestamp The unix timestamp after which the claim condition applies.
* The same claim condition applies until the `startTimestamp`
* of the next claim condition.
*
* @param maxClaimableSupply The maximum total number of tokens that can be claimed under
* the claim condition.
*
* @param supplyClaimed At any given point, the number of tokens that have been claimed
* under the claim condition.
*
* @param quantityLimitPerWallet The maximum number of tokens that can be claimed by a wallet.
*
* @param merkleRoot The allowlist of addresses that can claim tokens under the claim
* condition.
*
* @param pricePerToken The price required to pay per token claimed.
*
* @param currency The currency in which the `pricePerToken` must be paid.
*
* @param metadata Claim condition metadata.
*/structClaimCondition {
uint256 startTimestamp;
uint256 maxClaimableSupply;
uint256 supplyClaimed;
uint256 quantityLimitPerWallet;
bytes32 merkleRoot;
uint256 pricePerToken;
address currency;
string metadata;
}
}
Contract Source Code
File 12 of 44: IClaimConditionMultiPhase.sol
// SPDX-License-Identifier: Apache-2.0pragmasolidity ^0.8.0;/// @author thirdwebimport"./IClaimCondition.sol";
/**
* The interface `IClaimConditionMultiPhase` is written for thirdweb's 'Drop' contracts, which are distribution mechanisms for tokens.
*
* An authorized wallet can set a series of claim conditions, ordered by their respective `startTimestamp`.
* A claim condition defines criteria under which accounts can mint tokens. Claim conditions can be overwritten
* or added to by the contract admin. At any moment, there is only one active claim condition.
*/interfaceIClaimConditionMultiPhaseisIClaimCondition{
/**
* @notice The set of all claim conditions, at any given moment.
* Claim Phase ID = [currentStartId, currentStartId + length - 1];
*
* @param currentStartId The uid for the first claim condition amongst the current set of
* claim conditions. The uid for each next claim condition is one
* more than the previous claim condition's uid.
*
* @param count The total number of phases / claim conditions in the list
* of claim conditions.
*
* @param conditions The claim conditions at a given uid. Claim conditions
* are ordered in an ascending order by their `startTimestamp`.
*
* @param supplyClaimedByWallet Map from a claim condition uid and account to supply claimed by account.
*/structClaimConditionList {
uint256 currentStartId;
uint256 count;
mapping(uint256=> ClaimCondition) conditions;
mapping(uint256=>mapping(address=>uint256)) supplyClaimedByWallet;
}
}
Contract Source Code
File 13 of 44: IContractMetadata.sol
// SPDX-License-Identifier: Apache-2.0pragmasolidity ^0.8.0;/// @author thirdweb/**
* Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI
* for you contract.
*
* Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea.
*/interfaceIContractMetadata{
/// @dev Returns the metadata URI of the contract.functioncontractURI() externalviewreturns (stringmemory);
/**
* @dev Sets contract URI for the storefront-level metadata of the contract.
* Only module admin can call this function.
*/functionsetContractURI(stringcalldata _uri) external;
/// @dev Emitted when the contract URI is updated.eventContractURIUpdated(string prevURI, string newURI);
}
Contract Source Code
File 14 of 44: IDelayedReveal.sol
// SPDX-License-Identifier: Apache-2.0pragmasolidity ^0.8.0;/// @author thirdweb/**
* Thirdweb's `DelayedReveal` is a contract extension for base NFT contracts. It lets you create batches of
* 'delayed-reveal' NFTs. You can learn more about the usage of delayed reveal NFTs here - https://blog.thirdweb.com/delayed-reveal-nfts
*/interfaceIDelayedReveal{
/// @dev Emitted when tokens are revealed.eventTokenURIRevealed(uint256indexed index, string revealedURI);
/**
* @notice Reveals a batch of delayed reveal NFTs.
*
* @param identifier The ID for the batch of delayed-reveal NFTs to reveal.
*
* @param key The key with which the base URI for the relevant batch of NFTs was encrypted.
*/functionreveal(uint256 identifier, bytescalldata key) externalreturns (stringmemory revealedURI);
/**
* @notice Performs XOR encryption/decryption.
*
* @param data The data to encrypt. In the case of delayed-reveal NFTs, this is the "revealed" state
* base URI of the relevant batch of NFTs.
*
* @param key The key with which to encrypt data
*/functionencryptDecrypt(bytesmemory data, bytescalldata key) externalpurereturns (bytesmemory result);
}
Contract Source Code
File 15 of 44: IDrop.sol
// SPDX-License-Identifier: Apache-2.0pragmasolidity ^0.8.0;/// @author thirdwebimport"./IClaimConditionMultiPhase.sol";
/**
* The interface `IDrop` is written for thirdweb's 'Drop' contracts, which are distribution mechanisms for tokens.
*
* An authorized wallet can set a series of claim conditions, ordered by their respective `startTimestamp`.
* A claim condition defines criteria under which accounts can mint tokens. Claim conditions can be overwritten
* or added to by the contract admin. At any moment, there is only one active claim condition.
*/interfaceIDropisIClaimConditionMultiPhase{
/**
* @param proof Proof of concerned wallet's inclusion in an allowlist.
* @param quantityLimitPerWallet The total quantity of tokens the allowlisted wallet is eligible to claim over time.
* @param pricePerToken The price per token the allowlisted wallet must pay to claim tokens.
* @param currency The currency in which the allowlisted wallet must pay the price for claiming tokens.
*/structAllowlistProof {
bytes32[] proof;
uint256 quantityLimitPerWallet;
uint256 pricePerToken;
address currency;
}
/// @notice Emitted when tokens are claimed via `claim`.eventTokensClaimed(uint256indexed claimConditionIndex,
addressindexed claimer,
addressindexed receiver,
uint256 startTokenId,
uint256 quantityClaimed
);
/// @notice Emitted when the contract's claim conditions are updated.eventClaimConditionsUpdated(ClaimCondition[] claimConditions, bool resetEligibility);
/**
* @notice Lets an account claim a given quantity of NFTs.
*
* @param receiver The receiver of the NFTs to claim.
* @param quantity The quantity of NFTs to claim.
* @param currency The currency in which to pay for the claim.
* @param pricePerToken The price per token to pay for the claim.
* @param allowlistProof The proof of the claimer's inclusion in the merkle root allowlist
* of the claim conditions that apply.
* @param data Arbitrary bytes data that can be leveraged in the implementation of this interface.
*/functionclaim(address receiver,
uint256 quantity,
address currency,
uint256 pricePerToken,
AllowlistProof calldata allowlistProof,
bytesmemory data
) externalpayable;
/**
* @notice Lets a contract admin (account with `DEFAULT_ADMIN_ROLE`) set claim conditions.
*
* @param phases Claim conditions in ascending order by `startTimestamp`.
*
* @param resetClaimEligibility Whether to honor the restrictions applied to wallets who have claimed tokens in the current conditions,
* in the new claim conditions being set.
*
*/functionsetClaimConditions(ClaimCondition[] calldata phases, bool resetClaimEligibility) external;
}
Contract Source Code
File 16 of 44: IERC165.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)pragmasolidity ^0.8.0;/**
* @dev Interface of the ERC165 standard, as defined in the
* [EIP](https://eips.ethereum.org/EIPS/eip-165).
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/interfaceIERC165{
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/functionsupportsInterface(bytes4 interfaceId) externalviewreturns (bool);
}
// SPDX-License-Identifier: Apache 2.0pragmasolidity ^0.8.0;import"./IERC165.sol";
/**
* @dev Interface for the NFT Royalty Standard.
*
* A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
* support for royalty payments across all NFT marketplaces and ecosystem participants.
*
* _Available since v4.5._
*/interfaceIERC2981isIERC165{
/**
* @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
* exchange. The royalty amount is denominated and should be payed in that same unit of exchange.
*/functionroyaltyInfo(uint256 tokenId,
uint256 salePrice
) externalviewreturns (address receiver, uint256 royaltyAmount);
}
Contract Source Code
File 19 of 44: IERC721.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol)pragmasolidity ^0.8.0;/**
* @dev Required interface of an ERC721 compliant contract.
*/interfaceIERC721{
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/eventTransfer(addressindexedfrom, addressindexed to, uint256indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/eventApproval(addressindexed owner, addressindexed approved, uint256indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/eventApprovalForAll(addressindexed owner, addressindexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/functionbalanceOf(address owner) externalviewreturns (uint256);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/functionownerOf(uint256 tokenId) externalviewreturns (address);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/functionsafeTransferFrom(addressfrom, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/functiontransferFrom(addressfrom, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/functionapprove(address to, uint256 tokenId) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/functiongetApproved(uint256 tokenId) externalviewreturns (address);
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/functionsetApprovalForAll(address operator, bool _approved) external;
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/functionisApprovedForAll(address owner, address operator) externalviewreturns (bool);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/functionsafeTransferFrom(addressfrom, address to, uint256 tokenId, bytescalldata data) external;
}
Contract Source Code
File 20 of 44: IERC721A.sol
// SPDX-License-Identifier: MIT// ERC721A Contracts v3.3.0// Creator: Chiru Labspragmasolidity ^0.8.4;import"./IERC721.sol";
import"./IERC721Metadata.sol";
/**
* @dev Interface of an ERC721A compliant contract.
*/interfaceIERC721AisIERC721, IERC721Metadata{
/**
* The caller must own the token or be an approved operator.
*/errorApprovalCallerNotOwnerNorApproved();
/**
* The token does not exist.
*/errorApprovalQueryForNonexistentToken();
/**
* The caller cannot approve to their own address.
*/errorApproveToCaller();
/**
* The caller cannot approve to the current owner.
*/errorApprovalToCurrentOwner();
/**
* Cannot query the balance for the zero address.
*/errorBalanceQueryForZeroAddress();
/**
* Cannot mint to the zero address.
*/errorMintToZeroAddress();
/**
* The quantity of tokens minted must be more than zero.
*/errorMintZeroQuantity();
/**
* The token does not exist.
*/errorOwnerQueryForNonexistentToken();
/**
* The caller must own the token or be an approved operator.
*/errorTransferCallerNotOwnerNorApproved();
/**
* The token must be owned by `from`.
*/errorTransferFromIncorrectOwner();
/**
* Cannot safely transfer to a contract that does not implement the ERC721Receiver interface.
*/errorTransferToNonERC721ReceiverImplementer();
/**
* Cannot transfer to the zero address.
*/errorTransferToZeroAddress();
/**
* The token does not exist.
*/errorURIQueryForNonexistentToken();
// Compiler will pack this into a single 256bit word.structTokenOwnership {
// The address of the owner.address addr;
// Keeps track of the start time of ownership with minimal overhead for tokenomics.uint64 startTimestamp;
// Whether the token has been burned.bool burned;
}
// Compiler will pack this into a single 256bit word.structAddressData {
// Realistically, 2**64-1 is more than enough.uint64 balance;
// Keeps track of mint count with minimal overhead for tokenomics.uint64 numberMinted;
// Keeps track of burn count with minimal overhead for tokenomics.uint64 numberBurned;
// For miscellaneous variable(s) pertaining to the address// (e.g. number of whitelist mint slots used).// If there are multiple variables, please pack them into a uint64.uint64 aux;
}
/**
* @dev Returns the total amount of tokens stored by the contract.
*
* Burned tokens are calculated here, use `_totalMinted()` if you want to count just minted tokens.
*/functiontotalSupply() externalviewreturns (uint256);
}
Contract Source Code
File 21 of 44: IERC721AQueryable.sol
// SPDX-License-Identifier: MIT// ERC721A Contracts v3.3.0// Creator: Chiru Labspragmasolidity ^0.8.4;import"../interface/IERC721A.sol";
/**
* @dev Interface of an ERC721AQueryable compliant contract.
*/interfaceIERC721AQueryableisIERC721A{
/**
* Invalid query range (`start` >= `stop`).
*/errorInvalidQueryRange();
/**
* @dev Returns the `TokenOwnership` struct at `tokenId` without reverting.
*
* If the `tokenId` is out of bounds:
* - `addr` = `address(0)`
* - `startTimestamp` = `0`
* - `burned` = `false`
*
* If the `tokenId` is burned:
* - `addr` = `<Address of owner before token was burned>`
* - `startTimestamp` = `<Timestamp when token was burned>`
* - `burned = `true`
*
* Otherwise:
* - `addr` = `<Address of owner>`
* - `startTimestamp` = `<Timestamp of start of ownership>`
* - `burned = `false`
*/functionexplicitOwnershipOf(uint256 tokenId) externalviewreturns (TokenOwnership memory);
/**
* @dev Returns an array of `TokenOwnership` structs at `tokenIds` in order.
* See {ERC721AQueryable-explicitOwnershipOf}
*/functionexplicitOwnershipsOf(uint256[] memory tokenIds) externalviewreturns (TokenOwnership[] memory);
/**
* @dev Returns an array of token IDs owned by `owner`,
* in the range [`start`, `stop`)
* (i.e. `start <= tokenId < stop`).
*
* This function allows for tokens to be queried if the collection
* grows too big for a single call of {ERC721AQueryable-tokensOfOwner}.
*
* Requirements:
*
* - `start` < `stop`
*/functiontokensOfOwnerIn(address owner, uint256 start, uint256 stop) externalviewreturns (uint256[] memory);
/**
* @dev Returns an array of token IDs owned by `owner`.
*
* This function scans the ownership mapping and is O(totalSupply) in complexity.
* It is meant to be called off-chain.
*
* See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into
* multiple smaller scans if the collection is large enough to cause
* an out-of-gas error (10K pfp collections should be fine).
*/functiontokensOfOwner(address owner) externalviewreturns (uint256[] memory);
}
Contract Source Code
File 22 of 44: IERC721Metadata.sol
// SPDX-License-Identifier: Apache-2.0pragmasolidity ^0.8.0;/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension/// @dev See https://eips.ethereum.org/EIPS/eip-721/// Note: the ERC-165 identifier for this interface is 0x5b5e139f./* is ERC721 */interfaceIERC721Metadata{
/// @notice A descriptive name for a collection of NFTs in this contractfunctionname() externalviewreturns (stringmemory);
/// @notice An abbreviated name for NFTs in this contractfunctionsymbol() externalviewreturns (stringmemory);
/// @notice A distinct Uniform Resource Identifier (URI) for a given asset./// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC/// 3986. The URI may point to a JSON file that conforms to the "ERC721/// Metadata JSON Schema".functiontokenURI(uint256 _tokenId) externalviewreturns (stringmemory);
}
Contract Source Code
File 23 of 44: IERC721Receiver.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)pragmasolidity ^0.8.0;/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/interfaceIERC721Receiver{
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
*
* The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
*/functiononERC721Received(address operator,
addressfrom,
uint256 tokenId,
bytescalldata data
) externalreturns (bytes4);
}
Contract Source Code
File 24 of 44: ILazyMint.sol
// SPDX-License-Identifier: Apache-2.0pragmasolidity ^0.8.0;/// @author thirdweb/**
* Thirdweb's `LazyMint` is a contract extension for any base NFT contract. It lets you 'lazy mint' any number of NFTs
* at once. Here, 'lazy mint' means defining the metadata for particular tokenIds of your NFT contract, without actually
* minting a non-zero balance of NFTs of those tokenIds.
*/interfaceILazyMint{
/// @dev Emitted when tokens are lazy minted.eventTokensLazyMinted(uint256indexed startTokenId, uint256 endTokenId, string baseURI, bytes encryptedBaseURI);
/**
* @notice Lazy mints a given amount of NFTs.
*
* @param amount The number of NFTs to lazy mint.
*
* @param baseURIForTokens The base URI for the 'n' number of NFTs being lazy minted, where the metadata for each
* of those NFTs is `${baseURIForTokens}/${tokenId}`.
*
* @param extraData Additional bytes data to be used at the discretion of the consumer of the contract.
*
* @return batchId A unique integer identifier for the batch of NFTs lazy minted together.
*/functionlazyMint(uint256 amount,
stringcalldata baseURIForTokens,
bytescalldata extraData
) externalreturns (uint256 batchId);
}
Contract Source Code
File 25 of 44: IMulticall.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;/// @author thirdweb/**
* @dev Provides a function to batch together multiple calls in a single external call.
*
* _Available since v4.1._
*/interfaceIMulticall{
/**
* @dev Receives and executes a batch of function calls on this contract.
*/functionmulticall(bytes[] calldata data) externalreturns (bytes[] memory results);
}
Contract Source Code
File 26 of 44: IOwnable.sol
// SPDX-License-Identifier: Apache-2.0pragmasolidity ^0.8.0;/// @author thirdweb/**
* Thirdweb's `Ownable` is a contract extension to be used with any base contract. It exposes functions for setting and reading
* who the 'owner' of the inheriting smart contract is, and lets the inheriting contract perform conditional logic that uses
* information about who the contract's owner is.
*/interfaceIOwnable{
/// @dev Returns the owner of the contract.functionowner() externalviewreturns (address);
/// @dev Lets a module admin set a new owner for the contract. The new owner must be a module admin.functionsetOwner(address _newOwner) external;
/// @dev Emitted when a new Owner is set.eventOwnerUpdated(addressindexed prevOwner, addressindexed newOwner);
}
Contract Source Code
File 27 of 44: IPermissions.sol
// SPDX-License-Identifier: Apache-2.0pragmasolidity ^0.8.0;/// @author thirdweb/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/interfaceIPermissions{
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/eventRoleAdminChanged(bytes32indexed role, bytes32indexed previousAdminRole, bytes32indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/eventRoleGranted(bytes32indexed role, addressindexed account, addressindexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/eventRoleRevoked(bytes32indexed role, addressindexed account, addressindexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/functionhasRole(bytes32 role, address account) externalviewreturns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/functiongetRoleAdmin(bytes32 role) externalviewreturns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/functiongrantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/functionrevokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/functionrenounceRole(bytes32 role, address account) external;
}
Contract Source Code
File 28 of 44: IPermissionsEnumerable.sol
// SPDX-License-Identifier: Apache-2.0pragmasolidity ^0.8.0;/// @author thirdwebimport"./IPermissions.sol";
/**
* @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
*/interfaceIPermissionsEnumerableisIPermissions{
/**
* @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive.
*
* Role bearers are not sorted in any particular way, and their ordering may
* change at any point.
*
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
* you perform all queries on the same block. See the following
* [forum post](https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296)
* for more information.
*/functiongetRoleMember(bytes32 role, uint256 index) externalviewreturns (address);
/**
* @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role.
*/functiongetRoleMemberCount(bytes32 role) externalviewreturns (uint256);
}
Contract Source Code
File 29 of 44: IPlatformFee.sol
// SPDX-License-Identifier: Apache-2.0pragmasolidity ^0.8.0;/// @author thirdweb/**
* Thirdweb's `PlatformFee` is a contract extension to be used with any base contract. It exposes functions for setting and reading
* the recipient of platform fee and the platform fee basis points, and lets the inheriting contract perform conditional logic
* that uses information about platform fees, if desired.
*/interfaceIPlatformFee{
/// @dev Fee type variants: percentage fee and flat feeenumPlatformFeeType {
Bps,
Flat
}
/// @dev Returns the platform fee bps and recipient.functiongetPlatformFeeInfo() externalviewreturns (address, uint16);
/// @dev Lets a module admin update the fees on primary sales.functionsetPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) external;
/// @dev Emitted when fee on primary sales is updated.eventPlatformFeeInfoUpdated(addressindexed platformFeeRecipient, uint256 platformFeeBps);
/// @dev Emitted when the flat platform fee is updated.eventFlatPlatformFeeUpdated(address platformFeeRecipient, uint256 flatFee);
/// @dev Emitted when the platform fee type is updated.eventPlatformFeeTypeUpdated(PlatformFeeType feeType);
}
Contract Source Code
File 30 of 44: IPrimarySale.sol
// SPDX-License-Identifier: Apache-2.0pragmasolidity ^0.8.0;/// @author thirdweb/**
* Thirdweb's `Primary` is a contract extension to be used with any base contract. It exposes functions for setting and reading
* the recipient of primary sales, and lets the inheriting contract perform conditional logic that uses information about
* primary sales, if desired.
*/interfaceIPrimarySale{
/// @dev The adress that receives all primary sales value.functionprimarySaleRecipient() externalviewreturns (address);
/// @dev Lets a module admin set the default recipient of all primary sales.functionsetPrimarySaleRecipient(address _saleRecipient) external;
/// @dev Emitted when a new sale recipient is set.eventPrimarySaleRecipientUpdated(addressindexed recipient);
}
Contract Source Code
File 31 of 44: IRoyalty.sol
// SPDX-License-Identifier: Apache-2.0pragmasolidity ^0.8.0;/// @author thirdwebimport"../../eip/interface/IERC2981.sol";
/**
* Thirdweb's `Royalty` is a contract extension to be used with any base contract. It exposes functions for setting and reading
* the recipient of royalty fee and the royalty fee basis points, and lets the inheriting contract perform conditional logic
* that uses information about royalty fees, if desired.
*
* The `Royalty` contract is ERC2981 compliant.
*/interfaceIRoyaltyisIERC2981{
structRoyaltyInfo {
address recipient;
uint256 bps;
}
/// @dev Returns the royalty recipient and fee bps.functiongetDefaultRoyaltyInfo() externalviewreturns (address, uint16);
/// @dev Lets a module admin update the royalty bps and recipient.functionsetDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) external;
/// @dev Lets a module admin set the royalty recipient for a particular token Id.functionsetRoyaltyInfoForToken(uint256 tokenId, address recipient, uint256 bps) external;
/// @dev Returns the royalty recipient for a particular token Id.functiongetRoyaltyInfoForToken(uint256 tokenId) externalviewreturns (address, uint16);
/// @dev Emitted when royalty info is updated.eventDefaultRoyalty(addressindexed newRoyaltyRecipient, uint256 newRoyaltyBps);
/// @dev Emitted when royalty recipient for tokenId is seteventRoyaltyForToken(uint256indexed tokenId, addressindexed royaltyRecipient, uint256 royaltyBps);
}
// SPDX-License-Identifier: Apache-2.0pragmasolidity ^0.8.0;/// @author thirdwebimport"./interface/ILazyMint.sol";
import"./BatchMintMetadata.sol";
/**
* The `LazyMint` is a contract extension for any base NFT contract. It lets you 'lazy mint' any number of NFTs
* at once. Here, 'lazy mint' means defining the metadata for particular tokenIds of your NFT contract, without actually
* minting a non-zero balance of NFTs of those tokenIds.
*/abstractcontractLazyMintisILazyMint, BatchMintMetadata{
/// @dev The sender is not authorized to perform the actionerrorLazyMintUnauthorized();
errorLazyMintInvalidAmount();
/// @notice The tokenId assigned to the next new NFT to be lazy minted.uint256internal nextTokenIdToLazyMint;
/**
* @notice Lets an authorized address lazy mint a given amount of NFTs.
*
* @param _amount The number of NFTs to lazy mint.
* @param _baseURIForTokens The base URI for the 'n' number of NFTs being lazy minted, where the metadata for each
* of those NFTs is `${baseURIForTokens}/${tokenId}`.
* @param _data Additional bytes data to be used at the discretion of the consumer of the contract.
* @return batchId A unique integer identifier for the batch of NFTs lazy minted together.
*/functionlazyMint(uint256 _amount,
stringcalldata _baseURIForTokens,
bytescalldata _data
) publicvirtualoverridereturns (uint256 batchId) {
if (!_canLazyMint()) {
revert LazyMintUnauthorized();
}
if (_amount ==0) {
revert LazyMintInvalidAmount();
}
uint256 startId = nextTokenIdToLazyMint;
(nextTokenIdToLazyMint, batchId) = _batchMintMetadata(startId, _amount, _baseURIForTokens);
emit TokensLazyMinted(startId, startId + _amount -1, _baseURIForTokens, _data);
return batchId;
}
/// @dev Returns whether lazy minting can be performed in the given execution context.function_canLazyMint() internalviewvirtualreturns (bool);
}
Contract Source Code
File 34 of 44: MerkleProof.sol
// SPDX-License-Identifier: Apache 2.0pragmasolidity ^0.8.0;/// @author OpenZeppelin, thirdweblibraryMerkleProof{
functionverify(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internalpurereturns (bool, uint256) {
bytes32 computedHash = leaf;
uint256 index =0;
for (uint256 i =0; i < proof.length; i++) {
index *=2;
bytes32 proofElement = proof[i];
if (computedHash <= proofElement) {
// Hash(current computed hash + current element of the proof)
computedHash = _efficientHash(computedHash, proofElement);
} else {
// Hash(current element of the proof + current computed hash)
computedHash = _efficientHash(proofElement, computedHash);
index +=1;
}
}
// Check if the computed hash (root) is equal to the provided rootreturn (computedHash == root, index);
}
/**
* @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
*/function_efficientHash(bytes32 a, bytes32 b) privatepurereturns (bytes32 value) {
/// @solidity memory-safe-assemblyassembly {
mstore(0x00, a)
mstore(0x20, b)
value :=keccak256(0x00, 0x40)
}
}
}
Contract Source Code
File 35 of 44: Multicall.sol
// SPDX-License-Identifier: Apache 2.0pragmasolidity ^0.8.0;/// @author thirdwebimport"../lib/Address.sol";
import"./interface/IMulticall.sol";
/**
* @dev Provides a function to batch together multiple calls in a single external call.
*
* _Available since v4.1._
*/contractMulticallisIMulticall{
/**
* @notice Receives and executes a batch of function calls on this contract.
* @dev Receives and executes a batch of function calls on this contract.
*
* @param data The bytes data that makes up the batch of function calls to execute.
* @return results The bytes data that makes up the result of the batch of function calls executed.
*/functionmulticall(bytes[] calldata data) externalreturns (bytes[] memory results) {
results =newbytes[](data.length);
address sender = _msgSender();
bool isForwarder =msg.sender!= sender;
for (uint256 i =0; i < data.length; i++) {
if (isForwarder) {
results[i] = Address.functionDelegateCall(address(this), abi.encodePacked(data[i], sender));
} else {
results[i] = Address.functionDelegateCall(address(this), data[i]);
}
}
return results;
}
/// @notice Returns the sender in the given execution context.function_msgSender() internalviewvirtualreturns (address) {
returnmsg.sender;
}
}
Contract Source Code
File 36 of 44: Ownable.sol
// SPDX-License-Identifier: Apache-2.0pragmasolidity ^0.8.0;/// @author thirdwebimport"./interface/IOwnable.sol";
/**
* @title Ownable
* @notice Thirdweb's `Ownable` is a contract extension to be used with any base contract. It exposes functions for setting and reading
* who the 'owner' of the inheriting smart contract is, and lets the inheriting contract perform conditional logic that uses
* information about who the contract's owner is.
*/abstractcontractOwnableisIOwnable{
/// @dev The sender is not authorized to perform the actionerrorOwnableUnauthorized();
/// @dev Owner of the contract (purpose: OpenSea compatibility)addressprivate _owner;
/// @dev Reverts if caller is not the owner.modifieronlyOwner() {
if (msg.sender!= _owner) {
revert OwnableUnauthorized();
}
_;
}
/**
* @notice Returns the owner of the contract.
*/functionowner() publicviewoverridereturns (address) {
return _owner;
}
/**
* @notice Lets an authorized wallet set a new owner for the contract.
* @param _newOwner The address to set as the new owner of the contract.
*/functionsetOwner(address _newOwner) externaloverride{
if (!_canSetOwner()) {
revert OwnableUnauthorized();
}
_setupOwner(_newOwner);
}
/// @dev Lets a contract admin set a new owner for the contract. The new owner must be a contract admin.function_setupOwner(address _newOwner) internal{
address _prevOwner = _owner;
_owner = _newOwner;
emit OwnerUpdated(_prevOwner, _newOwner);
}
/// @dev Returns whether owner can be set in the given execution context.function_canSetOwner() internalviewvirtualreturns (bool);
}
Contract Source Code
File 37 of 44: Permissions.sol
// SPDX-License-Identifier: Apache-2.0pragmasolidity ^0.8.0;/// @author thirdwebimport"./interface/IPermissions.sol";
import"../lib/Strings.sol";
/**
* @title Permissions
* @dev This contracts provides extending-contracts with role-based access control mechanisms
*/contractPermissionsisIPermissions{
/// @dev The `account` is missing a role.errorPermissionsUnauthorizedAccount(address account, bytes32 neededRole);
/// @dev The `account` already is a holder of `role`errorPermissionsAlreadyGranted(address account, bytes32 role);
/// @dev Invalid priviledge to revokeerrorPermissionsInvalidPermission(address expected, address actual);
/// @dev Map from keccak256 hash of a role => a map from address => whether address has role.mapping(bytes32=>mapping(address=>bool)) private _hasRole;
/// @dev Map from keccak256 hash of a role to role admin. See {getRoleAdmin}.mapping(bytes32=>bytes32) private _getRoleAdmin;
/// @dev Default admin role for all roles. Only accounts with this role can grant/revoke other roles.bytes32publicconstant DEFAULT_ADMIN_ROLE =0x00;
/// @dev Modifier that checks if an account has the specified role; reverts otherwise.modifieronlyRole(bytes32 role) {
_checkRole(role, msg.sender);
_;
}
/**
* @notice Checks whether an account has a particular role.
* @dev Returns `true` if `account` has been granted `role`.
*
* @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
* @param account Address of the account for which the role is being checked.
*/functionhasRole(bytes32 role, address account) publicviewoverridereturns (bool) {
return _hasRole[role][account];
}
/**
* @notice Checks whether an account has a particular role;
* role restrictions can be swtiched on and off.
*
* @dev Returns `true` if `account` has been granted `role`.
* Role restrictions can be swtiched on and off:
* - If address(0) has ROLE, then the ROLE restrictions
* don't apply.
* - If address(0) does not have ROLE, then the ROLE
* restrictions will apply.
*
* @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
* @param account Address of the account for which the role is being checked.
*/functionhasRoleWithSwitch(bytes32 role, address account) publicviewreturns (bool) {
if (!_hasRole[role][address(0)]) {
return _hasRole[role][account];
}
returntrue;
}
/**
* @notice Returns the admin role that controls the specified role.
* @dev See {grantRole} and {revokeRole}.
* To change a role's admin, use {_setRoleAdmin}.
*
* @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
*/functiongetRoleAdmin(bytes32 role) externalviewoverridereturns (bytes32) {
return _getRoleAdmin[role];
}
/**
* @notice Grants a role to an account, if not previously granted.
* @dev Caller must have admin role for the `role`.
* Emits {RoleGranted Event}.
*
* @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
* @param account Address of the account to which the role is being granted.
*/functiongrantRole(bytes32 role, address account) publicvirtualoverride{
_checkRole(_getRoleAdmin[role], msg.sender);
if (_hasRole[role][account]) {
revert PermissionsAlreadyGranted(account, role);
}
_setupRole(role, account);
}
/**
* @notice Revokes role from an account.
* @dev Caller must have admin role for the `role`.
* Emits {RoleRevoked Event}.
*
* @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
* @param account Address of the account from which the role is being revoked.
*/functionrevokeRole(bytes32 role, address account) publicvirtualoverride{
_checkRole(_getRoleAdmin[role], msg.sender);
_revokeRole(role, account);
}
/**
* @notice Revokes role from the account.
* @dev Caller must have the `role`, with caller being the same as `account`.
* Emits {RoleRevoked Event}.
*
* @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
* @param account Address of the account from which the role is being revoked.
*/functionrenounceRole(bytes32 role, address account) publicvirtualoverride{
if (msg.sender!= account) {
revert PermissionsInvalidPermission(msg.sender, account);
}
_revokeRole(role, account);
}
/// @dev Sets `adminRole` as `role`'s admin role.function_setRoleAdmin(bytes32 role, bytes32 adminRole) internalvirtual{
bytes32 previousAdminRole = _getRoleAdmin[role];
_getRoleAdmin[role] = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/// @dev Sets up `role` for `account`function_setupRole(bytes32 role, address account) internalvirtual{
_hasRole[role][account] =true;
emit RoleGranted(role, account, msg.sender);
}
/// @dev Revokes `role` from `account`function_revokeRole(bytes32 role, address account) internalvirtual{
_checkRole(role, account);
delete _hasRole[role][account];
emit RoleRevoked(role, account, msg.sender);
}
/// @dev Checks `role` for `account`. Reverts with a message including the required role.function_checkRole(bytes32 role, address account) internalviewvirtual{
if (!_hasRole[role][account]) {
revert PermissionsUnauthorizedAccount(account, role);
}
}
/// @dev Checks `role` for `account`. Reverts with a message including the required role.function_checkRoleWithSwitch(bytes32 role, address account) internalviewvirtual{
if (!hasRoleWithSwitch(role, account)) {
revert PermissionsUnauthorizedAccount(account, role);
}
}
}
Contract Source Code
File 38 of 44: PermissionsEnumerable.sol
// SPDX-License-Identifier: Apache-2.0pragmasolidity ^0.8.0;/// @author thirdwebimport"./interface/IPermissionsEnumerable.sol";
import"./Permissions.sol";
/**
* @title PermissionsEnumerable
* @dev This contracts provides extending-contracts with role-based access control mechanisms.
* Also provides interfaces to view all members with a given role, and total count of members.
*/contractPermissionsEnumerableisIPermissionsEnumerable, Permissions{
/**
* @notice A data structure to store data of members for a given role.
*
* @param index Current index in the list of accounts that have a role.
* @param members map from index => address of account that has a role
* @param indexOf map from address => index which the account has.
*/structRoleMembers {
uint256 index;
mapping(uint256=>address) members;
mapping(address=>uint256) indexOf;
}
/// @dev map from keccak256 hash of a role to its members' data. See {RoleMembers}.mapping(bytes32=> RoleMembers) private roleMembers;
/**
* @notice Returns the role-member from a list of members for a role,
* at a given index.
* @dev Returns `member` who has `role`, at `index` of role-members list.
* See struct {RoleMembers}, and mapping {roleMembers}
*
* @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
* @param index Index in list of current members for the role.
*
* @return member Address of account that has `role`
*/functiongetRoleMember(bytes32 role, uint256 index) externalviewoverridereturns (address member) {
uint256 currentIndex = roleMembers[role].index;
uint256 check;
for (uint256 i =0; i < currentIndex; i +=1) {
if (roleMembers[role].members[i] !=address(0)) {
if (check == index) {
member = roleMembers[role].members[i];
return member;
}
check +=1;
} elseif (hasRole(role, address(0)) && i == roleMembers[role].indexOf[address(0)]) {
check +=1;
}
}
}
/**
* @notice Returns total number of accounts that have a role.
* @dev Returns `count` of accounts that have `role`.
* See struct {RoleMembers}, and mapping {roleMembers}
*
* @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
*
* @return count Total number of accounts that have `role`
*/functiongetRoleMemberCount(bytes32 role) externalviewoverridereturns (uint256 count) {
uint256 currentIndex = roleMembers[role].index;
for (uint256 i =0; i < currentIndex; i +=1) {
if (roleMembers[role].members[i] !=address(0)) {
count +=1;
}
}
if (hasRole(role, address(0))) {
count +=1;
}
}
/// @dev Revokes `role` from `account`, and removes `account` from {roleMembers}/// See {_removeMember}function_revokeRole(bytes32 role, address account) internaloverride{
super._revokeRole(role, account);
_removeMember(role, account);
}
/// @dev Grants `role` to `account`, and adds `account` to {roleMembers}/// See {_addMember}function_setupRole(bytes32 role, address account) internaloverride{
super._setupRole(role, account);
_addMember(role, account);
}
/// @dev adds `account` to {roleMembers}, for `role`function_addMember(bytes32 role, address account) internal{
uint256 idx = roleMembers[role].index;
roleMembers[role].index +=1;
roleMembers[role].members[idx] = account;
roleMembers[role].indexOf[account] = idx;
}
/// @dev removes `account` from {roleMembers}, for `role`function_removeMember(bytes32 role, address account) internal{
uint256 idx = roleMembers[role].indexOf[account];
delete roleMembers[role].members[idx];
delete roleMembers[role].indexOf[account];
}
}
Contract Source Code
File 39 of 44: PlatformFee.sol
// SPDX-License-Identifier: Apache-2.0pragmasolidity ^0.8.0;/// @author thirdwebimport"./interface/IPlatformFee.sol";
/**
* @title Platform Fee
* @notice Thirdweb's `PlatformFee` is a contract extension to be used with any base contract. It exposes functions for setting and reading
* the recipient of platform fee and the platform fee basis points, and lets the inheriting contract perform conditional logic
* that uses information about platform fees, if desired.
*/abstractcontractPlatformFeeisIPlatformFee{
/// @dev The sender is not authorized to perform the actionerrorPlatformFeeUnauthorized();
/// @dev The recipient is invaliderrorPlatformFeeInvalidRecipient(address recipient);
/// @dev The fee bps exceeded the max valueerrorPlatformFeeExceededMaxFeeBps(uint256 max, uint256 actual);
/// @dev The address that receives all platform fees from all sales.addressprivate platformFeeRecipient;
/// @dev The % of primary sales collected as platform fees.uint16private platformFeeBps;
/// @dev Fee type variants: percentage fee and flat fee
PlatformFeeType private platformFeeType;
/// @dev The flat amount collected by the contract as fees on primary sales.uint256private flatPlatformFee;
/// @dev Returns the platform fee recipient and bps.functiongetPlatformFeeInfo() publicviewoverridereturns (address, uint16) {
return (platformFeeRecipient, uint16(platformFeeBps));
}
/// @dev Returns the platform fee bps and recipient.functiongetFlatPlatformFeeInfo() publicviewreturns (address, uint256) {
return (platformFeeRecipient, flatPlatformFee);
}
/// @dev Returns the platform fee type.functiongetPlatformFeeType() publicviewreturns (PlatformFeeType) {
return platformFeeType;
}
/**
* @notice Updates the platform fee recipient and bps.
* @dev Caller should be authorized to set platform fee info.
* See {_canSetPlatformFeeInfo}.
* Emits {PlatformFeeInfoUpdated Event}; See {_setupPlatformFeeInfo}.
*
* @param _platformFeeRecipient Address to be set as new platformFeeRecipient.
* @param _platformFeeBps Updated platformFeeBps.
*/functionsetPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) externaloverride{
if (!_canSetPlatformFeeInfo()) {
revert PlatformFeeUnauthorized();
}
_setupPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps);
}
/// @dev Sets the platform fee recipient and bpsfunction_setupPlatformFeeInfo(address _platformFeeRecipient, uint256 _platformFeeBps) internal{
if (_platformFeeBps >10_000) {
revert PlatformFeeExceededMaxFeeBps(10_000, _platformFeeBps);
}
if (_platformFeeRecipient ==address(0)) {
revert PlatformFeeInvalidRecipient(_platformFeeRecipient);
}
platformFeeBps =uint16(_platformFeeBps);
platformFeeRecipient = _platformFeeRecipient;
emit PlatformFeeInfoUpdated(_platformFeeRecipient, _platformFeeBps);
}
/// @notice Lets a module admin set a flat fee on primary sales.functionsetFlatPlatformFeeInfo(address _platformFeeRecipient, uint256 _flatFee) external{
if (!_canSetPlatformFeeInfo()) {
revert PlatformFeeUnauthorized();
}
_setupFlatPlatformFeeInfo(_platformFeeRecipient, _flatFee);
}
/// @dev Sets a flat fee on primary sales.function_setupFlatPlatformFeeInfo(address _platformFeeRecipient, uint256 _flatFee) internal{
flatPlatformFee = _flatFee;
platformFeeRecipient = _platformFeeRecipient;
emit FlatPlatformFeeUpdated(_platformFeeRecipient, _flatFee);
}
/// @notice Lets a module admin set platform fee type.functionsetPlatformFeeType(PlatformFeeType _feeType) external{
if (!_canSetPlatformFeeInfo()) {
revert PlatformFeeUnauthorized();
}
_setupPlatformFeeType(_feeType);
}
/// @dev Sets platform fee type.function_setupPlatformFeeType(PlatformFeeType _feeType) internal{
platformFeeType = _feeType;
emit PlatformFeeTypeUpdated(_feeType);
}
/// @dev Returns whether platform fee info can be set in the given execution context.function_canSetPlatformFeeInfo() internalviewvirtualreturns (bool);
}
Contract Source Code
File 40 of 44: PrimarySale.sol
// SPDX-License-Identifier: Apache-2.0pragmasolidity ^0.8.0;/// @author thirdwebimport"./interface/IPrimarySale.sol";
/**
* @title Primary Sale
* @notice Thirdweb's `PrimarySale` is a contract extension to be used with any base contract. It exposes functions for setting and reading
* the recipient of primary sales, and lets the inheriting contract perform conditional logic that uses information about
* primary sales, if desired.
*/abstractcontractPrimarySaleisIPrimarySale{
/// @dev The sender is not authorized to perform the actionerrorPrimarySaleUnauthorized();
/// @dev The recipient is invaliderrorPrimarySaleInvalidRecipient(address recipient);
/// @dev The address that receives all primary sales value.addressprivate recipient;
/// @dev Returns primary sale recipient address.functionprimarySaleRecipient() publicviewoverridereturns (address) {
return recipient;
}
/**
* @notice Updates primary sale recipient.
* @dev Caller should be authorized to set primary sales info.
* See {_canSetPrimarySaleRecipient}.
* Emits {PrimarySaleRecipientUpdated Event}; See {_setupPrimarySaleRecipient}.
*
* @param _saleRecipient Address to be set as new recipient of primary sales.
*/functionsetPrimarySaleRecipient(address _saleRecipient) externaloverride{
if (!_canSetPrimarySaleRecipient()) {
revert PrimarySaleUnauthorized();
}
_setupPrimarySaleRecipient(_saleRecipient);
}
/// @dev Lets a contract admin set the recipient for all primary sales.function_setupPrimarySaleRecipient(address _saleRecipient) internal{
if (_saleRecipient ==address(0)) {
revert PrimarySaleInvalidRecipient(_saleRecipient);
}
recipient = _saleRecipient;
emit PrimarySaleRecipientUpdated(_saleRecipient);
}
/// @dev Returns whether primary sale recipient can be set in the given execution context.function_canSetPrimarySaleRecipient() internalviewvirtualreturns (bool);
}
Contract Source Code
File 41 of 44: Royalty.sol
// SPDX-License-Identifier: Apache-2.0pragmasolidity ^0.8.0;/// @author thirdwebimport"./interface/IRoyalty.sol";
/**
* @title Royalty
* @notice Thirdweb's `Royalty` is a contract extension to be used with any base contract. It exposes functions for setting and reading
* the recipient of royalty fee and the royalty fee basis points, and lets the inheriting contract perform conditional logic
* that uses information about royalty fees, if desired.
*
* @dev The `Royalty` contract is ERC2981 compliant.
*/abstractcontractRoyaltyisIRoyalty{
/// @dev The sender is not authorized to perform the actionerrorRoyaltyUnauthorized();
/// @dev The recipient is invaliderrorRoyaltyInvalidRecipient(address recipient);
/// @dev The fee bps exceeded the max valueerrorRoyaltyExceededMaxFeeBps(uint256 max, uint256 actual);
/// @dev The (default) address that receives all royalty value.addressprivate royaltyRecipient;
/// @dev The (default) % of a sale to take as royalty (in basis points).uint16private royaltyBps;
/// @dev Token ID => royalty recipient and bps for tokenmapping(uint256=> RoyaltyInfo) private royaltyInfoForToken;
/**
* @notice View royalty info for a given token and sale price.
* @dev Returns royalty amount and recipient for `tokenId` and `salePrice`.
* @param tokenId The tokenID of the NFT for which to query royalty info.
* @param salePrice Sale price of the token.
*
* @return receiver Address of royalty recipient account.
* @return royaltyAmount Royalty amount calculated at current royaltyBps value.
*/functionroyaltyInfo(uint256 tokenId,
uint256 salePrice
) externalviewvirtualoverridereturns (address receiver, uint256 royaltyAmount) {
(address recipient, uint256 bps) = getRoyaltyInfoForToken(tokenId);
receiver = recipient;
royaltyAmount = (salePrice * bps) /10_000;
}
/**
* @notice View royalty info for a given token.
* @dev Returns royalty recipient and bps for `_tokenId`.
* @param _tokenId The tokenID of the NFT for which to query royalty info.
*/functiongetRoyaltyInfoForToken(uint256 _tokenId) publicviewoverridereturns (address, uint16) {
RoyaltyInfo memory royaltyForToken = royaltyInfoForToken[_tokenId];
return
royaltyForToken.recipient ==address(0)
? (royaltyRecipient, uint16(royaltyBps))
: (royaltyForToken.recipient, uint16(royaltyForToken.bps));
}
/**
* @notice Returns the defualt royalty recipient and BPS for this contract's NFTs.
*/functiongetDefaultRoyaltyInfo() externalviewoverridereturns (address, uint16) {
return (royaltyRecipient, uint16(royaltyBps));
}
/**
* @notice Updates default royalty recipient and bps.
* @dev Caller should be authorized to set royalty info.
* See {_canSetRoyaltyInfo}.
* Emits {DefaultRoyalty Event}; See {_setupDefaultRoyaltyInfo}.
*
* @param _royaltyRecipient Address to be set as default royalty recipient.
* @param _royaltyBps Updated royalty bps.
*/functionsetDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) externaloverride{
if (!_canSetRoyaltyInfo()) {
revert RoyaltyUnauthorized();
}
_setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps);
}
/// @dev Lets a contract admin update the default royalty recipient and bps.function_setupDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) internal{
if (_royaltyBps >10_000) {
revert RoyaltyExceededMaxFeeBps(10_000, _royaltyBps);
}
royaltyRecipient = _royaltyRecipient;
royaltyBps =uint16(_royaltyBps);
emit DefaultRoyalty(_royaltyRecipient, _royaltyBps);
}
/**
* @notice Updates default royalty recipient and bps for a particular token.
* @dev Sets royalty info for `_tokenId`. Caller should be authorized to set royalty info.
* See {_canSetRoyaltyInfo}.
* Emits {RoyaltyForToken Event}; See {_setupRoyaltyInfoForToken}.
*
* @param _recipient Address to be set as royalty recipient for given token Id.
* @param _bps Updated royalty bps for the token Id.
*/functionsetRoyaltyInfoForToken(uint256 _tokenId, address _recipient, uint256 _bps) externaloverride{
if (!_canSetRoyaltyInfo()) {
revert RoyaltyUnauthorized();
}
_setupRoyaltyInfoForToken(_tokenId, _recipient, _bps);
}
/// @dev Lets a contract admin set the royalty recipient and bps for a particular token Id.function_setupRoyaltyInfoForToken(uint256 _tokenId, address _recipient, uint256 _bps) internal{
if (_bps >10_000) {
revert RoyaltyExceededMaxFeeBps(10_000, _bps);
}
royaltyInfoForToken[_tokenId] = RoyaltyInfo({ recipient: _recipient, bps: _bps });
emit RoyaltyForToken(_tokenId, _recipient, _bps);
}
/// @dev Returns whether royalty info can be set in the given execution context.function_canSetRoyaltyInfo() internalviewvirtualreturns (bool);
}
Contract Source Code
File 42 of 44: SafeERC20.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (token/ERC20/utils/SafeERC20.sol)pragmasolidity ^0.8.0;import"../../../../../eip/interface/IERC20.sol";
import { Address } from"../../../../../lib/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/librarySafeERC20{
usingAddressforaddress;
functionsafeTransfer(IERC20 token, address to, uint256 value) internal{
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
functionsafeTransferFrom(IERC20 token, addressfrom, address to, uint256 value) internal{
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/functionsafeApprove(IERC20 token, address spender, uint256 value) internal{
// safeApprove should only be called when setting an initial allowance,// or when resetting it to zero. To increase and decrease it, use// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'require(
(value ==0) || (token.allowance(address(this), spender) ==0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
functionsafeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal{
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
functionsafeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal{
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/function_callOptionalReturn(IERC20 token, bytesmemory data) private{
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that// the target address contains contract code and also asserts for success in the low-level call.bytesmemory returndata =address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length>0) {
// Return data is optionalrequire(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
Contract Source Code
File 43 of 44: Strings.sol
// SPDX-License-Identifier: Apache-2.0pragmasolidity ^0.8.0;/// @author thirdweb/**
* @dev String operations.
*/libraryStrings{
bytes16privateconstant _HEX_SYMBOLS ="0123456789abcdef";
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/functiontoString(uint256 value) internalpurereturns (stringmemory) {
// Inspired by OraclizeAPI's implementation - MIT licence// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.solif (value ==0) {
return"0";
}
uint256 temp = value;
uint256 digits;
while (temp !=0) {
digits++;
temp /=10;
}
bytesmemory buffer =newbytes(digits);
while (value !=0) {
digits -=1;
buffer[digits] =bytes1(uint8(48+uint256(value %10)));
value /=10;
}
returnstring(buffer);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/functiontoHexString(uint256 value) internalpurereturns (stringmemory) {
if (value ==0) {
return"0x00";
}
uint256 temp = value;
uint256 length =0;
while (temp !=0) {
length++;
temp >>=8;
}
return toHexString(value, length);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/functiontoHexString(uint256 value, uint256 length) internalpurereturns (stringmemory) {
bytesmemory buffer =newbytes(2* length +2);
buffer[0] ="0";
buffer[1] ="x";
for (uint256 i =2* length +1; i >1; --i) {
buffer[i] = _HEX_SYMBOLS[value &0xf];
value >>=4;
}
require(value ==0, "Strings: hex length insufficient");
returnstring(buffer);
}
/// @dev Returns the hexadecimal representation of `value`./// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte,/// and the alphabets are capitalized conditionally according to/// https://eips.ethereum.org/EIPS/eip-55functiontoHexStringChecksummed(address value) internalpurereturns (stringmemory str) {
str = toHexString(value);
/// @solidity memory-safe-assemblyassembly {
let mask :=shl(6, div(not(0), 255)) // `0b010000000100000000 ...`let o :=add(str, 0x22)
let hashed :=and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... `let t :=shl(240, 136) // `0b10001000 << 240`for {
let i :=0
} 1 {
} {
mstore(add(i, i), mul(t, byte(i, hashed)))
i :=add(i, 1)
ifeq(i, 20) {
break
}
}
mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask)))))
o :=add(o, 0x20)
mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask)))))
}
}
/// @dev Returns the hexadecimal representation of `value`./// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte.functiontoHexString(address value) internalpurereturns (stringmemory str) {
str = toHexStringNoPrefix(value);
/// @solidity memory-safe-assemblyassembly {
let strLength :=add(mload(str), 2) // Compute the length.mstore(str, 0x3078) // Write the "0x" prefix.
str :=sub(str, 2) // Move the pointer.mstore(str, strLength) // Write the length.
}
}
/// @dev Returns the hexadecimal representation of `value`./// The output is encoded using 2 hexadecimal digits per byte.functiontoHexStringNoPrefix(address value) internalpurereturns (stringmemory str) {
/// @solidity memory-safe-assemblyassembly {
str :=mload(0x40)
// Allocate the memory.// We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length,// 0x02 bytes for the prefix, and 0x28 bytes for the digits.// The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80.mstore(0x40, add(str, 0x80))
// Store "0123456789abcdef" in scratch space.mstore(0x0f, 0x30313233343536373839616263646566)
str :=add(str, 2)
mstore(str, 40)
let o :=add(str, 0x20)
mstore(add(o, 40), 0)
value :=shl(96, value)
// We write the string from rightmost digit to leftmost digit.// The following is essentially a do-while loop that also handles the zero case.for {
let i :=0
} 1 {
} {
let p :=add(o, add(i, i))
let temp :=byte(i, value)
mstore8(add(p, 1), mload(and(temp, 15)))
mstore8(p, mload(shr(4, temp)))
i :=add(i, 1)
ifeq(i, 20) {
break
}
}
}
}
/// @dev Returns the hex encoded string from the raw bytes./// The output is encoded using 2 hexadecimal digits per byte.functiontoHexString(bytesmemory raw) internalpurereturns (stringmemory str) {
str = toHexStringNoPrefix(raw);
/// @solidity memory-safe-assemblyassembly {
let strLength :=add(mload(str), 2) // Compute the length.mstore(str, 0x3078) // Write the "0x" prefix.
str :=sub(str, 2) // Move the pointer.mstore(str, strLength) // Write the length.
}
}
/// @dev Returns the hex encoded string from the raw bytes./// The output is encoded using 2 hexadecimal digits per byte.functiontoHexStringNoPrefix(bytesmemory raw) internalpurereturns (stringmemory str) {
/// @solidity memory-safe-assemblyassembly {
let length :=mload(raw)
str :=add(mload(0x40), 2) // Skip 2 bytes for the optional prefix.mstore(str, add(length, length)) // Store the length of the output.// Store "0123456789abcdef" in scratch space.mstore(0x0f, 0x30313233343536373839616263646566)
let o :=add(str, 0x20)
let end :=add(raw, length)
for {
} iszero(eq(raw, end)) {
} {
raw :=add(raw, 1)
mstore8(add(o, 1), mload(and(mload(raw), 15)))
mstore8(o, mload(and(shr(4, mload(raw)), 15)))
o :=add(o, 2)
}
mstore(o, 0) // Zeroize the slot after the string.mstore(0x40, add(o, 0x20)) // Allocate the memory.
}
}
}
Contract Source Code
File 44 of 44: WallStreetBitsERC721.sol
// SPDX-License-Identifier: Apache-2.0pragmasolidity ^0.8.11;import"@thirdweb-dev/contracts/eip/queryable/ERC721AQueryable.sol";
import"@thirdweb-dev/contracts/extension/Multicall.sol";
import"@thirdweb-dev/contracts/extension/BatchMintMetadata.sol";
import"@thirdweb-dev/contracts/extension/ContractMetadata.sol";
import"@thirdweb-dev/contracts/extension/PlatformFee.sol";
import"@thirdweb-dev/contracts/extension/Royalty.sol";
import"@thirdweb-dev/contracts/extension/PrimarySale.sol";
import"@thirdweb-dev/contracts/extension/Ownable.sol";
import"@thirdweb-dev/contracts/extension/DelayedReveal.sol";
import"@thirdweb-dev/contracts/extension/LazyMint.sol";
import"@thirdweb-dev/contracts/extension/PermissionsEnumerable.sol";
import"@thirdweb-dev/contracts/extension/Drop.sol";
import"@thirdweb-dev/contracts/lib/Strings.sol";
import { CurrencyTransferLib } from"@thirdweb-dev/contracts/lib/CurrencyTransferLib.sol";
contractWallStreetBitsERC721isERC721AQueryable,
Multicall,
BatchMintMetadata,
ContractMetadata,
PlatformFee,
Royalty,
PrimarySale,
Ownable,
DelayedReveal,
LazyMint,
PermissionsEnumerable,
Drop{
usingStringsforuint256;
/*///////////////////////////////////////////////////////////////
State variables
//////////////////////////////////////////////////////////////*//// @dev Only transfers to or from TRANSFER_ROLE holders are valid, when transfers are restricted.bytes32private transferRole;
/// @dev Only MINTER_ROLE holders can sign off on `MintRequest`s and lazy mint tokens.bytes32private minterRole;
/// @dev Only METADATA_ROLE holders can reveal the URI for a batch of delayed reveal NFTs, and update or freeze batch metadata.bytes32private metadataRole;
/// @dev Max bps in the thirdweb system.uint256privateconstant MAX_BPS =10_000;
/// @dev Global max total supply of NFTs.uint256public maxTotalSupply;
/// @dev Emitted when the global max supply of tokens is updated.eventMaxTotalSupplyUpdated(uint256 maxTotalSupply);
/*///////////////////////////////////////////////////////////////
Constructor
//////////////////////////////////////////////////////////////*//**
* @notice Initializes the contract during construction.
*
* @param _defaultAdmin The default admin of the contract.
* @param _name The name of the contract.
* @param _symbol The symbol of the contract.
* @param _contractURI The URI for the contract's metadata.
* @param _royaltyRecipient The address to receive royalties.
* @param _royaltyBps The royalty basis points to be charged. Max = 10000 (10000 = 100%, 1000 = 10%)
* @param _primarySaleRecipient The address to receive primary sale value.
* @param _platformFeeBps The platform fee basis points to be charged. Max = 10000 (10000 = 100%, 1000 = 10%)
* @param _platformFeeRecipient The address to receive the platform fee.
*/constructor(address _defaultAdmin,
stringmemory _name,
stringmemory _symbol,
stringmemory _contractURI,
address _royaltyRecipient,
uint128 _royaltyBps,
address _primarySaleRecipient,
uint128 _platformFeeBps,
address _platformFeeRecipient
) ERC721A(_name, _symbol) {
bytes32 _transferRole =keccak256("TRANSFER_ROLE");
bytes32 _minterRole =keccak256("MINTER_ROLE");
bytes32 _metadataRole =keccak256("METADATA_ROLE");
_setupContractURI(_contractURI);
_setupOwner(_defaultAdmin);
_setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin);
_setupRole(_minterRole, _defaultAdmin);
_setupRole(_transferRole, _defaultAdmin);
_setupRole(_transferRole, address(0));
_setupRole(_metadataRole, _defaultAdmin);
_setRoleAdmin(_metadataRole, _metadataRole);
_setupPlatformFeeInfo(_platformFeeRecipient, _platformFeeBps);
_setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps);
_setupPrimarySaleRecipient(_primarySaleRecipient);
transferRole = _transferRole;
minterRole = _minterRole;
metadataRole = _metadataRole;
}
/*//////////////////////////////////////////////////////////////
ERC165 Logic
//////////////////////////////////////////////////////////////*//**
* @dev See ERC165: https://eips.ethereum.org/EIPS/eip-165
* @inheritdoc IERC165
*/functionsupportsInterface(bytes4 interfaceId) publicviewvirtualoverride(ERC721A, IERC165) returns (bool) {
return
interfaceId ==0x01ffc9a7||// ERC165 Interface ID for ERC165
interfaceId ==0x80ac58cd||// ERC165 Interface ID for ERC721
interfaceId ==0x5b5e139f||// ERC165 Interface ID for ERC721Metadata
interfaceId ==type(IERC2981).interfaceId; // ERC165 ID for ERC2981
}
/*///////////////////////////////////////////////////////////////
Overriden ERC 721 logic
//////////////////////////////////////////////////////////////*//**
* @notice Returns the metadata URI for an NFT.
* @dev See `BatchMintMetadata` for handling of metadata in this contract.
*
* @param _tokenId The tokenId of an NFT.
*/functiontokenURI(uint256 _tokenId) publicviewvirtualoverride(ERC721A, IERC721Metadata) returns (stringmemory) {
(uint256 batchId, ) = _getBatchId(_tokenId);
stringmemory batchUri = _getBaseURI(_tokenId);
if (isEncryptedBatch(batchId)) {
returnstring(abi.encodePacked(batchUri, "0"));
} else {
returnstring(abi.encodePacked(batchUri, _tokenId.toString()));
}
}
/*///////////////////////////////////////////////////////////////
Overriden lazy minting logic
//////////////////////////////////////////////////////////////*//**
* @notice Lets an authorized address lazy mint a given amount of NFTs.
*
* @param _amount The number of NFTs to lazy mint.
* @param _baseURIForTokens The placeholder base URI for the 'n' number of NFTs being lazy minted, where the
* metadata for each of those NFTs is `${baseURIForTokens}/${tokenId}`.
* @param _data The encrypted base URI + provenance hash for the batch of NFTs being lazy minted.
* @return batchId A unique integer identifier for the batch of NFTs lazy minted together.
*/functionlazyMint(uint256 _amount,
stringcalldata _baseURIForTokens,
bytescalldata _data
) publicvirtualoverridereturns (uint256 batchId) {
if (_data.length>0) {
(bytesmemory encryptedURI, bytes32 provenanceHash) =abi.decode(_data, (bytes, bytes32));
if (encryptedURI.length!=0&& provenanceHash !="") {
_setEncryptedData(nextTokenIdToLazyMint + _amount, _data);
}
}
return LazyMint.lazyMint(_amount, _baseURIForTokens, _data);
}
/*///////////////////////////////////////////////////////////////
Delayed reveal logic
//////////////////////////////////////////////////////////////*//**
* @notice Lets an account with `METADATA_ROLE` reveal the URI for a batch of 'delayed-reveal' NFTs.
*
* @param _index The ID for the batch of delayed-reveal NFTs to reveal.
* @param _key The key with which the base URI for the relevant batch of NFTs was encrypted.
*/functionreveal(uint256 _index, bytescalldata _key) externalonlyRole(metadataRole) returns (stringmemory revealedURI) {
uint256 batchId = getBatchIdAtIndex(_index);
revealedURI = getRevealURI(batchId, _key);
_setEncryptedData(batchId, "");
_setBaseURI(batchId, revealedURI);
emit TokenURIRevealed(_index, revealedURI);
}
/**
* @notice Updates the base URI for a batch of tokens. Can only be called if the batch has been revealed/is not encrypted.
*
* @param _index Index of the desired batch in batchIds array
* @param _uri the new base URI for the batch.
*/functionupdateBatchBaseURI(uint256 _index, stringcalldata _uri) externalonlyRole(metadataRole) {
require(!isEncryptedBatch(getBatchIdAtIndex(_index)), "Encrypted batch");
uint256 batchId = getBatchIdAtIndex(_index);
_setBaseURI(batchId, _uri);
}
/**
* @notice Freezes the base URI for a batch of tokens.
*
* @param _index Index of the desired batch in batchIds array.
*/functionfreezeBatchBaseURI(uint256 _index) externalonlyRole(metadataRole) {
require(!isEncryptedBatch(getBatchIdAtIndex(_index)), "Encrypted batch");
uint256 batchId = getBatchIdAtIndex(_index);
_freezeBaseURI(batchId);
}
/*///////////////////////////////////////////////////////////////
Setter functions
//////////////////////////////////////////////////////////////*//// @dev Lets a contract admin set the global maximum supply for collection's NFTs.functionsetMaxTotalSupply(uint256 _maxTotalSupply) externalonlyRole(DEFAULT_ADMIN_ROLE) {
maxTotalSupply = _maxTotalSupply;
emit MaxTotalSupplyUpdated(_maxTotalSupply);
}
/*//////////////////////////////////////////////////////////////
Minting/burning logic
//////////////////////////////////////////////////////////////*//**
* @notice Lets an owner or approved operator burn the NFT of the given tokenId.
* @dev ERC721A's `_burn(uint256,bool)` internally checks for token approvals.
*
* @param _tokenId The tokenId of the NFT to burn.
*/functionburn(uint256 _tokenId) externalvirtual{
_burn(_tokenId, true);
}
/*///////////////////////////////////////////////////////////////
Internal functions
//////////////////////////////////////////////////////////////*//**
* @dev Runs before every `claim` function call.
*
* @param _quantity The quantity of NFTs being claimed.
*/function_beforeClaim(address,
uint256 _quantity,
address,
uint256,
AllowlistProof calldata,
bytesmemory) internalviewoverride{
require(_currentIndex + _quantity <= nextTokenIdToLazyMint, "!Tokens");
require(maxTotalSupply ==0|| _currentIndex + _quantity <= maxTotalSupply, "!Supply");
}
/**
* @dev Collects and distributes the primary sale value of NFTs being claimed.
*
* @param _primarySaleRecipient The address to receive the primary sale value.
* @param _quantityToClaim The quantity of NFTs being claimed.
* @param _currency The currency in which the NFTs are being claimed.
* @param _pricePerToken The price per token in the given currency.
*/function_collectPriceOnClaim(address _primarySaleRecipient,
uint256 _quantityToClaim,
address _currency,
uint256 _pricePerToken
) internaloverride{
if (_pricePerToken ==0) {
require(msg.value==0, "!V");
return;
}
(address platformFeeRecipient, uint16 platformFeeBps) = getPlatformFeeInfo();
address saleRecipient = _primarySaleRecipient ==address(0) ? primarySaleRecipient() : _primarySaleRecipient;
uint256 totalPrice = _quantityToClaim * _pricePerToken;
uint256 platformFees = (totalPrice * platformFeeBps) / MAX_BPS;
bool validMsgValue;
if (_currency == CurrencyTransferLib.NATIVE_TOKEN) {
validMsgValue =msg.value== totalPrice;
} else {
validMsgValue =msg.value==0;
}
require(validMsgValue, "!V");
CurrencyTransferLib.transferCurrency(_currency, _msgSender(), platformFeeRecipient, platformFees);
CurrencyTransferLib.transferCurrency(_currency, _msgSender(), saleRecipient, totalPrice - platformFees);
}
/**
* @dev Transfers the NFTs being claimed.
*
* @param _to The address to which the NFTs are being transferred.
* @param _quantityBeingClaimed The quantity of NFTs being claimed.
*/function_transferTokensOnClaim(address _to,
uint256 _quantityBeingClaimed
) internalvirtualoverridereturns (uint256 startTokenId) {
startTokenId = _currentIndex;
_safeMint(_to, _quantityBeingClaimed);
}
/// @dev Checks whether platform fee info can be set in the given execution context.function_canSetPlatformFeeInfo() internalviewoverridereturns (bool) {
return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
}
/// @dev Checks whether primary sale recipient can be set in the given execution context.function_canSetPrimarySaleRecipient() internalviewoverridereturns (bool) {
return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
}
/// @dev Checks whether owner can be set in the given execution context.function_canSetOwner() internalviewoverridereturns (bool) {
return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
}
/// @dev Checks whether royalty info can be set in the given execution context.function_canSetRoyaltyInfo() internalviewoverridereturns (bool) {
return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
}
/// @dev Checks whether contract metadata can be set in the given execution context.function_canSetContractURI() internalviewoverridereturns (bool) {
return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
}
/// @dev Checks whether platform fee info can be set in the given execution context.function_canSetClaimConditions() internalviewoverridereturns (bool) {
return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
}
/// @dev Returns whether lazy minting can be done in the given execution context.function_canLazyMint() internalviewvirtualoverridereturns (bool) {
return hasRole(minterRole, _msgSender());
}
/*///////////////////////////////////////////////////////////////
Miscellaneous
//////////////////////////////////////////////////////////////*//**
* Returns the total amount of tokens minted in the contract.
*/functiontotalMinted() externalviewreturns (uint256) {
return _totalMinted();
}
/// @notice The tokenId assigned to the next new NFT to be lazy minted.functionnextTokenIdToMint() publicviewvirtualreturns (uint256) {
return nextTokenIdToLazyMint;
}
/// @notice The tokenId assigned to the next new NFT to be claimed.functionnextTokenIdToClaim() publicviewvirtualreturns (uint256) {
return _currentIndex;
}
/// @dev See {ERC721-_beforeTokenTransfer}.function_beforeTokenTransfers(addressfrom,
address to,
uint256 startTokenId,
uint256 quantity
) internalvirtualoverride{
super._beforeTokenTransfers(from, to, startTokenId, quantity);
// if transfer is restricted on the contract, we still want to allow burning and mintingif (!hasRole(transferRole, address(0)) &&from!=address(0) && to !=address(0)) {
if (!hasRole(transferRole, from) &&!hasRole(transferRole, to)) {
revert("!Transfer-Role");
}
}
}
function_dropMsgSender() internalviewvirtualoverridereturns (address) {
returnmsg.sender;
}
/// @notice Returns the sender in the given execution context.function_msgSender() internalviewoverride(Multicall, Context) returns (address) {
returnmsg.sender;
}
}