SNARKeling Treasure Hunt

First Flight #59
Beginner FriendlyGameFiFoundry
100 EXP
View results
Submission Details
Severity: low
Valid

Claimed Event Emits msg.sender Instead of recipient, Breaking Off-Chain Tracking

Finding 3: Claimed Event Emits msg.sender Instead of recipient, Breaking Off-Chain Tracking

Root + Impact

Wrong address emitted in event + off-chain monitoring fails to identify actual payout recipient

Description

  • The Claimed event is designed to log which address received the reward payout for a given treasure. Off-chain indexers, subgraphs, and monitoring systems rely on this event to track which participants have been paid and to reconcile on-chain payouts with real-world treasure finders.

  • The Claimed event emits msg.sender (the address that submitted the transaction) instead of recipient (the address that actually received the ETH reward). Since the protocol intentionally allows a claimer to specify a different recipient address (the recipient is bound into the ZK proof as a public input), the event consistently logs the wrong address whenever the caller and recipient differ.

event Claimed(bytes32 indexed treasureHash, address indexed recipient); // @> event signature declares "recipient"
function claim(bytes calldata proof, bytes32 treasureHash, address payable recipient) external nonReentrant() {
// ...
(bool sent, ) = recipient.call{value: REWARD}(""); // @> ETH is sent to `recipient`
require(sent, "ETH_TRANSFER_FAILED");
emit Claimed(treasureHash, msg.sender); // @> emits `msg.sender` instead of `recipient`
}

Risk

Likelihood:

  • This occurs on every claim where the participant submits the proof on behalf of a different recipient address — a core use case the protocol is explicitly designed to support

  • Any off-chain system consuming the Claimed event to build payout records receives the wrong address on every such claim

Impact:

  • Off-chain indexers and subgraphs record the transaction submitter as the payout recipient instead of the actual ETH receiver, corrupting payout audit trails

  • Treasure hunt organizers cannot accurately reconcile which real-world participants received rewards, undermining the transparency of the hunt

  • Front-end dashboards displaying "who received rewards" show incorrect data to users

Proof of Concept

function testClaimedEventEmitsWrongAddress() public {
(
bytes memory proof,
bytes32 treasureHash,
address payable recipient
) = _loadFixture();
// recipient is a third party (not msg.sender)
// msg.sender in this test is address(this) from the test contract
vm.prank(participant);
vm.expectEmit(true, true, false, true);
emit Claimed(treasureHash, participant); // This currently PASSES — event emits msg.sender (participant)
hunt.claim(proof, treasureHash, recipient);
// But the actual ETH was sent to `recipient`, not `participant`
// The event is misleading — it says `participant` got paid, but `recipient` got the ETH
assertEq(recipient.balance, INITIAL_PARTICIPANT_BALANCE + 10 ether); // recipient got the ETH
// An off-chain indexer reading the event would think `participant` received funds
}

Recommended Mitigation

event Claimed(bytes32 indexed treasureHash, address indexed recipient);
function claim(...) external nonReentrant() {
// ...
- emit Claimed(treasureHash, msg.sender);
+ emit Claimed(treasureHash, recipient);
}
Updates

Lead Judging Commences

s3mvl4d Lead Judge 18 days ago
Submission Judgement Published
Validated
Assigned finding tags:

incorrect event parameter

The event is declared as event `Claimed(bytes32 indexed treasureHash, address indexed recipient);`, which clearly indicates that the second indexed field is meant to represent the reward recipient, but `claim()` emits `Claimed(treasureHash, msg.sender)` instead of `Claimed(treasureHash, recipient)`, even though the ETH transfer is sent to recipient and the proof itself is constructed around the public inputs (treasureHash, recipient). As a standalone finding, this is appropriately low severity because it is fundamentally an event/accounting inconsistency rather than a direct loss-of-funds issue: the core state transition and payout still follow the intended recipient, but off-chain consumers reading the event log will observe incorrect metadata about who was associated with the claim.

Support

FAQs

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

Give us feedback!