Skip to content

DropERC721-C #690

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 8, 2025
Merged

DropERC721-C #690

merged 5 commits into from
Aug 8, 2025

Conversation

kumaryash90
Copy link
Member

@kumaryash90 kumaryash90 commented Aug 8, 2025

Summary by CodeRabbit

  • New Features

    • Introduced a new ERC721 drop contract with advanced features including lazy minting, delayed reveal, role-based access control, fee distribution, and transfer restrictions.
  • Chores

    • Added a new external dependency for creator token standards.
    • Updated configuration to support the new dependency.

Copy link

coderabbitai bot commented Aug 8, 2025

Walkthrough

A new ERC721 drop contract (DropERC721C.sol) was introduced, providing advanced minting, drop, and access control features. The project now includes the creator-token-standards library as a Git submodule, with corresponding import remapping added in foundry.toml. No existing files were modified or removed.

Changes

Cohort / File(s) Change Summary
New ERC721 Drop Contract
contracts/prebuilts/drop/DropERC721C.sol
Introduced DropERC721C, an ERC721 contract with lazy minting, delayed reveal, role-based access, fee distribution, and transfer restrictions.
Creator Token Standards Submodule
.gitmodules, lib/creator-token-standards, foundry.toml
Added creator-token-standards as a Git submodule, updated remappings in foundry.toml to map imports, and referenced a specific commit in the submodule.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant DropERC721C
    participant Platform
    participant SaleRecipient
    participant RoyaltyRecipient

    User->>DropERC721C: lazyMint(amount, baseURI, data)
    DropERC721C-->>User: batchId

    User->>DropERC721C: claim(tokenId)
    DropERC721C->>Platform: distribute platform fee
    DropERC721C->>SaleRecipient: distribute sale proceeds
    DropERC721C->>RoyaltyRecipient: distribute royalty (if applicable)
    DropERC721C-->>User: transfer token

    User->>DropERC721C: reveal(batchIndex, key)
    DropERC721C-->>User: revealedURI
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Note

🔌 MCP (Model Context Protocol) integration is now available in Early Access!

Pro users can now connect to remote MCP servers under the Integrations page to get reviews and chat conversations that understand additional development context.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f83dc76 and 1f0f767.

