Beatland Festival

AI First Flight #4
Beginner FriendlyFoundrySolidityNFT
EXP
View results
Submission Details
Impact: medium
Likelihood: medium
Invalid

Unsafe Token ID Encoding Leads to Silent Data Truncation and Incorrect Token Decoding

Unsafe Token ID Encoding Leads to Silent Data Truncation and Incorrect Token Decoding

Description

The protocol uses a custom token ID encoding scheme to pack a collectionId and an itemId into a single uint256 value. This is implemented by left-shifting the collectionId by 128 bits and adding the itemId into the lower 128 bits:

@> function encodeTokenId(uint256 collectionId, uint256 itemId) public pure returns (uint256) {
return (collectionId << COLLECTION_ID_SHIFT) + itemId;
}

Decoding is performed by shifting the token ID right by 128 bits to recover the collectionId, and by casting the token ID to uint128 to recover the itemId:

function decodeTokenId(uint256 tokenId) public pure returns (uint256 collectionId, uint256 itemId) {
collectionId = tokenId >> COLLECTION_ID_SHIFT;
itemId = uint256(uint128(tokenId));
}

This design implicitly assumes that both collectionId and itemId fit within 128 bits. However, the functions accept full uint256 values as inputs and do not enforce any bounds on these parameters.

If either collectionId or itemId exceeds 2^128 - 1, higher-order bits will be silently truncated during encoding or decoding. In particular, when decoding itemId, any non-zero bits in the upper 128 bits of tokenId are discarded, leading to loss of information and incorrect reconstruction of the original values.

This behavior does not cause a revert and may result in malformed token IDs that decode to unexpected collection or item identifiers, breaking internal assumptions across the protocol.


Risk

Likelihood: Medium

The issue does not require special permissions and affects public helper functions. While normal protocol flows may only use small, well-formed IDs, the lack of validation allows incorrect or malicious inputs to be passed, especially in future extensions, integrations, or edge cases. The silent nature of the truncation increases the likelihood of unnoticed misuse.

Impact: Medium

Incorrect encoding of token IDs can lead to logical inconsistencies, such as tokens being associated with the wrong collection or item number. This can break metadata resolution, ownership tracking, and collection-specific logic, potentially leading to user confusion or incorrect application behavior.


Proof of Concept

The following fuzz test demonstrates that the token ID encoding and decoding logic breaks when either collectionId or itemId exceeds the assumed 128-bit boundary. In such cases, decoding does not restore the original values due to silent truncation.

function testFuzz_EncodeError(uint256 collectionId, uint256 itemId) public view {
// Restrict inputs to values that exceed the 128-bit range
vm.assume(collectionId > type(uint128).max);
vm.assume(itemId > type(uint128).max);
// Attempt to encode the oversized values
try festivalPass.encodeTokenId(collectionId, itemId) returns (uint256 encoded) {
// Decode the encoded tokenId back into its components
(uint256 decodedCollection, uint256 decodedItem) = festivalPass.decodeTokenId(encoded);
// Verify that decoded values no longer match the original inputs
// due to truncation during encoding/decoding
assertFalse(decodedCollection == collectionId);
assertFalse(decodedItem == itemId);
} catch {
// Encoding may revert in some edge cases; such behavior
// further highlights the lack of input validation
}
}

To run the test, use the following Foundry command:

forge test --match-test testFuzz_EncodeError -vvvv

Output:

[PASS] testFuzz_EncodeError(uint256,uint256) (runs: 256, μ: 12140, ~: 12653)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 20.58ms (19.12ms CPU time)
Ran 1 test suite in 21.42ms (20.58ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

As shown by the test results, oversized inputs lead to incorrect decoding without reverting, which confirms the presence of the aforementioned vulnerability in the protocol.


Impact

If exploited or triggered unintentionally, this issue can cause token IDs to decode incorrectly, resulting in NFTs being misattributed to the wrong collection or edition. Since the truncation happens silently without reverting, the protocol may continue operating with corrupted identifiers, making the issue difficult to detect and debug.


Recommended Mitigation

Add input validation in the encodeTokenId function to ensure that both collectionId and itemId do not exceed the maximum value of a 128-bit unsigned integer (type(uint128).max). If either value exceeds this limit, the function should revert the transaction. This prevents any truncation or incorrect decoding from occurring and ensures the integrity of token IDs.

function encodeTokenId(uint256 collectionId, uint256 itemId) public pure returns (uint256) {
+ require(collectionId < type(uint128).max, "Incorrect data");
+ require(itemId < type(uint128).max, "Incorrect data");
return (collectionId << COLLECTION_ID_SHIFT) + itemId;
}
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge 23 days ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!