Pieces Protocol

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

Token Accounting Inconsistency Leading to DOS and Fund Locking

Summary

The TokenDivider contract maintains its own accounting system through the balances mapping, but this state can become desynchronized with actual token balances when users transfer tokens directly through the ERC20 contract. This leads to a situation where:

  1. Users who have tokens can't use contract features (DOS)

  2. Users who don't have tokens are recorded as having them (Accounting Error)

Vulnerability Details

The contract implements a custom accounting system using the following mapping:

mapping(address user => mapping(address erc20Address => uint256 amount)) balances;

However, this accounting system is not synchronized with direct ERC20 token transfers. When users transfer tokens directly using the ERC20 contract's transfer() or transferFrom() functions, the balances mapping in TokenDivider is not updated. This creates a discrepancy between:

  • The actual token balances (tracked by the ERC20 contract)

  • The recorded balances in TokenDivider's accounting system

function sellErc20(address nftPegged, uint256 price,uint256 amount) external {
//cant set nft to zero address
if(nftPegged == address(0)) {
revert TokenDivider__NftAddressIsZero();
}
//...
ERC20Info memory tokenInfo = nftToErc20Info[nftPegged];
//balances[msg.sender][tokenInfo.erc20Address] is the amount of erc20 tokens the msg.sender holds
//someone might have transferred the tokens in to the address, but the contract state hasnt been updated
//(!!!) this is a DOS vector, where the contract thinks the msg.sender doesnt have the erc20 tokens, but they actually do
if(balances[msg.sender][tokenInfo.erc20Address] < amount) {
revert TokenDivider__InsuficientBalance();
}
//...
}
}

Impact

Users who accept tokens directly from ERC20 transfers are not accounted in balances[user][token] mapping and cannot use other functions like sellERC20() and claimNFT() leading to loss of user assets and funds locked in contract.

Tools Used

Manual + Foundry

Recommendations

  1. Direct balance checking using ERC20 balance checks:
    IERC(tokenAddress).balanceOf(userAddress)instead of the mapping.

Updates

Lead Judging Commences

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

Transfer ERC20ToGenerateNftFraccion separately to the contract

Support

FAQs

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