📒 Files selected for processing (1)
  • contracts/prebuilts/drop/DropERC721C.sol (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • contracts/prebuilts/drop/DropERC721C.sol
✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch yash/droperc721-c

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e85528e and 8524624.

⛔ Files ignored due to path filters (1)
  • foundry.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • .gitmodules (1 hunks)
  • contracts/prebuilts/drop/DropERC721C.sol (1 hunks)
  • foundry.toml (1 hunks)
  • lib/creator-token-standards (1 hunks)
🔇 Additional comments (5)
foundry.toml (1)

43-43: Remapping is correct; verify submodule inclusion in CI/CD
The path prefix matches the library’s src layout and follows the existing trailing-slash convention, so forge should resolve imports properly.
Please confirm that

  1. .gitmodules tracks lib/creator-token-standards at the intended commit, and
  2. your build pipeline runs git submodule update --init --recursive before forge build,

otherwise compilation will fail on clean clones.

lib/creator-token-standards (1)

1-1: Ensure submodule is fully declared and policy-compliant

Only the commit pointer is present. Please verify that:

  1. .gitmodules (path+URL) and foundry.toml remap entries are committed in the same PR so CI/clones work out-of-the-box.
  2. The pinned SHA corresponds to a vetted, tagged release that satisfies our license & security policies.

If either item is missing or non-compliant, update before merge.

.gitmodules (1)

58-60: LGTM! Submodule configuration is correct.

The new creator-token-standards submodule entry is properly configured and follows the standard format consistent with other submodules in the project.

contracts/prebuilts/drop/DropERC721C.sol (2)

75-76: Hardcoded fee recipient address requires documentation.

The DEFAULT_FEE_RECIPIENT is permanently set to 0x1Af20C6B23373350aD464700B5965CE4B0D2aD94 and will receive 1% of all primary sales. This creates an immutable fee collection point that should be clearly documented for transparency. Consider:

  1. Adding comprehensive documentation explaining this fee structure
  2. Making this configurable through initialization if flexibility is needed

122-122: Verify the intent of granting TRANSFER_ROLE to address(0).

Granting TRANSFER_ROLE to address(0) is unusual and could have unintended security implications. This might bypass transfer restrictions in the _beforeTokenTransfers hook. Please confirm this is intentional and document the reasoning.

_setupRole(_transferRole, _defaultAdmin);
_setupRole(_transferRole, address(0));
_setupRole(_metadataRole, _defaultAdmin);
_setRoleAdmin(_metadataRole, _metadataRole);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Metadata role self-administration creates privilege escalation risk.

Setting _metadataRole as its own admin means any account with METADATA_ROLE can grant this role to other accounts. This could lead to unintended privilege escalation where a metadata manager can create additional metadata managers without admin approval.

Consider setting DEFAULT_ADMIN_ROLE as the admin for metadata role:

-_setRoleAdmin(_metadataRole, _metadataRole);
+_setRoleAdmin(_metadataRole, DEFAULT_ADMIN_ROLE);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
_setRoleAdmin(_metadataRole, _metadataRole);
_setRoleAdmin(_metadataRole, DEFAULT_ADMIN_ROLE);
🤖 Prompt for AI Agents
In contracts/prebuilts/drop/DropERC721C.sol at line 124, the code sets
_metadataRole as its own admin, allowing any holder of METADATA_ROLE to grant it
to others, causing a privilege escalation risk. Change the admin role of
_metadataRole to DEFAULT_ADMIN_ROLE instead by calling
_setRoleAdmin(_metadataRole, DEFAULT_ADMIN_ROLE) to ensure only admins can grant
metadata roles.

Comment on lines +216 to +217
if (encryptedURI.length != 0 && provenanceHash != "") {
_setEncryptedData(nextTokenIdToLazyMint + _amount, _data);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Incorrect bytes32 comparison with empty string.

The condition provenanceHash != "" is comparing a bytes32 with an empty string literal, which is not the correct way to check for an empty bytes32 value.

Apply this fix:

-if (encryptedURI.length != 0 && provenanceHash != "") {
+if (encryptedURI.length != 0 && provenanceHash != bytes32(0)) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (encryptedURI.length != 0 && provenanceHash != "") {
_setEncryptedData(nextTokenIdToLazyMint + _amount, _data);
if (encryptedURI.length != 0 && provenanceHash != bytes32(0)) {
_setEncryptedData(nextTokenIdToLazyMint + _amount, _data);
🤖 Prompt for AI Agents
In contracts/prebuilts/drop/DropERC721C.sol around lines 216 to 217, the code
incorrectly compares a bytes32 variable provenanceHash to an empty string
literal. To fix this, replace the comparison provenanceHash != "" with
provenanceHash != bytes32(0) to correctly check if provenanceHash is not empty.

Comment on lines +307 to +325
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
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Double fee structure should be clearly documented.

The contract implements a dual fee system:

  1. A hardcoded 1% fee to DEFAULT_FEE_RECIPIENT (line 307)
  2. A configurable platform fee to platformFeeRecipient (line 308)

Both fees are deducted from the buyer's payment. This means the total fees could be higher than expected (e.g., if platform fee is 5%, total fees would be 6%). This dual fee structure should be prominently documented to ensure transparency for users and integrators.

🤖 Prompt for AI Agents
In contracts/prebuilts/drop/DropERC721C.sol around lines 307 to 325, add clear
comments explaining the dual fee structure: a fixed 1% fee sent to
DEFAULT_FEE_RECIPIENT and a separate configurable platform fee sent to
platformFeeRecipient, both deducted from the buyer's payment. This documentation
should clarify that total fees are the sum of these two amounts to ensure
transparency for users and integrators.

@kumaryash90 kumaryash90 requested a review from jakeloo August 8, 2025 05:50
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (3)
contracts/prebuilts/drop/DropERC721C.sol (3)

124-124: Metadata role self-administration creates privilege escalation risk.

Setting _metadataRole as its own admin means any account with METADATA_ROLE can grant this role to other accounts. This could lead to unintended privilege escalation where a metadata manager can create additional metadata managers without admin approval.

Consider setting DEFAULT_ADMIN_ROLE as the admin for metadata role:

-_setRoleAdmin(_metadataRole, _metadataRole);
+_setRoleAdmin(_metadataRole, DEFAULT_ADMIN_ROLE);

216-217: Incorrect bytes32 comparison with empty string.

The condition provenanceHash != "" is comparing a bytes32 with an empty string literal, which is not the correct way to check for an empty bytes32 value.

Apply this fix:

-if (encryptedURI.length != 0 && provenanceHash != "") {
+if (encryptedURI.length != 0 && provenanceHash != bytes32(0)) {

307-325: Double fee structure should be clearly documented.

The contract implements a dual fee system:

  1. A hardcoded 1% fee to DEFAULT_FEE_RECIPIENT (line 307)
  2. A configurable platform fee to platformFeeRecipient (line 308)

Both fees are deducted from the buyer's payment. This means the total fees could be higher than expected (e.g., if platform fee is 5%, total fees would be 6%). This dual fee structure should be prominently documented to ensure transparency for users and integrators.

Add clear comments explaining the dual fee structure and consider documenting this in the contract's NatSpec documentation.

🧹 Nitpick comments (1)
contracts/prebuilts/drop/DropERC721C.sol (1)

372-376: Inconsistent error handling pattern.

The _requireCallerIsContractOwner function uses a custom NotAuthorized() error, which is good practice. However, other access control checks in the contract use require() statements with string messages, creating inconsistency.

Consider standardizing error handling across the contract by either:

  1. Using custom errors consistently throughout, or
  2. Using require statements with descriptive messages consistently

For better gas efficiency, custom errors are preferred:

function _canSetPlatformFeeInfo() internal view override returns (bool) {
-   return hasRole(DEFAULT_ADMIN_ROLE, _msgSender());
+   if (!hasRole(DEFAULT_ADMIN_ROLE, _msgSender())) {
+       revert NotAuthorized();
+   }
+   return true;
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8524624 and 679470d.

📒 Files selected for processing (1)
  • contracts/prebuilts/drop/DropERC721C.sol (1 hunks)
🔇 Additional comments (4)
contracts/prebuilts/drop/DropERC721C.sol (4)

286-288: Supply validation logic is correct and comprehensive.

The _beforeClaim function properly validates both lazy mint bounds and global max supply constraints. The logic correctly handles the case where maxTotalSupply is 0 (unlimited) and ensures tokens cannot be claimed beyond what's been lazy minted.


422-426: Transfer role restriction logic is well-implemented.

The transfer restriction logic correctly allows minting and burning (transfers to/from address(0)) while enforcing role-based restrictions for regular transfers. The logic properly checks both sender and recipient roles, ensuring either party having the transfer role allows the transfer.


448-466: Message sender resolution correctly prioritizes ERC2771.

The _msgSender() and _msgData() overrides properly resolve the diamond problem in multiple inheritance by explicitly using ERC2771ContextUpgradeable implementation. This ensures meta-transaction support works correctly.


20-21: Creator-token-standards submodule integration verified

The @limitbreak/creator-token-standards submodule is correctly initialized and mapped:

  • Submodule present at lib/creator-token-standards (commit 980a63b33591d568b6e04b45f37deba05a55f787)
  • Remapped in foundry.toml under @limitbreak/creator-token-standards/=lib/creator-token-standards/src/
  • Imports in contracts/prebuilts/drop/DropERC721C.sol (CreatorTokenBase.sol, AutomaticValidatorTransferApproval.sol) resolve to the expected paths

Please ensure the referenced commit has been audited and approved for security.

Comment on lines +75 to +77
address public constant DEFAULT_FEE_RECIPIENT = 0x1Af20C6B23373350aD464700B5965CE4B0D2aD94;
uint16 private constant DEFAULT_FEE_BPS = 100;

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Hardcoded fee recipient and rate create centralization risk.

The contract hardcodes a specific fee recipient address and 1% fee rate. This creates a permanent dependency on this address and prevents fee adjustment, which could be problematic for long-term governance or if the address becomes compromised.

Consider making the default fee recipient and rate configurable through admin functions:

-address public constant DEFAULT_FEE_RECIPIENT = 0x1Af20C6B23373350aD464700B5965CE4B0D2aD94;
-uint16 private constant DEFAULT_FEE_BPS = 100;
+address public defaultFeeRecipient;
+uint16 public defaultFeeBps;

Add setter functions with appropriate access controls to update these values.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In contracts/prebuilts/drop/DropERC721C.sol around lines 75 to 77, the fee
recipient address and fee rate are hardcoded, causing centralization risk and
lack of flexibility. To fix this, remove the constant declarations and instead
declare these as private or internal variables. Then, add setter functions with
proper access control (e.g., onlyOwner or admin modifier) to allow updating the
fee recipient address and fee rate securely. This will enable governance or
admin to adjust fees if needed.

Comment on lines +139 to +147
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());
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Auto-approval logic introduces potential security bypass.

The isApprovedForAll override automatically approves the transfer validator when autoApproveTransfersFromValidator is true. This could bypass intended approval checks if the transfer validator is compromised or if the auto-approval setting is unintentionally enabled.

Add explicit documentation and consider adding an event when auto-approval is used:

function isApprovedForAll(address owner, address operator) public view virtual override returns (bool isApproved) {
    isApproved = super.isApprovedForAll(owner, operator);

    if (!isApproved) {
        if (autoApproveTransfersFromValidator) {
+           // Auto-approving transfer validator - ensure this is intended behavior
            isApproved = operator == address(getTransferValidator());
        }
    }
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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());
}
}
}
function isApprovedForAll(address owner, address operator) public view virtual override returns (bool isApproved) {
isApproved = super.isApprovedForAll(owner, operator);
if (!isApproved) {
if (autoApproveTransfersFromValidator) {
// Auto-approving transfer validator - ensure this is intended behavior
isApproved = operator == address(getTransferValidator());
}
}
}
🤖 Prompt for AI Agents
In contracts/prebuilts/drop/DropERC721C.sol around lines 139 to 147, the
isApprovedForAll function auto-approves the transfer validator which may cause
security risks. Add explicit NatSpec comments above the function explaining the
auto-approval behavior and its risks. Additionally, emit a custom event inside
the conditional block when auto-approval is granted to provide on-chain
visibility of such approvals.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (5)
contracts/prebuilts/drop/DropERC721C.sol (5)

73-75: Hard-coded fee recipient and rate remain non-configurable
This still embeds an immutable address and 1 % fee, posing governance and upgradeability risks. Make them configurable under admin control instead of constant.


121-121: Metadata role is self-admin → privilege escalation
Using _metadataRole as its own admin lets any metadata manager grant that role to others. Switch admin to DEFAULT_ADMIN_ROLE.


136-144: Auto-approval path needs explicit documentation / event
The silent auto-approval of the transfer validator can mask unintended approvals. Add NatSpec explaining the risk and emit an event when triggered.


213-214: Incorrect empty-check on bytes32 provenance hash
provenanceHash != "" compares a bytes32 to a string literal. Use bytes32(0) instead.


299-306: Dual-fee calculation needs explicit documentation
Two separate fees (fixed 1 % + configurable platform fee) are deducted from buyers. Document prominently to avoid integrator confusion.

🧹 Nitpick comments (1)
contracts/prebuilts/drop/DropERC721C.sol (1)

243-258: Missing events for metadata base-URI changes
updateBatchBaseURI and freezeBatchBaseURI modify token metadata but emit no events. Emit dedicated events so indexers and clients can track these critical state changes.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 679470d and af4fcbc.

📒 Files selected for processing (1)
  • contracts/prebuilts/drop/DropERC721C.sol (1 hunks)

Comment on lines +264 to +268
/// @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);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Guard against lowering maxTotalSupply below minted count
setMaxTotalSupply allows any admin to set a value smaller than _totalMinted(), permanently blocking further claims. Add a check:

require(_maxTotalSupply >= _totalMinted(), "max supply < minted");
🤖 Prompt for AI Agents
In contracts/prebuilts/drop/DropERC721C.sol around lines 264 to 268, the
setMaxTotalSupply function allows setting maxTotalSupply to a value less than
the number of already minted tokens, which can block further claims. Add a
require statement at the start of the function to ensure the new maxTotalSupply
is not less than _totalMinted(), using require(_maxTotalSupply >=
_totalMinted(), "max supply < minted"); to prevent this issue.

Comment on lines +299 to +323
(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
);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Re-entrancy exposure in fee collection
_collectPriceOnClaim forwards Ether/ERC20 to external recipients before the mint occurs and without nonReentrant. A malicious recipient could re-enter claim (directly or via fallback) and drain funds / exceed supply.
Add OpenZeppelin ReentrancyGuard to the contract or restructure so state changes happen before external calls.

🤖 Prompt for AI Agents
In contracts/prebuilts/drop/DropERC721C.sol around lines 299 to 323, the
function _collectPriceOnClaim sends Ether/ERC20 payments to external recipients
before completing minting and lacks reentrancy protection. To fix this, add
OpenZeppelin's ReentrancyGuard to the contract and apply the nonReentrant
modifier to _collectPriceOnClaim, or alternatively, restructure the function so
that all state changes, including minting, occur before any external calls to
transfer funds.

@jakeloo jakeloo enabled auto-merge (squash) August 8, 2025 07:07
@jakeloo jakeloo merged commit be86157 into main Aug 8, 2025
3 checks passed
@jakeloo jakeloo deleted the yash/droperc721-c branch August 8, 2025 07:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants