Skip to content

Commit 7ab3767

Browse files
committed
Refactor token ID handling in ERC20FixedDenomination and ERC404NullOwnerCappedUpgradeable 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.
1 parent 8d4e965 commit 7ab3767

File tree

2 files changed

+61
-22
lines changed

2 files changed

+61
-22
lines changed

contracts/src/ERC20FixedDenomination.sol

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ contract ERC20FixedDenomination is ERC404NullOwnerCappedUpgradeable {
9090
_transferERC20(from, to, units());
9191

9292
// Transfer the specific NFT using the proper function
93-
uint256 id = ID_ENCODING_PREFIX + nftId;
93+
uint256 id = _encodeMintId(nftId);
9494
_transferERC721(from, to, id);
9595
}
9696

@@ -155,10 +155,11 @@ contract ERC20FixedDenomination is ERC404NullOwnerCappedUpgradeable {
155155
/// @notice Returns metadata URI for NFT tokens
156156
/// @dev Returns a data URI with JSON metadata fetched from the main Ethscriptions contract
157157
function tokenURI(uint256 id_) public view virtual override returns (string memory) {
158-
// This will revert InvalidTokenId / NotFound on bad ids
159-
ownerOf(id_);
160-
161-
uint256 mintId = id_ & ~ID_ENCODING_PREFIX;
158+
// Normalize and enforce existence (accepts human mintId or encoded tokenId)
159+
uint256 tokenId = _normalizeTokenId(id_);
160+
ownerOf(tokenId); // reverts on invalid / nonexistent
161+
162+
uint256 mintId = _decodeTokenId(tokenId);
162163

163164
// Get the ethscriptionId for this mintId from the manager
164165
ERC20FixedDenominationManager mgr = ERC20FixedDenominationManager(manager);

contracts/src/ERC404NullOwnerCappedUpgradeable.sol

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ abstract contract ERC404NullOwnerCappedUpgradeable is
7979
// ERC20 Events are inherited from IERC20 (Transfer, Approval)
8080

8181
// ERC721 Events (using different names to avoid conflicts with ERC20)
82+
// event Transfer(address indexed from, address indexed to, uint256 value);
83+
event ERC20Transfer(address indexed from, address indexed to, uint256 value);
8284
event ERC721Transfer(address indexed from, address indexed to, uint256 indexed id);
8385
event ERC721Approval(address indexed owner, address indexed spender, uint256 indexed id);
8486
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
@@ -202,12 +204,9 @@ abstract contract ERC404NullOwnerCappedUpgradeable is
202204
}
203205

204206
function ownerOf(uint256 id_) public view virtual override(IERC404) returns (address) {
205-
if (!_isValidTokenId(id_)) {
206-
revert InvalidTokenId();
207-
}
208-
209207
TokenStorage storage $ = _getS();
210-
TokenData storage t = $.tokens[id_];
208+
uint256 tokenId = _normalizeTokenId(id_);
209+
TokenData storage t = $.tokens[tokenId];
211210

212211
if (!t.exists) {
213212
revert NotFound();
@@ -223,7 +222,13 @@ abstract contract ERC404NullOwnerCappedUpgradeable is
223222

224223
function getApproved(uint256 id_) public view virtual returns (address) {
225224
TokenStorage storage $ = _getS();
226-
return $.getApproved[id_];
225+
uint256 tokenId = _normalizeTokenId(id_);
226+
227+
if (!$.tokens[tokenId].exists) {
228+
revert NotFound();
229+
}
230+
231+
return $.getApproved[tokenId];
227232
}
228233

229234
function isApprovedForAll(address owner_, address operator_) public view virtual override(IERC404) returns (bool) {
@@ -366,25 +371,29 @@ abstract contract ERC404NullOwnerCappedUpgradeable is
366371
$.balances[to_] += value_;
367372
}
368373

369-
emit Transfer(from_, to_, value_);
374+
emit ERC20Transfer(from_, to_, value_);
370375
}
371376

372377
/// @notice Transfer an ERC721 token
373378
function _transferERC721(address from_, address to_, uint256 id_) internal virtual {
374379
TokenStorage storage $ = _getS();
375-
TokenData storage t = $.tokens[id_];
376-
377-
if (from_ != ownerOf(id_)) {
380+
uint256 tokenId = _normalizeTokenId(id_);
381+
TokenData storage t = $.tokens[tokenId];
382+
383+
if (!t.exists) {
384+
revert NotFound();
385+
}
386+
if (from_ != t.owner) {
378387
revert Unauthorized();
379388
}
380389

381390
if (from_ != address(0)) {
382391
// Clear approval
383-
delete $.getApproved[id_];
392+
delete $.getApproved[tokenId];
384393

385394
// Remove from sender's owned list
386395
uint256 lastTokenId = $.owned[from_][$.owned[from_].length - 1];
387-
if (lastTokenId != id_) {
396+
if (lastTokenId != tokenId) {
388397
uint256 updatedIndex = t.index;
389398
$.owned[from_][updatedIndex] = lastTokenId;
390399
$.tokens[lastTokenId].index = uint88(updatedIndex);
@@ -399,9 +408,9 @@ abstract contract ERC404NullOwnerCappedUpgradeable is
399408
}
400409
t.owner = to_;
401410
t.index = uint88(newIndex);
402-
$.owned[to_].push(id_);
411+
$.owned[to_].push(tokenId);
403412

404-
emit ERC721Transfer(from_, to_, id_);
413+
emit ERC721Transfer(from_, to_, tokenId);
405414
}
406415

407416
/// @notice Mint ERC20 tokens without triggering NFT creation
@@ -424,7 +433,7 @@ abstract contract ERC404NullOwnerCappedUpgradeable is
424433
TokenStorage storage $ = _getS();
425434

426435
// Add the ID_ENCODING_PREFIX to the provided ID
427-
uint256 id = ID_ENCODING_PREFIX + nftId_;
436+
uint256 id = _encodeMintId(nftId_);
428437

429438
TokenData storage t = $.tokens[id];
430439

@@ -444,8 +453,37 @@ abstract contract ERC404NullOwnerCappedUpgradeable is
444453
// HELPER FUNCTIONS
445454
// =============================================================
446455

447-
function _isValidTokenId(uint256 id_) internal pure returns (bool) {
448-
return id_ > ID_ENCODING_PREFIX && id_ != type(uint256).max;
456+
/// @dev Normalizes caller input into the encoded tokenId form (adds prefix for human mint IDs).
457+
function _normalizeTokenId(uint256 id_) internal pure returns (uint256 tokenId_) {
458+
if (id_ == 0 || id_ == type(uint256).max) {
459+
revert InvalidTokenId();
460+
}
461+
462+
tokenId_ = id_ < ID_ENCODING_PREFIX ? _encodeMintId(id_) : id_;
463+
464+
if (tokenId_ <= ID_ENCODING_PREFIX || tokenId_ == type(uint256).max) {
465+
revert InvalidTokenId();
466+
}
467+
}
468+
469+
/// @dev Encodes a human-friendly mintId into the full tokenId with prefix.
470+
function _encodeMintId(uint256 mintId_) internal pure returns (uint256) {
471+
if (mintId_ == 0 || mintId_ >= ID_ENCODING_PREFIX) {
472+
revert InvalidTokenId();
473+
}
474+
uint256 tokenId = ID_ENCODING_PREFIX + mintId_;
475+
if (tokenId == type(uint256).max) {
476+
revert InvalidTokenId();
477+
}
478+
return tokenId;
479+
}
480+
481+
/// @dev Decodes an encoded tokenId back to the human-friendly mintId.
482+
function _decodeTokenId(uint256 tokenId_) internal pure returns (uint256) {
483+
if (tokenId_ <= ID_ENCODING_PREFIX || tokenId_ == type(uint256).max) {
484+
revert InvalidTokenId();
485+
}
486+
return tokenId_ - ID_ENCODING_PREFIX;
449487
}
450488

451489
// =============================================================

0 commit comments

Comments
 (0)