From 852462490031e04e267403745e1ff3d0deaa4415 Mon Sep 17 00:00:00 2001 From: Yash Date: Fri, 8 Aug 2025 11:16:07 +0530 Subject: [PATCH 1/5] DropERC721-C --- .gitmodules | 3 + contracts/prebuilts/drop/DropERC721C.sol | 467 +++++++++++++++++++++++ foundry.lock | 59 +++ foundry.toml | 1 + lib/creator-token-standards | 1 + 5 files changed, 531 insertions(+) create mode 100644 contracts/prebuilts/drop/DropERC721C.sol create mode 100644 foundry.lock create mode 160000 lib/creator-token-standards diff --git a/.gitmodules b/.gitmodules index db5ccba93..15020bf29 100644 --- a/.gitmodules +++ b/.gitmodules @@ -55,3 +55,6 @@ [submodule "lib/swap-router-contracts"] path = lib/swap-router-contracts url = https://github.com/Uniswap/swap-router-contracts +[submodule "lib/creator-token-standards"] + path = lib/creator-token-standards + url = https://github.com/limitbreakinc/creator-token-standards diff --git a/contracts/prebuilts/drop/DropERC721C.sol b/contracts/prebuilts/drop/DropERC721C.sol new file mode 100644 index 000000000..15beba21a --- /dev/null +++ b/contracts/prebuilts/drop/DropERC721C.sol @@ -0,0 +1,467 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity ^0.8.11; + +/// @author thirdweb + +// $$\ $$\ $$\ $$\ $$\ +// $$ | $$ | \__| $$ | $$ | +// $$$$$$\ $$$$$$$\ $$\ $$$$$$\ $$$$$$$ |$$\ $$\ $$\ $$$$$$\ $$$$$$$\ +// \_$$ _| $$ __$$\ $$ |$$ __$$\ $$ __$$ |$$ | $$ | $$ |$$ __$$\ $$ __$$\ +// $$ | $$ | $$ |$$ |$$ | \__|$$ / $$ |$$ | $$ | $$ |$$$$$$$$ |$$ | $$ | +// $$ |$$\ $$ | $$ |$$ |$$ | $$ | $$ |$$ | $$ | $$ |$$ ____|$$ | $$ | +// \$$$$ |$$ | $$ |$$ |$$ | \$$$$$$$ |\$$$$$\$$$$ |\$$$$$$$\ $$$$$$$ | +// \____/ \__| \__|\__|\__| \_______| \_____\____/ \_______|\_______/ + +// ========== External imports ========== + +import "../../extension/Multicall.sol"; +import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/interfaces/IERC2981Upgradeable.sol"; +import "@limitbreak/creator-token-standards/utils/CreatorTokenBase.sol"; +import "@limitbreak/creator-token-standards/utils/AutomaticValidatorTransferApproval.sol"; + +import "../../eip/ERC721AVirtualApproveUpgradeable.sol"; + +// ========== Internal imports ========== + +import "../../external-deps/openzeppelin/metatx/ERC2771ContextUpgradeable.sol"; +import "../../lib/CurrencyTransferLib.sol"; + +// ========== Features ========== + +import "../../extension/ContractMetadata.sol"; +import "../../extension/PlatformFee.sol"; +import "../../extension/Royalty.sol"; +import "../../extension/PrimarySale.sol"; +import "../../extension/Ownable.sol"; +import "../../extension/DelayedReveal.sol"; +import "../../extension/LazyMint.sol"; +import "../../extension/PermissionsEnumerable.sol"; +import "../../extension/Drop.sol"; + +contract DropERC721C is + Initializable, + ContractMetadata, + PlatformFee, + Royalty, + PrimarySale, + Ownable, + DelayedReveal, + LazyMint, + PermissionsEnumerable, + Drop, + ERC2771ContextUpgradeable, + Multicall, + ERC721AUpgradeable, + CreatorTokenBase, + AutomaticValidatorTransferApproval +{ + using StringsUpgradeable for uint256; + + /*/////////////////////////////////////////////////////////////// + State variables + //////////////////////////////////////////////////////////////*/ + + /// @dev Only transfers to or from TRANSFER_ROLE holders are valid, when transfers are restricted. + bytes32 private transferRole; + /// @dev Only MINTER_ROLE holders can sign off on `MintRequest`s and lazy mint tokens. + bytes32 private minterRole; + /// @dev Only METADATA_ROLE holders can reveal the URI for a batch of delayed reveal NFTs, and update or freeze batch metadata. + bytes32 private metadataRole; + + /// @dev Max bps in the thirdweb system. + uint256 private constant MAX_BPS = 10_000; + + address public constant DEFAULT_FEE_RECIPIENT = 0x1Af20C6B23373350aD464700B5965CE4B0D2aD94; + uint16 private constant DEFAULT_FEE_BPS = 100; + + /// @dev Constant value representing the ERC721 token type for signatures and transfer hooks + uint256 constant TOKEN_TYPE_ERC721 = 721; + + /// @dev Global max total supply of NFTs. + uint256 public maxTotalSupply; + + /// @dev Emitted when the global max supply of tokens is updated. + event MaxTotalSupplyUpdated(uint256 maxTotalSupply); + + error NotAuthorized(); + + /*/////////////////////////////////////////////////////////////// + Constructor + initializer logic + //////////////////////////////////////////////////////////////*/ + + constructor() initializer {} + + /// @dev Initializes the contract, like a constructor. + function initialize( + address _defaultAdmin, + string memory _name, + string memory _symbol, + string memory _contractURI, + address[] memory _trustedForwarders, + address _saleRecipient, + address _royaltyRecipient, + uint128 _royaltyBps, + uint128 _platformFeeBps, + address _platformFeeRecipient + ) external initializer { + bytes32 _transferRole = keccak256("TRANSFER_ROLE"); + bytes32 _minterRole = keccak256("MINTER_ROLE"); + bytes32 _metadataRole = keccak256("METADATA_ROLE"); + + // Initialize inherited contracts, most base-like -> most derived. + __ERC2771Context_init(_trustedForwarders); + __ERC721A_init(_name, _symbol); + + _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(_saleRecipient); + + transferRole = _transferRole; + minterRole = _minterRole; + metadataRole = _metadataRole; + } + + /** + * @notice Overrides behavior of isApprovedFor all such that if an operator is not explicitly approved + * for all, the contract owner can optionally auto-approve the 721-C transfer validator for transfers. + */ + function isApprovedForAll(address owner, address operator) public view virtual override returns (bool isApproved) { + isApproved = super.isApprovedForAll(owner, operator); + + if (!isApproved) { + if (autoApproveTransfersFromValidator) { + isApproved = operator == address(getTransferValidator()); + } + } + } + + /** + * @notice Returns the function selector for the transfer validator's validation function to be called + * @notice for transaction simulation. + */ + function getTransferValidationFunction() external pure returns (bytes4 functionSignature, bool isViewFunction) { + functionSignature = bytes4(keccak256("validateTransfer(address,address,address,uint256)")); + isViewFunction = true; + } + + function _tokenType() internal pure override returns(uint16) { + return uint16(TOKEN_TYPE_ERC721); + } + + function _contextSuffixLength() internal view virtual override(Context, ContextUpgradeable) returns (uint256) { + return 0; + } + + /*/////////////////////////////////////////////////////////////// + ERC 165 / 721 / 2981 logic + //////////////////////////////////////////////////////////////*/ + + /// @dev Returns the URI for a given tokenId. + function tokenURI(uint256 _tokenId) public view override returns (string memory) { + (uint256 batchId, ) = _getBatchId(_tokenId); + string memory batchUri = _getBaseURI(_tokenId); + + if (isEncryptedBatch(batchId)) { + return string(abi.encodePacked(batchUri, "0")); + } else { + return string(abi.encodePacked(batchUri, _tokenId.toString())); + } + } + + /// @dev See ERC 165 + function supportsInterface( + bytes4 interfaceId + ) public view virtual override(ERC721AUpgradeable, IERC165) returns (bool) { + return super.supportsInterface(interfaceId) || type(IERC2981Upgradeable).interfaceId == interfaceId || interfaceId == type(ICreatorToken).interfaceId || interfaceId == type(ICreatorTokenLegacy).interfaceId; + } + + /*/////////////////////////////////////////////////////////////// + Contract identifiers + //////////////////////////////////////////////////////////////*/ + + function contractType() external pure returns (bytes32) { + return bytes32("DropERC721"); + } + + function contractVersion() external pure returns (uint8) { + return uint8(4); + } + + /*/////////////////////////////////////////////////////////////// + Lazy minting + delayed-reveal logic + //////////////////////////////////////////////////////////////*/ + + /** + * @dev Lets an account with `MINTER_ROLE` lazy mint 'n' NFTs. + * The URIs for each token is the provided `_baseURIForTokens` + `{tokenId}`. + */ + function lazyMint( + uint256 _amount, + string calldata _baseURIForTokens, + bytes calldata _data + ) public override returns (uint256 batchId) { + if (_data.length > 0) { + (bytes memory encryptedURI, bytes32 provenanceHash) = abi.decode(_data, (bytes, bytes32)); + if (encryptedURI.length != 0 && provenanceHash != "") { + _setEncryptedData(nextTokenIdToLazyMint + _amount, _data); + } + } + + return super.lazyMint(_amount, _baseURIForTokens, _data); + } + + /// @dev Lets an account with `METADATA_ROLE` reveal the URI for a batch of 'delayed-reveal' NFTs. + /// @param _index the ID of a token with the desired batch. + /// @param _key the key to decrypt the batch's URI. + function reveal( + uint256 _index, + bytes calldata _key + ) external onlyRole(metadataRole) returns (string memory 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. + */ + function updateBatchBaseURI(uint256 _index, string calldata _uri) external onlyRole(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. + */ + function freezeBatchBaseURI(uint256 _index) external onlyRole(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. + function setMaxTotalSupply(uint256 _maxTotalSupply) external onlyRole(DEFAULT_ADMIN_ROLE) { + maxTotalSupply = _maxTotalSupply; + emit MaxTotalSupplyUpdated(_maxTotalSupply); + } + + /*/////////////////////////////////////////////////////////////// + Internal functions + //////////////////////////////////////////////////////////////*/ + + /// @dev Runs before every `claim` function call. + function _beforeClaim( + address, + uint256 _quantity, + address, + uint256, + AllowlistProof calldata, + bytes memory + ) internal view override { + require(_currentIndex + _quantity <= nextTokenIdToLazyMint, "!Tokens"); + require(maxTotalSupply == 0 || _currentIndex + _quantity <= maxTotalSupply, "!Supply"); + } + + /// @dev Collects and distributes the primary sale value of NFTs being claimed. + function _collectPriceOnClaim( + address _primarySaleRecipient, + uint256 _quantityToClaim, + address _currency, + uint256 _pricePerToken + ) internal override { + 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 platformFeesTw = (totalPrice * DEFAULT_FEE_BPS) / MAX_BPS; + 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(), DEFAULT_FEE_RECIPIENT, platformFeesTw); + CurrencyTransferLib.transferCurrency(_currency, _msgSender(), platformFeeRecipient, platformFees); + CurrencyTransferLib.transferCurrency( + _currency, + _msgSender(), + saleRecipient, + totalPrice - platformFees - platformFeesTw + ); + } + + /// @dev Transfers the NFTs being claimed. + function _transferTokensOnClaim( + address _to, + uint256 _quantityBeingClaimed + ) internal override returns (uint256 startTokenId) { + startTokenId = _currentIndex; + _safeMint(_to, _quantityBeingClaimed); + } + + /// @dev Checks whether platform fee info can be set in the given execution context. + function _canSetPlatformFeeInfo() internal view override returns (bool) { + return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); + } + + /// @dev Checks whether primary sale recipient can be set in the given execution context. + function _canSetPrimarySaleRecipient() internal view override returns (bool) { + return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); + } + + /// @dev Checks whether owner can be set in the given execution context. + function _canSetOwner() internal view override returns (bool) { + return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); + } + + /// @dev Checks whether royalty info can be set in the given execution context. + function _canSetRoyaltyInfo() internal view override returns (bool) { + return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); + } + + /// @dev Checks whether contract metadata can be set in the given execution context. + function _canSetContractURI() internal view override returns (bool) { + return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); + } + + /// @dev Checks whether platform fee info can be set in the given execution context. + function _canSetClaimConditions() internal view override returns (bool) { + return hasRole(DEFAULT_ADMIN_ROLE, _msgSender()); + } + + /// @dev Returns whether lazy minting can be done in the given execution context. + function _canLazyMint() internal view virtual override returns (bool) { + return hasRole(minterRole, _msgSender()); + } + + function _requireCallerIsContractOwner() internal view virtual override { + if(!hasRole(DEFAULT_ADMIN_ROLE, _msgSender())) { + revert NotAuthorized(); + } + } + + /*/////////////////////////////////////////////////////////////// + Miscellaneous + //////////////////////////////////////////////////////////////*/ + + /** + * Returns the total amount of tokens minted in the contract. + */ + function totalMinted() external view returns (uint256) { + return _totalMinted(); + } + + /// @dev The tokenId of the next NFT that will be minted / lazy minted. + function nextTokenIdToMint() external view returns (uint256) { + return nextTokenIdToLazyMint; + } + + /// @dev The next token ID of the NFT that can be claimed. + function nextTokenIdToClaim() external view returns (uint256) { + return _currentIndex; + } + + /// @dev Burns `tokenId`. See {ERC721-_burn}. + function burn(uint256 tokenId) external virtual { + // note: ERC721AUpgradeable's `_burn(uint256,bool)` internally checks for token approvals. + _burn(tokenId, true); + } + + /// @dev See {ERC721-_beforeTokenTransfer}. + function _beforeTokenTransfers( + address from, + address to, + uint256 startTokenId, + uint256 quantity + ) internal virtual override { + super._beforeTokenTransfers(from, to, startTokenId, quantity); + + for (uint256 i = 0; i < quantity;) { + _validateBeforeTransfer(from, to, startTokenId + i); + unchecked { + ++i; + } + } + + // if transfer is restricted on the contract, we still want to allow burning and minting + if (!hasRole(transferRole, address(0)) && from != address(0) && to != address(0)) { + if (!hasRole(transferRole, from) && !hasRole(transferRole, to)) { + revert("!Transfer-Role"); + } + } + } + + /// @dev Ties the erc721a _afterTokenTransfer hook to more granular transfer validation logic + function _afterTokenTransfers( + address from, + address to, + uint256 startTokenId, + uint256 quantity + ) internal virtual override { + for (uint256 i = 0; i < quantity;) { + _validateAfterTransfer(from, to, startTokenId + i); + unchecked { + ++i; + } + } + } + + function _dropMsgSender() internal view virtual override returns (address) { + return _msgSender(); + } + + function _msgSender() + internal + view + virtual + override(Context, ContextUpgradeable, ERC2771ContextUpgradeable, Multicall) + returns (address sender) + { + return ERC2771ContextUpgradeable._msgSender(); + } + + function _msgData() + internal + view + virtual + override(Context, ContextUpgradeable, ERC2771ContextUpgradeable) + returns (bytes calldata) + { + return ERC2771ContextUpgradeable._msgData(); + } +} diff --git a/foundry.lock b/foundry.lock new file mode 100644 index 000000000..1f9afe38b --- /dev/null +++ b/foundry.lock @@ -0,0 +1,59 @@ +{ + "lib/seaport-types": { + "rev": "25bae8ddfa8709e5c51ab429fe06024e46a18f15" + }, + "lib/ERC721A": { + "rev": "17fb77ffce10bb9a2bb94cac1fea17e2bf9e8a27" + }, + "lib/chainlink": { + "rev": "5d44bd4e8fa2bdc80228a0df891960d72246b645" + }, + "lib/creator-token-standards": { + "tag": { + "name": "v5.0.0", + "rev": "980a63b33591d568b6e04b45f37deba05a55f787" + } + }, + "lib/solady": { + "rev": "c6738e40225288842ce890cd265a305684e52c3d" + }, + "lib/openzeppelin-contracts": { + "rev": "dc44c9f1a4c3b10af99492eed84f83ed244203f6" + }, + "lib/openzeppelin-contracts-upgradeable": { + "rev": "2d081f24cac1a867f6f73d512f2022e1fa987854" + }, + "lib/swap-router-contracts": { + "rev": "c696aada49b33c8e764e6f0bd0a0a56bd8aa455f" + }, + "lib/ERC721A-Upgradeable": { + "rev": "80b4afb376ba1e886053c5aa82af852b3a09ba58" + }, + "lib/seaport": { + "rev": "1d12e33b71b6988cbbe955373ddbc40a87bd5b16" + }, + "lib/dynamic-contracts": { + "rev": "14af36f8f3af50d7d4ccfe6f16df589b27edd662" + }, + "lib/seaport-core": { + "rev": "d4e8c74adc472b311ab64b5c9f9757b5bba57a15" + }, + "lib/seaport-sol": { + "rev": "040d005768abafe3308b5f996aca3fd843d9c20e" + }, + "lib/ds-test": { + "rev": "e282159d5170298eb2455a6c05280ab5a73a4ef0" + }, + "lib/v3-periphery": { + "rev": "80f26c86c57b8a5e4b913f42844d4c8bd274d058" + }, + "lib/v3-core": { + "rev": "e3589b192d0be27e100cd0daaf6c97204fdb1899" + }, + "lib/forge-std": { + "rev": "2f112697506eab12d433a65fdc31a639548fe365" + }, + "lib/murky": { + "rev": "40de6e80117f39cda69d71b07b7c824adac91b29" + } +} \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index 464eeb8d6..78157a68f 100644 --- a/foundry.toml +++ b/foundry.toml @@ -40,6 +40,7 @@ remappings = [ 'contracts/=contracts/', 'erc721a-upgradeable/=lib/ERC721A-Upgradeable/', 'erc721a/=lib/ERC721A/', + '@limitbreak/creator-token-standards/=lib/creator-token-standards/src/', '@thirdweb-dev/dynamic-contracts/=lib/dynamic-contracts/', 'lib/sstore2=lib/dynamic-contracts/lib/sstore2/', 'solady/=lib/solady/', diff --git a/lib/creator-token-standards b/lib/creator-token-standards new file mode 160000 index 000000000..980a63b33 --- /dev/null +++ b/lib/creator-token-standards @@ -0,0 +1 @@ +Subproject commit 980a63b33591d568b6e04b45f37deba05a55f787 From 679470db8c683a5f13a1a21fac03546a375acd8b Mon Sep 17 00:00:00 2001 From: Yash Date: Fri, 8 Aug 2025 11:26:05 +0530 Subject: [PATCH 2/5] fix override --- contracts/prebuilts/drop/DropERC721C.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/prebuilts/drop/DropERC721C.sol b/contracts/prebuilts/drop/DropERC721C.sol index 15beba21a..18213c196 100644 --- a/contracts/prebuilts/drop/DropERC721C.sol +++ b/contracts/prebuilts/drop/DropERC721C.sol @@ -160,7 +160,7 @@ contract DropERC721C is } function _contextSuffixLength() internal view virtual override(Context, ContextUpgradeable) returns (uint256) { - return 0; + return super._contextSuffixLength(); } /*/////////////////////////////////////////////////////////////// From af4fcbc9ec6666fd1448094bc0362e807da483f5 Mon Sep 17 00:00:00 2001 From: Yash Date: Fri, 8 Aug 2025 11:59:27 +0530 Subject: [PATCH 3/5] remove 2771 --- contracts/prebuilts/drop/DropERC721C.sol | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/contracts/prebuilts/drop/DropERC721C.sol b/contracts/prebuilts/drop/DropERC721C.sol index 18213c196..5c6d98e1a 100644 --- a/contracts/prebuilts/drop/DropERC721C.sol +++ b/contracts/prebuilts/drop/DropERC721C.sol @@ -24,7 +24,6 @@ import "../../eip/ERC721AVirtualApproveUpgradeable.sol"; // ========== Internal imports ========== -import "../../external-deps/openzeppelin/metatx/ERC2771ContextUpgradeable.sol"; import "../../lib/CurrencyTransferLib.sol"; // ========== Features ========== @@ -50,7 +49,6 @@ contract DropERC721C is LazyMint, PermissionsEnumerable, Drop, - ERC2771ContextUpgradeable, Multicall, ERC721AUpgradeable, CreatorTokenBase, @@ -110,7 +108,6 @@ contract DropERC721C is bytes32 _metadataRole = keccak256("METADATA_ROLE"); // Initialize inherited contracts, most base-like -> most derived. - __ERC2771Context_init(_trustedForwarders); __ERC721A_init(_name, _symbol); _setupContractURI(_contractURI); @@ -449,19 +446,19 @@ contract DropERC721C is internal view virtual - override(Context, ContextUpgradeable, ERC2771ContextUpgradeable, Multicall) + override(Context, ContextUpgradeable, Multicall) returns (address sender) { - return ERC2771ContextUpgradeable._msgSender(); + return super._msgSender(); } function _msgData() internal view virtual - override(Context, ContextUpgradeable, ERC2771ContextUpgradeable) + override(Context, ContextUpgradeable) returns (bytes calldata) { - return ERC2771ContextUpgradeable._msgData(); + return super._msgData(); } } From f83dc7631f7e0457c345bbc90ca0088038cd817d Mon Sep 17 00:00:00 2001 From: Yash Date: Fri, 8 Aug 2025 12:11:47 +0530 Subject: [PATCH 4/5] initialize CreatorTokenBase --- contracts/prebuilts/drop/DropERC721C.sol | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contracts/prebuilts/drop/DropERC721C.sol b/contracts/prebuilts/drop/DropERC721C.sol index 5c6d98e1a..d77f1babc 100644 --- a/contracts/prebuilts/drop/DropERC721C.sol +++ b/contracts/prebuilts/drop/DropERC721C.sol @@ -127,6 +127,9 @@ contract DropERC721C is transferRole = _transferRole; minterRole = _minterRole; metadataRole = _metadataRole; + + _emitDefaultTransferValidator(); + _registerTokenType(DEFAULT_TRANSFER_VALIDATOR); } /** From 1f0f767b62e5eb05155b4db086ce62f8b3d370da Mon Sep 17 00:00:00 2001 From: Yash Date: Fri, 8 Aug 2025 12:18:31 +0530 Subject: [PATCH 5/5] comments --- contracts/prebuilts/drop/DropERC721C.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/prebuilts/drop/DropERC721C.sol b/contracts/prebuilts/drop/DropERC721C.sol index d77f1babc..81639eaa3 100644 --- a/contracts/prebuilts/drop/DropERC721C.sol +++ b/contracts/prebuilts/drop/DropERC721C.sol @@ -96,7 +96,7 @@ contract DropERC721C is string memory _name, string memory _symbol, string memory _contractURI, - address[] memory _trustedForwarders, + address[] memory _trustedForwarders, // unused address _saleRecipient, address _royaltyRecipient, uint128 _royaltyBps,