Beatland Festival

First Flight #44
Beginner FriendlyFoundrySolidityNFT
100 EXP
View results
Submission Details
Impact: medium
Likelihood: medium
Invalid

Unsafe External Call in redeemMemorabilia() May Allow Reentrancy

Root + Impact

Description

  • Describe the normal behavior in one or more sentences

Redeeming memorabilia should follow a safe pattern: internal state updates first, then external calls. In this implementation, an external call to burnFrom() on the BeatToken contract is made before any internal mint logic, and there is no nonReentrant modifier in place.

  • Explain the specific issue or problem in one or more sentences

This breaks the checks effects interactions pattern, exposing the function to potential reentrancy issues if burnFrom() is hooked (e.g., via a malicious BeatToken implementation).

function redeemMemorabilia(uint256 tokenId, uint256 cost) external {
@> BeatToken(beatToken).burnFrom(msg.sender, cost); // external call
_mint(msg.sender, tokenId, 1, ""); // internal state change
}

Risk

Likelihood:

  • Reason 1 // Describe WHEN this will occur (avoid using "if" statements)

Occurs whenever redeemMemorabilia() is called.

  • Reason 2

Reentrancy becomes exploitable if BeatToken is malicious, upgradable, or if control over it is gained.

Impact:

  • Impact 1

Can result in multiple redemptions with the same token balance.

  • Impact 2

Potentially allows unauthorized minting, DoS via unexpected flow, or draining of collectibles.

Proof of Concept

Assume BeatToken.burnFrom() contains a callback to redeemMemorabilia()

function burnFrom(address user, uint256 amount) external {
if (!alreadyCalled) {
alreadyCalled = true;
Festival(msg.sender).redeemMemorabilia(tokenId, cost);
}
// Then burns user's tokens...
}

Reentrancy allows redeemMemorabilia() to be called again before mint finishes, enabling multiple redemptions per burn.

Recommended Mitigation

The redeemMemorabilia() function performs an external call to burnFrom() before completing internal logic. If the external contract (e.g. a malicious BEAT token) contains reentrant code, it could re-enter the function and exploit the contract’s state. This makes the function vulnerable to reentrancy.

- remove this code
BeatToken(beatToken).burnFrom(msg.sender, cost);
_mint(msg.sender, tokenId, 1, "");
+ add this code
_mint(msg.sender, tokenId, 1, "");
BeatToken(beatToken).burnFrom(msg.sender, cost);
Reordering assumes mint is safe to call first, which depends on application logic.
Updates

Lead Judging Commences

inallhonesty Lead Judge 29 days ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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