Raisebox Faucet

First Flight #50
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Impact: low
Likelihood: high
Invalid

Event `Claimed` Emits Wrong Address in Race Condition Scenario

Root + Impact

The Claimed event emits msg.sender instead of faucetClaimer, in the claimFaucetTokens() method

Description

  • The expected behavior is that events should accurately reflect what actually happened - tokens are transferred to faucetClaimer, so the event should emit faucetClaimer.

  • The line 232 emits msg.sender in the Claimed event, but line 231 transfers tokens to faucetClaimer. Due to the race condition, these can be different addresses when concurrent transactions overwrite the shared faucetClaimer state variable.

function claimFaucetTokens() public {
faucetClaimer = msg.sender; // Can be overwritten by concurrent tx
// ... function logic ...
// Interactions
@> _transfer(address(this), faucetClaimer, faucetDrip); // Transfers to faucetClaimer
@> emit Claimed(msg.sender, faucetDrip); // But emits msg.sender!
}

Risk

Likelihood:

  • Occurs whenever the race condition is triggered by concurrent claims in the same block

  • More likely when the sender and faucet claimer are different

Impact:

  • Off-chain systems monitoring events will receive incorrect information about who received tokens

  • Analytics and tracking systems will show wrong claim recipients

  • Could cause confusion in debugging or auditing claim history

Proof of Concept

Here is the proof of concept, where 2 traction are sent in same block

it("Should show event emits msg.sender instead of actual recipient", async function () {
console.log(`\nAlice address: ${alice.address}`);
console.log(`Bob address: ${bob.address}`);
// Simulate race condition scenario
await ethers.provider.send("evm_setAutomine", [false]);
// Both submit claims concurrently
const aliceTx = faucet.connect(alice).claimFaucetTokens();
const bobTx = faucet.connect(bob).claimFaucetTokens();
await Promise.all([aliceTx, bobTx]);
await ethers.provider.send("evm_mine");
await ethers.provider.send("evm_setAutomine", [true]);
// Check balances
const aliceBalance = await faucet.balanceOf(alice.address);
const bobBalance = await faucet.balanceOf(bob.address);
console.log(`\nAlice received: ${ethers.formatEther(aliceBalance)} tokens`);
console.log(`Bob received: ${ethers.formatEther(bobBalance)} tokens`);
// Get events
const filter = faucet.filters.Claimed();
const events = await faucet.queryFilter(filter);
console.log(`\n=== Event Analysis ===");
events.forEach((event, i) => {
console.log(`Event ${i + 1}: Claimed(${event.args.user}, ${ethers.formatEther(event.args.amount)})`);
});
});

Recommended Mitigation

Change the event to emit faucetClaimer instead of msg.sender to accurately reflect the actual recipient.

function claimFaucetTokens() public {
// ... function logic ...
// Interactions
_transfer(address(this), faucetClaimer, faucetDrip);
- emit Claimed(msg.sender, faucetDrip);
+ emit Claimed(faucetClaimer, faucetDrip);
}
Updates

Lead Judging Commences

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

Support

FAQs

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