Closed c4-bot-10 closed 2 months ago
Hello @koolexcrypto , I believe you should take a look at this one because even if it is a design decision , the information is being hidden from the lambda user ( There is no function inside the contract to call in order to see what is coming next ) . So an attacker can get an edge and mint only golden god nft per gen . At the end of max generation he will own every possible golden god NFT . This is catastrophic because it put power inside a single user hands , he will then be free to game the system .
Thank you for your feedback.
Since it is intended design and the it doesn't lead to a significant risk, it remain invalid or at most QA.
Hello , @koolexcrypto after re-reading this report I believe it’s a medium because of the following:
The final goal of randomness in this game is to create a mint race between players . Smart contract is written in a way that is trying to hide the upcoming nft to the user . User does not know what he will mint before minting. So all users are bling minting. The issue here arise when a malicious user can craft his transaction by breaking the protocol and seeing what is coming next while others don’t . This he can freely mint best nfts as he wish and kidnap all the best value nft of the game .
impact : In real world there is a game concept called P2W ( Pay to win ) it’s game in which to win players should pay their weapons …. What happens with those games is F2P ( Free to play ) gamers eventuall leave the game when the discover the P2W mechanism and long term impact is the game will go dead . There are a lot of example of those games out there . In our case here , when legit user will see that only one wallet is minting golden god entropy nft for each generation , they will just stop minting nft in the game because they know they are being outplayed .
Because this make the game unfair to other users , and put all the golden god entropy nft in one single malicious user wallet , I believe this is of a medium severity . Let me know what you think
Lines of code
https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/EntropyGenerator/EntropyGenerator.sol#L164-L175
Vulnerability details
Impact
Each generation of NFT have one NFT that will be most valuable. Knowing that there is 10 generation, an attacker can mint the 10 most valuable NFT of the entire TraitForgeNFT collection and game the system. The protocol randomly assign the entropy of 999999 to a slot between 513 and 770 to make it appear only at the end of minting of total generation supply. This golden god value (999999) appearance is said to be totally random by the protocol. There is no way for normal users to see the upcoming NFT entropy, they mint an NFT they don't know anything about. It's only after minting that they can see what they got as seen in _mintInternal() function which mint the token and then calculate it's entropy value and assign it.
Furthermore the variables that are checked for in orther to determine the golden god entropy value are made private in an attempt to prevent users from calculating the entropy of non minted tokens so they can game it: see: https://github.com/code-423n4/2024-07-traitforge/blob/279b2887e3d38bc219a05d332cbcb0655b2dc644/contracts/EntropyGenerator/EntropyGenerator.sol#L12-L13
And a simple formula is used to calculate the golden god entropy internally ( again internally because the functions are only called internally and are not accessible to external caller). This is the formula:
Proof of Concept
Copy and paste this PoC inside TestForgeNft.test.ts (replace the file content with this PoC or ideally create a new test file)
const { expect } = require('chai'); const { ethers } = require('hardhat');
describe('TraitForgeNFT', () => { let entityForging: EntityForging; let nft: TraitForgeNft; let owner: HardhatEthersSigner; let user1: HardhatEthersSigner; let user2: HardhatEthersSigner; let entityTrading: EntityTrading; let entropyGenerator: EntropyGenerator; let nukeFund: NukeFund; let devFund: DevFund; let merkleInfo: any;
before(async () => { [owner, user1, user2] = await ethers.getSigners(); //const user0 = '0x0000000000000000000000000000000000000000';
});
describe('Attacker gaming the system', () => {
}); });