Pieces Protocol

First Flight #32
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: high
Valid

TokenDivider::divideNft Allows Multiple ERC20 Token Deployments for Same NFT Leading to State Inconsistencies

Summary

TokenDivider::divideNft Allows Multiple ERC20 Token Deployments for Same NFT Leading to State Inconsistencies

Vulnerability Details

In the TokenDivider::divideNft function, a new ERC20 token contract is deployed each time the function is called, even for the same NFT. This can lead to a serious issue where newer token deployments overwrite the previous mappings in nftToErc20Info and erc20ToNft, potentially causing loss of user funds and state inconsistencies.

Impact

This vulnerability can result in:

  • Loss of track of previously minted ERC20 tokens

  • Users unable to locate or claim their tokens from previous fractionalization

  • State inconsistencies in the protocol

  • Potential permanent loss of user funds locked in old ERC20 contracts

Proof of Concept: Add the following test to demonstrate the vulnerability:

function testMultipleErc20DeploymentsForSameNft() public {
vm.startPrank(USER);
ERC721Mock(erc721Mock).approve(address(tokenDivider), 0);
tokenDivider.divideNft(address(erc721Mock), 0, AMOUNT);
address firstErc20 = tokenDivider.getErc20InfoFromNft(address(erc721Mock)).erc20Address;
vm.startPrank(address(tokenDivider));
ERC721Mock(erc721Mock).safeTransferFrom(address(tokenDivider), USER, 0);
vm.stopPrank();
vm.startPrank(USER);
ERC721Mock(erc721Mock).approve(address(tokenDivider), 0);
tokenDivider.divideNft(address(erc721Mock), 0, AMOUNT);
address secondErc20 = tokenDivider.getErc20InfoFromNft(address(erc721Mock)).erc20Address;
assertFalse(firstErc20 == secondErc20);
ERC20Mock firstErc20Mock = ERC20Mock(firstErc20);
uint256 lostTokens = firstErc20Mock.balanceOf(USER);
assertEq(lostTokens, AMOUNT);
firstErc20Mock.approve(address(tokenDivider), AMOUNT);
vm.expectRevert();
tokenDivider.claimNft(address(erc721Mock));
vm.stopPrank();
}

Tools Used

Foundry

Recommendations

function divideNft(address nftAddress, uint256 tokenId, uint256 amount) onlyNftOwner(nftAddress, tokenId) external {
if(nftAddress == address(0)) { revert TokenDivider__NftAddressIsZero(); }
if(amount == 0) { revert TokenDivider__AmountCantBeZero(); }
+ if(nftToErc20Info[nftAddress].erc20Address != address(0)) {
+ revert TokenDivider__NftAlreadyFractionalized();
+ }
ERC20ToGenerateNftFraccion erc20Contract = new ERC20ToGenerateNftFraccion(
string(abi.encodePacked(ERC721(nftAddress).name(), "Fraccion")),
string(abi.encodePacked("F", ERC721(nftAddress).symbol())));
// Rest of the function remains the same
}
Updates

Lead Judging Commences

fishy Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Wrong nft collection handling

Support

FAQs

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