Pieces Protocol

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

Owner of token can burn it, which makes the NFT unclaimable.

Summary

After an NFT is divided to ERC20 tokens, a token can be burned by any owner of the token, which makes the NFT unclaimable.

Vulnerability Details

When an NFT is divided into ERC20 tokens using the TokenDivider contract, the resulting ERC20 tokens can be burned by any token holder. This can lead to a situation where the total supply of ERC20 tokens is reduced, making it impossible to claim the original NFT, as the required amount of ERC20 tokens no longer exists.

Impact

High - Lost of funds(ERC721 Token)

Tools Used

Foundry Tests

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import {Test} from "forge-std/Test.sol";
import {ERC20ToGenerateNftFraccion} from "src/token/ERC20ToGenerateNftFraccion.sol";
import {DeployTokenDivider} from 'script/DeployTokenDivider.s.sol';
import {TokenDivider} from 'src/TokenDivider.sol';
import {ERC721Mock} from '../mocks/ERC721Mock.sol';
import {ERC20Mock} from '@openzeppelin/contracts/mocks/token/ERC20Mock.sol';
import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
contract ERC20ToGenerateNftFraccionTest is Test {
DeployTokenDivider deployer;
TokenDivider tokenDivider;
ERC721Mock erc721Mock;
address public USER = makeAddr("user");
address public USER2 = makeAddr("user2");
uint256 constant public STARTING_USER_BALANCE = 10e18;
uint256 constant public AMOUNT = 100;
uint256 constant public TOKEN_ID = 0;
function testBurnTokens() public {
deployer = new DeployTokenDivider();
tokenDivider = deployer.run();
erc721Mock = new ERC721Mock();
erc721Mock.mint(USER);
vm.deal(USER2, STARTING_USER_BALANCE);
vm.startPrank(USER);
erc721Mock.approve(address(tokenDivider), TOKEN_ID);
tokenDivider.divideNft(address(erc721Mock), TOKEN_ID, AMOUNT);
vm.stopPrank();
ERC20ToGenerateNftFraccion erc20 = ERC20ToGenerateNftFraccion(tokenDivider.getErc20InfoFromNft(address(erc721Mock)).erc20Address);
vm.startPrank(USER);
// Burn 1 token from the user
erc20.burn(1);
vm.stopPrank();
assertEq(erc20.totalSupply(), tokenDivider.getErc20TotalMintedAmount(address(erc20)));
}
}

Recommendations

Instead of using a contract mapping for the minted amount:

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

Use the ERC20 standard function totalSupply to check the total supply of ERC20 tokens:

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

Lead Judging Commences

fishy 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.