Trick or Treat

First Flight #27
Beginner FriendlyFoundry
100 EXP
View results
Submission Details
Severity: low
Invalid

[L-02] Missing Event Emission After NFT Transfer (Event Not Emitted Causes Inconsistent State Tracking)

Summary

In the resolveTrick function of the SpookySwap contract, after successfully transferring an NFT from the contract to the user, the contract fails to emit the Swapped event. This omission can lead to off-chain systems and users not being aware of the NFT transfer, causing inconsistencies between the on-chain state and external perceptions. The lack of event emission affects user experience and the reliability of applications that rely on these events to update NFT ownership statuses.

Vulnerability Details

Issue Overview

When a user calls the resolveTrick function to complete their purchase (after initially not paying enough during a "trick" scenario), the contract correctly transfers the NFT to the user. However, it does not emit the Swapped event to signal this transfer. Events are crucial for off-chain applications like wallets and explorers to track token movements and update user balances.

Affected Code

function resolveTrick(uint256 tokenId) public payable nonReentrant {
require(pendingNFTs[tokenId] == msg.sender, "Not authorized to complete purchase");
string memory treatName = tokenIdToTreatName[tokenId];
Treat memory treat = treatList[treatName];
uint256 requiredCost = treat.cost * 2; // Double price
uint256 amountPaid = pendingNFTsAmountPaid[tokenId];
uint256 totalPaid = amountPaid + msg.value;
require(totalPaid >= requiredCost, "Insufficient ETH sent to complete purchase");
// Transfer the NFT to the buyer
@> _transfer(address(this), msg.sender, tokenId);
// Clean up storage
delete pendingNFTs[tokenId];
delete pendingNFTsAmountPaid[tokenId];
delete tokenIdToTreatName[tokenId];
// Refund excess ETH if any
if (totalPaid > requiredCost) {
uint256 refund = totalPaid - requiredCost;
(bool refundSuccess, ) = msg.sender.call{value: refund}("");
require(refundSuccess, "Refund failed");
}
}

Problem Explanation

  • Missing Event Emission: After transferring the NFT to the user using _transfer(address(this), msg.sender, tokenId);, the function does not emit the Swapped event or any equivalent event to indicate the successful transfer.

  • Impact on Off-Chain Systems: Off-chain services that rely on event logs to update NFT ownership will not detect this transfer, leading to outdated or incorrect information displayed to users.

  • User Confusion: Users may not see their newly acquired NFT in wallets or platforms immediately, causing confusion and potential mistrust in the system.

Impact

  • Inaccurate Off-Chain Data: Wallets, explorers, and other applications may fail to update the user's NFT holdings, as they rely on emitted events to track changes.

  • User Experience Degradation: Users might believe the transaction failed or is pending, leading to unnecessary support requests or negative perceptions of the platform.

  • Operational Integrity: The contract's reliability is undermined when standard practices like event emissions are not consistently followed.

Severity Classification: Low

While the NFT transfer occurs correctly on-chain, the missing event affects the user experience and the accuracy of off-chain data. It does not, however, lead to a loss of funds, security vulnerabilities, or critical disruptions in contract functionality.

Tools Used

Manual code review

Recommendations

  1. Emit the Swapped Event After NFT Transfer

    Add the emit Swapped statement immediately after the NFT is transferred to the user within the resolveTrick function.

function resolveTrick(uint256 tokenId) public payable nonReentrant {
require(pendingNFTs[tokenId] == msg.sender, "Not authorized to complete purchase");
string memory treatName = tokenIdToTreatName[tokenId];
Treat memory treat = treatList[treatName];
uint256 requiredCost = treat.cost * 2; // Double price
uint256 amountPaid = pendingNFTsAmountPaid[tokenId];
uint256 totalPaid = amountPaid + msg.value;
require(totalPaid >= requiredCost, "Insufficient ETH sent to complete purchase");
// Transfer the NFT to the buyer
_transfer(address(this), msg.sender, tokenId);
+ // Emit the Swapped event to indicate successful transfer
+ emit Swapped(msg.sender, treatName, tokenId);
// Clean up storage
delete pendingNFTs[tokenId];
delete pendingNFTsAmountPaid[tokenId];
delete tokenIdToTreatName[tokenId];
// Refund excess ETH if any
if (totalPaid > requiredCost) {
uint256 refund = totalPaid - requiredCost;
(bool refundSuccess, ) = msg.sender.call{value: refund}("");
require(refundSuccess, "Refund failed");
}
}
Updates

Appeal created

bube Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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