Pieces Protocol

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

ERC20 Information gets overridden if user converts 2 NFTs from same NFT address into ERC20 using divideNFT method.

Summary

The contract assumes that one user will convert only 1 NFT from an NFT address into ERC20. What if user has minted 2 NFTs from an NFT address? In such case if User wants to convert second NFT from same NFT Address into ERC20. The previous information is overridden.

Vulnerability Details

If there are multiple NFTs minted from same NFT address for the user. And user converts both NFTs into ERC20 one aftre another, "mapping(address nft => ERC20Info) nftToErc20Info;" is overridden. Previous ERC20Info is lost and replaced by recent ERC20Info.

To replication this following can be done -

  • Step 1 - User divides the first NFT (Token Id 0)

  • Step 2 - User gets (mints) second NFT from same ERC721 (Token id 1). Do a console.logAddress(tokenDivider.getErc20InfoFromNft(address(erc721Mock)).erc20Address)

  • Step 3 - User divides the second NFT (Token id 1). Notice that eRC20 Address has been overridden now.Do a console.logAddress(tokenDivider.getErc20InfoFromNft(address(erc721Mock)).erc20Address)

Notice that cosole log after Step 3 returns a different value for ERC20Address that Step 2.

Following is the POC. Just paste this test in TokenDividerTest.t.sol and run the test as "forge test -vv --match-test testNFTERCAssociation". Notice that console.log returns different values.

function testNFTERCAssociation() public {
vm.startPrank(USER);
// Step 1 - User divides the first NFT (Token Id 0)
erc721Mock.approve(address(tokenDivider), TOKEN_ID);
tokenDivider.divideNft(address(erc721Mock), TOKEN_ID, AMOUNT);
console.log("Address Corresponding to NFT is - ");
console.logAddress(
tokenDivider.getErc20InfoFromNft(address(erc721Mock)).erc20Address
);
vm.stopPrank();
// Step 2 - User gets second NFT from same ERC721 (Token id 1)
uint256 TOKEN_ID_2 = 1;
erc721Mock.mint(USER);
vm.startPrank(USER);
// Step 3 - User divides the second NFT (Token id 1). Notice that eRC20 Address has been overridden now
erc721Mock.approve(address(tokenDivider), TOKEN_ID_2);
tokenDivider.divideNft(address(erc721Mock), TOKEN_ID_2, AMOUNT);
console.log("Address Corresponding to NFT is - ");
console.logAddress(
tokenDivider.getErc20InfoFromNft(address(erc721Mock)).erc20Address
);
vm.stopPrank();
}

Impact

  • This is a logical flaw. Contract assumes that user cannot convert second NFT from same NFT address.

  • Data loss as the previous ERC20 Information is lost.

  • Impacts other functions which use nftToErc20Info variable like claimNFT, transferErcTokens

Tools User

Foundry

Recommendations

This can be resolved in couple of ways -

In divideNFT add a check if NFT Address is already associated with ERC20 token. Use the same token to mint more NFTs for user.

OR

"mapping(address nft => ERC20Info) nftToErc20Info;" should me "mapping(address nft => mapping(address ERC20 => tokenid)" etc

Updates

Lead Judging Commences

fishy Lead Judge 5 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.