Skip to content

Conversation

@RogerPodacter
Copy link
Member

@RogerPodacter RogerPodacter commented Nov 24, 2025

…nerCappedUpgradeable contracts

  • Updated the _transferERC721 and tokenURI functions to utilize a new _normalizeTokenId method for improved token ID validation and handling.
  • Introduced _encodeMintId and _decodeTokenId methods to streamline the encoding and decoding of token IDs, enhancing clarity and maintainability.
  • Replaced direct ID manipulations with the new methods to ensure consistent token ID processing across the contract.

Note

Drops ID encoding prefix and uses raw tokenId/mintId throughout, adds simple ID validation, streamlines transfers/minting, and updates metadata and tests accordingly.

  • Contracts:
    • Token ID handling: Remove ID_ENCODING_PREFIX and all encode/decode logic; use raw tokenId/mintId directly across _mintERC721, _transferERC721, ownerOf, getApproved, and forceTransfer.
    • Validation: Introduce _validateTokenId (nonzero, not type(uint256).max) and apply in ownerOf, getApproved, _mintERC721, and tokenURI.
    • ERC20FixedDenomination:
      • tokenURI(uint256) now takes raw mintId, validates, and builds metadata (renames name field to Token #<id>); removes fallback metadata path.
      • forceTransfer now transfers NFT with raw nftId.
    • ERC404NullOwnerCappedUpgradeable:
      • Cap/units checks simplified; remove upper bound tied to encoding.
      • Add balanceOf(address owner, uint256 id) for per-token ownership (returns 1/0).
      • _transferERC721 uses direct storage checks (no ownerOf call); clears approval and updates indices.
      • _transferERC20 mint path no longer checks encoding-based mint limit; still enforces cap.
      • Add ERC20Transfer event (not emitted; Transfer emission retained).
  • Tests:
    • Update expectations to raw token IDs (e.g., ownerOf(1)/ownerOf(2)), and adjust force transfer/cap tests accordingly.

Written by Cursor Bugbot for commit 83ef4a3. This will update automatically on new commits. Configure here.

…nerCappedUpgradeable contracts

- Updated the `_transferERC721` and `tokenURI` functions to utilize a new `_normalizeTokenId` method for improved token ID validation and handling.
- Introduced `_encodeMintId` and `_decodeTokenId` methods to streamline the encoding and decoding of token IDs, enhancing clarity and maintainability.
- Replaced direct ID manipulations with the new methods to ensure consistent token ID processing across the contract.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request refactors token ID handling in the ERC404 hybrid ERC20/ERC721 contracts by introducing three new helper methods (_normalizeTokenId, _encodeMintId, and _decodeTokenId) to streamline the encoding and decoding of token IDs. The changes improve code clarity and maintainability by replacing direct bitwise and arithmetic operations with well-named functions.

Key Changes

  • Introduced _normalizeTokenId(), _encodeMintId(), and _decodeTokenId() helper functions for consistent token ID processing
  • Updated _transferERC721(), ownerOf(), getApproved(), and tokenURI() to use the new helper methods
  • Added a new ERC20Transfer event (though this breaks ERC20 standard compliance)

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
contracts/src/ERC404NullOwnerCappedUpgradeable.sol Introduced token ID encoding/decoding helper functions, refactored existing methods to use them, and added a non-standard ERC20Transfer event
contracts/src/ERC20FixedDenomination.sol Updated transferEthscription() and tokenURI() to use the new encoding/decoding helper functions instead of direct arithmetic/bitwise operations

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


/// @dev Encodes a human-friendly mintId into the full tokenId with prefix.
function _encodeMintId(uint256 mintId_) internal pure returns (uint256) {
if (mintId_ == 0 || mintId_ >= ID_ENCODING_PREFIX) {
Copy link

Copilot AI Nov 24, 2025

Choose a reason for hiding this comment

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

Inconsistent validation between _encodeMintId and _mintERC721. The _encodeMintId function rejects mintId_ >= ID_ENCODING_PREFIX, but _mintERC721 (line 429) uses nftId_ >= ID_ENCODING_PREFIX - 1. This creates an inconsistency where _mintERC721 rejects ID_ENCODING_PREFIX - 1, but _encodeMintId would accept it.

Consider aligning the validation logic. If the intent is to prevent overflow when adding the prefix, use the same bound in both places: either >= ID_ENCODING_PREFIX - 1 or >= ID_ENCODING_PREFIX. The stricter bound (>= ID_ENCODING_PREFIX - 1) seems more appropriate to prevent tokenId == type(uint256).max after encoding.

Suggested change
if (mintId_ == 0 || mintId_ >= ID_ENCODING_PREFIX) {
if (mintId_ == 0 || mintId_ >= ID_ENCODING_PREFIX - 1) {

Copilot uses AI. Check for mistakes.
}

emit Transfer(from_, to_, value_);
emit ERC20Transfer(from_, to_, value_);
Copy link

Copilot AI Nov 24, 2025

Choose a reason for hiding this comment

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

The introduction of ERC20Transfer event breaks ERC20 standard compliance. The ERC20 standard requires emitting a Transfer(address indexed from, address indexed to, uint256 value) event, which is inherited from IERC20. By emitting ERC20Transfer instead at line 374, external tools, indexers, and contracts expecting standard ERC20 events will not detect these transfers.

The contract should emit the standard Transfer event from IERC20 for ERC20 transfers. If event naming conflicts are a concern, consider using event overloading or aliasing instead of creating a non-standard event name.

Suggested change
emit ERC20Transfer(from_, to_, value_);
emit Transfer(from_, to_, value_);

Copilot uses AI. Check for mistakes.

// ERC721 Events (using different names to avoid conflicts with ERC20)
// event Transfer(address indexed from, address indexed to, uint256 value);
event ERC20Transfer(address indexed from, address indexed to, uint256 value);
Copy link

Copilot AI Nov 24, 2025

Choose a reason for hiding this comment

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

The introduction of ERC20Transfer event breaks ERC20 standard compliance. The standard ERC20 Transfer event (inherited from IERC20) should be emitted for ERC20 transfers, not a custom ERC20Transfer event. External tools, indexers, and contracts will not recognize this non-standard event signature.

Suggested change
event ERC20Transfer(address indexed from, address indexed to, uint256 value);

Copilot uses AI. Check for mistakes.
…edDenomination and ERC404NullOwnerCappedUpgradeable contracts

- Simplified token ID management by removing the encoding and decoding methods, directly using the mint ID in relevant functions.
- Enhanced the `tokenURI` function to validate token IDs and streamline metadata generation.
- Updated tests to reflect changes in token ID handling and ownership assertions, ensuring consistency with the new implementation.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.


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


uint256 mintId = id_ & ~ID_ENCODING_PREFIX;
function tokenURI(uint256 mintId) public view virtual override returns (string memory) {
_validateTokenId(mintId);
Copy link

Copilot AI Nov 24, 2025

Choose a reason for hiding this comment

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

The _validateTokenId call on line 157 is redundant since ownerOf on line 158 also calls _validateTokenId internally and will revert if the token doesn't exist. Remove the duplicate validation call.

Suggested change
_validateTokenId(mintId);

Copilot uses AI. Check for mistakes.
// Get the ethscriptionId for this mintId from the manager
ERC20FixedDenominationManager mgr = ERC20FixedDenominationManager(manager);
bytes32 ethscriptionId = mgr.getMintEthscriptionId(deployEthscriptionId, mintId);

Copy link

Copilot AI Nov 24, 2025

Choose a reason for hiding this comment

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

Missing validation for ethscriptionId == bytes32(0). The removed code at lines 164-173 handled the case when no ethscription is found. Without this check, the code will attempt to fetch an ethscription with a zero ID, which may cause unexpected behavior or revert in the Ethscriptions contract.

Suggested change
// If no ethscription is found, return default metadata
if (ethscriptionId == bytes32(0)) {
string memory json = string.concat(
'{"name":"', name(), ' Token #', mintId.toString(), '"',
',"description":"Fixed denomination token for ', mintAmount().toString(), ' ', symbol(), ' tokens"',
',"ethscription_id":"0x0"',
'}'
);
return string.concat("data:application/json;base64,", Base64.encode(bytes(json)));
}

Copilot uses AI. Check for mistakes.
uint256 mintId = id_ & ~ID_ENCODING_PREFIX;
function tokenURI(uint256 mintId) public view virtual override returns (string memory) {
_validateTokenId(mintId);
ownerOf(mintId); // reverts on invalid / nonexistent
Copy link

Choose a reason for hiding this comment

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

Bug: Missing fallback for zero ethscriptionId in tokenURI

The tokenURI function no longer handles the case where getMintEthscriptionId returns bytes32(0). Previously, the code returned minimal metadata when ethscriptionId was zero. Now it proceeds to call getEthscription with a zero ID, which reverts with EthscriptionDoesNotExist(). This breaks the function for tokens where the ethscription mapping hasn't been initialized or in edge cases where the association is missing.

Fix in Cursor Fix in Web

@RogerPodacter RogerPodacter merged commit a6af08c into evm-backend-demo Nov 24, 2025
8 checks passed
@RogerPodacter RogerPodacter deleted the fix_collections branch November 24, 2025 22:42
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