Skip to content

Add ERC721 Burn Enumerable#209

Merged
mudgen merged 5 commits into
Perfect-Abstractions:mainfrom
PhantomOz:main
Nov 21, 2025
Merged

Add ERC721 Burn Enumerable#209
mudgen merged 5 commits into
Perfect-Abstractions:mainfrom
PhantomOz:main

Conversation

@PhantomOz

Copy link
Copy Markdown
Contributor

Summary

Added a new ERC721EnumerableBurnFacet to provide external burn functionality for ERC721 Enumerable tokens, following the Compose design pattern of self-contained facets.

Changes Made

  • New Facet: Created ERC721EnumerableBurnFacet.sol which implements burn(uint256) logic directly, including necessary storage layout, events, and errors to remain self-contained without external imports.
  • Library Update: Updated LibERC721Enumerable.sol to include the missing tokenURIOf mapping in the storage struct, ensuring layout compatibility across facets.
  • Testing: Added ERC721EnumerableBurnFacet.t.sol and a corresponding harness ERC721EnumerableBurnFacetHarness.sol to verify burn behavior, including ownership checks, approval validation, and enumeration updates.

Checklist

Before submitting this PR, please ensure:

  • Code follows the Solidity feature ban - No inheritance, constructors, modifiers, public/private variables, external library functions, using for directives, or selfdestruct

  • Code follows Design Principles - Readable, uses diamond storage, favors composition over inheritance

  • Code matches the codebase style - Consistent formatting, documentation, and patterns (e.g. ERC20Facet.sol)

  • Code is formatted with forge fmt

  • Existing tests pass - Run tests to be sure existing tests pass.

  • New tests are optional - If you don't provide tests for new functionality or changes then please create a new issue so this can be assigned to someone.

  • All tests pass - Run forge test and ensure everything works

  • Documentation updated - If applicable, update relevant documentation

Make sure to follow the contributing guidelines.

Additional Notes

@netlify

netlify Bot commented Nov 20, 2025

Copy link
Copy Markdown

👷 Deploy request for compose-diamonds pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit 08b9628

@mudgen

mudgen commented Nov 21, 2025

Copy link
Copy Markdown
Contributor

@PhantomOz Thank you! I will be checking this out tomorrow.

To fix the issue below, can you please run forge update in your local Compose repo and then commit and push to your pull request to fix this problem:
Screenshot from 2025-11-20 21-33-46

@PhantomOz

Copy link
Copy Markdown
Contributor Author

@mudgen done

@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report

Coverage

Metric Coverage Details
Lines 65% 1115/1717 lines
Functions 72% 219/304 functions
Branches 45% 127/281 branches

Last updated: Fri, 21 Nov 2025 16:01:00 GMT for commit 08b9628

@mudgen mudgen requested a review from Copilot November 21, 2025 16:01
@github-actions

Copy link
Copy Markdown
Contributor

Gas Report

No gas usage changes detected between main and main.

All functions maintain the same gas costs. ✅

Last updated: Fri, 21 Nov 2025 16:01:21 GMT for commit 08b9628

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a new ERC721EnumerableBurnFacet to provide external burn functionality for ERC721 Enumerable tokens using the diamond storage pattern. The facet implements burn logic independently to support the Compose design philosophy of self-contained, composable facets.

  • Added ERC721EnumerableBurnFacet.sol with inline burn implementation
  • Updated LibERC721Enumerable.sol storage layout to include tokenURIOf mapping
  • Added comprehensive test suite for burn functionality

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

File Description
src/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.sol New facet providing external burn function with inline implementation
src/token/ERC721/ERC721Enumerable/LibERC721Enumerable.sol Added tokenURIOf mapping to storage struct
test/token/ERC721/ERC721Enumerable/ERC721EnumerableBurnFacet.t.sol Test suite for burn facet
test/token/ERC721/ERC721Enumerable/harnesses/ERC721EnumerableBurnFacetHarness.sol Test harness providing helper functions

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +46 to +50
// We don't have the error defined in this facet, so we revert with generic or define it?
// The test expects ERC721OutOfBoundsIndex?
// Wait, the test `test_Burn_UpdatesEnumerationOrdering` calls `tokenOfOwnerByIndex`.
// If I don't implement it correctly, it fails.
// The test doesn't check for revert on this function, it checks return value.

