Pieces Protocol

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

Burned ERC20 tokens aren't updated in TokenDivider balances causing unclaimable NFT

Summary

The deployed fERC20 when dividing an NFT is burnable but TokenDivider::claimNft doesn't take it into account. Users balances aren't updated on burn and the fERC20 total supply isn't checked. If a user burns any amount of fERC20 tokens, the NFT will be stuck in the contract indefinitely

Vulnerability Details

ERC20ToGenerateNftFraccion is a burnable token and tokens can be burned by interacting with the ERC20 contract

contract ERC20ToGenerateNftFraccion is ERC20, ERC20Burnable {

In TokenDivider::claimNft burned tokens and total supply of the ERC20 aren't checked causing a mismatched in users balances if they burned any amount of tokens

if (
balances[msg.sender][tokenInfo.erc20Address] <
erc20ToMintedAmount[tokenInfo.erc20Address]
) {
revert TokenDivider__NotEnoughErc20Balance();
}

Impact

  • It will be impossible to use the function TokenDivider::claimNft and the NFT will be stuck for ever in the contract as there are no other ways to retrieve the NFT

POC

function testBurningAnyAmountMakesNftUnclaimable(
uint256 amountToBurn
) public nftDivided {
vm.assume(amountToBurn <= AMOUNT && amountToBurn > 0);
ERC20ToGenerateNftFraccion fraccionERC20 = ERC20ToGenerateNftFraccion(
tokenDivider.getErc20InfoFromNft(address(erc721Mock)).erc20Address
);
assertEq(
tokenDivider.getErc20TotalMintedAmount(address(fraccionERC20)),
AMOUNT
);
vm.startPrank(USER);
fraccionERC20.burn(amountToBurn);
// User has the total supply as a balance
assertEq(fraccionERC20.totalSupply(), fraccionERC20.balanceOf(USER));
// User approves all
fraccionERC20.approve(address(tokenDivider), type(uint256).max);
vm.expectRevert();
// User can't claim the NFT despite having the total supply
tokenDivider.claimNft(address(erc721Mock));
vm.stopPrank();
}

Recommendations

  • The token total supply and user's fERC20 could be compared via the fERC20 contract

ERC20Info memory tokenInfo = nftToErc20Info[nftPegged];
+ IERC20 erc20Contract = IERC20(tokenInfo.erc20Address)
- if (balances[msg.sender][tokenInfo.erc20Address] < amount) {
+ if (erc20Contract.balanceOf(msg.sender) < erc20Contract.totalSupply()) {
revert TokenDivider__InsuficientBalance();
}
Updates

Lead Judging Commences

juan_pedro_ventu Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Appeal created

0xalexsr Submitter
4 months ago
0xalexsr Submitter
4 months ago
juan_pedro_ventu Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Lack of token access control chekcs

Any person can mint the ERC20 token generated in representation of the NFT

Support

FAQs

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