Beginner FriendlyFoundry
100 EXP
View results
Submission Details
Severity: medium
Valid

`MondrianWallet::tokenURI` function exhibits weak randomness, leading to NFTs with predictable metadata.

Summary

The MondrianWallet documentation states,

You'll see the tokenURI function returns one of 4 random Mondrian art paintings. Each should have equal distribution and be random.

However, the tokenURI function depends solely on the modNumber, which is the result of tokenId % 10. This operation divides tokenId by 10 and returns the remainder. Since tokenId is presumably unique for each token, the modNumber will cycle through the values 0 through 9 in a predictable pattern.
In this code, if modNumber is 0, it returns ART_ONE, if modNumber is 1, it returns ART_TWO, 2 -> ART_THREE, and everything from 3 to 9 will be ART_FOUR. After that the cycle will repeat for the next 10 tokens and so on.

Impact

Likelihood: HIGH
Impact: MEDIUM

Users are led to believe that the NFTs are randomly generated, but in reality, the metadata is predictable. This can lead to a loss of trust in the protocol and its incentive model.

Proof of Concept

The predictable nature of the tokenURI function can be observed in the following code snippet (for the sake of the test I've added a mintNft function to be able to mint multiple NFTs):

function testNFTUriGenerationIsNotRandom() public {
for (uint i = 0; i <= 20; i++) {
vm.prank(user1);
wallet.mintNft(i);
}
vm.startPrank(user1);
assertEq(wallet.tokenURI(0), wallet.ART_ONE());
assertEq(wallet.tokenURI(1), wallet.ART_TWO());
assertEq(wallet.tokenURI(2), wallet.ART_THREE());
for (uint i = 3; i < 9; i++) {
assertEq(wallet.tokenURI(i), wallet.ART_FOUR());
}
assertEq(wallet.tokenURI(10), wallet.ART_ONE());
assertEq(wallet.tokenURI(11), wallet.ART_TWO());
assertEq(wallet.tokenURI(12), wallet.ART_THREE());
for (uint i = 13; i < 19; i++) {
assertEq(wallet.tokenURI(i), wallet.ART_FOUR());
}
vm.stopPrank();
}
Ran 1 test for test/MondrianWalletTest.t.sol:MondrianTest
[PASS] testNFTUriGenerationIsNotRandom() (gas: 682389)
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 9.53ms (1.07ms CPU time)

Tools Used

Manual review + Foundry

Recommended mitigation

To ensure an equal chance of selecting each variation, the protocol needs to implement a truly random selection mechanism, such as using a random number generator or incorporating external randomness from an oracle.

Updates

Lead Judging Commences

inallhonesty Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

NFTs are not random

Support

FAQs

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