Pieces Protocol

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

Non-Compliance with CEI Pattern in tranferErcTokens()

Summary

In the original transferErcTokens function, the state is updated before confirming the success of the external transfer. This can lead to inconsistencies in the contract’s state if the external transfer fails after state changes are applied, which is a violation of the CEI (Checks-Effects-Interactions) pattern.

Vulnerability Details

  • The state changes (updating balances) occur before the external transfer is confirmed.

  • If the external transfer fails (due to insufficient funds, transaction reversion, etc.), the contract's state will already be modified, leading to inconsistent or incorrect balances.

  • This creates a potential risk for funds being lost or misallocated due to the mismatch between the internal state and external contract state.

Impact

  • Inconsistent State: If the external token transfer fails after updating the state, the internal balances may not match the true token holdings.

  • Security Risk: Attackers could exploit this inconsistency by manipulating balances, leading to incorrect fund allocations or even the possibility of a reentrancy attack in certain cases.

Tools Used

Manual code review

Recommendations

  1. Follow CEI Pattern: Ensure that state changes are made only after the success of the external transfer.

  2. Revised Function: Update the function to:

    • Perform checks first (input validation, balance checks).

    • Perform external interaction (transfer tokens) next.

    • Update the state and emit events only if the external interaction is successful.

function transferErcTokens(address nftAddress, address to, uint256 amount) external {
if(nftAddress == address(0)) revert TokenDivider__NftAddressIsZero();
if(to == address(0)) revert TokenDivider__CantTransferToAddressZero();
if(amount == 0) revert TokenDivider__AmountCantBeZero();
// Change to storage for consistency
ERC20Info storage tokenInfo = nftToErc20Info[nftAddress];
if(balances[msg.sender][tokenInfo.erc20Address] < amount) {
revert TokenDivider__NotEnoughErc20Balance();
}
// Perform the transfer first (Interaction)
bool success = IERC20(tokenInfo.erc20Address).transferFrom(msg.sender, to, amount);
if(!success) revert TokenDivider__TransferFailed();
// Update state after successful transfer (Effects)
balances[msg.sender][tokenInfo.erc20Address] -= amount;
balances[to][tokenInfo.erc20Address] += amount;
emit TokensTransfered(amount, tokenInfo.erc20Address);
}

By adopting this pattern, the contract ensures that its state remains consistent, and external interactions will not cause unwanted side effects or vulnerabilities.

Updates

Lead Judging Commences

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

Reentrancy

Appeal created

fishy Lead Judge 8 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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