Pieces Protocol

First Flight #32
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Severity: medium
Invalid

[M-2] Invalid tokenInfo Validation in TokenDivider::transferErcTokens

Summary

The transferErcTokens function retrieves the ERC20Info struct from the nftToErc20Info mapping but does not validate whether the tokenInfo is properly initialized or contains valid data. If the nftAddress does not exist in the nftToErc20Info mapping, the tokenInfo will contain default values (e.g., address(0) for erc20Address). This can lead to unexpected behavior, such as transferring tokens from/to invalid addresses or interacting with uninitialized ERC20 contracts, potentially resulting in fund loss or state inconsistencies.

Vulnerability Details

The transferErcTokens function does not validate the tokenInfo struct before using it. If the nftAddress is not mapped to a valid ERC20Info, the tokenInfo will contain default values (e.g., erc20Address = address(0)). This can result in the function attempting to interact with an invalid or uninitialized ERC20 contract, leading to failed transactions or unintended consequences.

The following code demonstrates the Vulnerability:

function transferErcTokens(address nftAddress, address to, uint256 amount) external {
// ... (checks omitted for brevity)
ERC20Info memory tokenInfo = nftToErc20Info[nftAddress]; // No validation
balances[msg.sender][tokenInfo.erc20Address] -= amount;
balances[to][tokenInfo.erc20Address] += amount;
emit TokensTransfered(amount, tokenInfo.erc20Address);
// Attempt to transfer tokens without validating tokenInfo
IERC20(tokenInfo.erc20Address).transferFrom(msg.sender, to, amount);
}

Exploit Scenario:

A user calls transferErcTokens with an nftAddress that is not mapped to any ERC20Info (e.g., due to a typo or incorrect input).

The tokenInfo struct contains default values (e.g., erc20Address = address(0)).

The function attempts to transfer tokens using IERC20(tokenInfo.erc20Address).transferFrom, which will fail or interact with an invalid address.

The balances are updated incorrectly, leading to inconsistencies and potential fund loss.

Impact

Medium Impact: Funds are indirectly at risk if the tokenInfo is invalid (e.g., erc20Address is address(0)). Users may lose funds if tokens are sent to an invalid or uninitialized ERC20 contract.

State Inconsistency: The contract's state (balances) may become inconsistent, as balances are updated even if the token transfer fails or interacts with an invalid address.

Unexpected Behavior: The function may attempt to interact with an uninitialized or invalid ERC20 contract, leading to failed transactions or unintended consequences.

Tools Used

Manual review, foundry

Recommendations

Add a validation check to ensure that the tokenInfo contains a valid erc20Address before proceeding with the transfer. For example:

function transferErcTokens(address nftAddress, address to, uint256 amount) external {
// ... (checks omitted for brevity)
ERC20Info memory tokenInfo = nftToErc20Info[nftAddress];
if (tokenInfo.erc20Address == address(0)) {
revert TokenDivider__InvalidErc20Address(); // Revert if erc20Address is invalid
}
balances[msg.sender][tokenInfo.erc20Address] -= amount;
balances[to][tokenInfo.erc20Address] += amount;
emit TokensTransfered(amount, tokenInfo.erc20Address);
// Ensure the transferFrom call is checked
bool success = IERC20(tokenInfo.erc20Address).transferFrom(msg.sender, to, amount);
require(success, "Token transfer failed");
}

Additional Considerations:

Consider adding an event to log invalid nftAddress inputs for better debugging and monitoring.

Use a custom error for reverting on invalid erc20Address to save gas and improve clarity.

Updates

Lead Judging Commences

fishy Lead Judge 5 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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