Copilot AI Nov 21, 2025

Copy link

Choose a reason for hiding this comment

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

Remove commented-out thought process and internal questions from production code. These inline comments appear to be development notes that should be removed before merging.

Suggested change
// We don't have the error defined in this facet, so we revert with generic or define it?
// The test expects ERC721OutOfBoundsIndex?
// Wait, the test `test_Burn_UpdatesEnumerationOrdering` calls `tokenOfOwnerByIndex`.
// If I don't implement it correctly, it fails.
// The test doesn't check for revert on this function, it checks return value.

Copilot uses AI. Check for mistakes.
// Wait, the test `test_Burn_UpdatesEnumerationOrdering` calls `tokenOfOwnerByIndex`.
// If I don't implement it correctly, it fails.
// The test doesn't check for revert on this function, it checks return value.
revert("Index out of bounds");

Copilot AI Nov 21, 2025

Copy link

Choose a reason for hiding this comment

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

Use a custom error instead of a string revert message for consistency with the codebase pattern. Other functions in this contract use custom errors like ERC721NonexistentToken. Consider defining and using ERC721OutOfBoundsIndex error similar to the one in ERC721EnumerableFacet.sol.

Copilot uses AI. Check for mistakes.
/// @title ERC-721 Enumerable Burn Facet
/// @notice Provides an external burn entry point that composes with other ERC-721 enumerable facets.
/// @dev Keeps burn logic isolated so diamonds can opt-in without inheriting unrelated functionality.
/// The corresponding library file for the facet that has the burn() internal function is in the LibERC721Enumerable.sol file.

Copilot AI Nov 21, 2025

Copy link

Choose a reason for hiding this comment

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

The comment states there is a burn() internal function in LibERC721Enumerable.sol, but this facet implements burn() inline and doesn't use the library function. The library's burn() also has a different signature (takes a _sender parameter). Either update this comment to clarify why the library function isn't being used, or consider using the library function for consistency.

Suggested change
/// The corresponding library file for the facet that has the burn() internal function is in the LibERC721Enumerable.sol file.
/// Note: This facet implements its own burn() logic inline rather than using the internal burn() function from LibERC721Enumerable.sol, because the library's function has a different signature (requires a _sender parameter) and is not directly suitable for this external entry point.

Copilot uses AI. Check for mistakes.
Comment on lines +66 to +67
delete s.ownerOf[_tokenId];
delete s.approved[_tokenId];

Copilot AI Nov 21, 2025

Copy link

Choose a reason for hiding this comment

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

The burn implementation duplicates logic from LibERC721Enumerable.burn() (lines 147-167). Consider calling the library function instead to reduce code duplication and ensure consistency. The library function signature is burn(uint256 _tokenId, address _sender) which could be called as LibERC721.burn(_tokenId, msg.sender).

Copilot uses AI. Check for mistakes.
assertEq(token.balanceOf(alice), 1);
assertEq(token.totalSupply(), 2);

vm.expectRevert(abi.encodeWithSelector(ERC721EnumerableBurnFacet.ERC721NonexistentToken.selector, 1));

Copilot AI Nov 21, 2025

Copy link

Choose a reason for hiding this comment

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

[nitpick] For consistency with test patterns in the codebase and better readability, consider storing the error selector in a local variable or importing the error separately instead of accessing it through the contract type. This is particularly relevant since the same error is used in multiple places.

Copilot uses AI. Check for mistakes.
@mudgen mudgen merged commit 2f2bf0d into Perfect-Abstractions:main Nov 21, 2025
10 checks passed
@mudgen

mudgen commented Nov 21, 2025

Copy link
Copy Markdown
Contributor

@PhantomOz Thanks for your work!

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.

3 participants