The contract under review contains multiple instances of low-level calls using msg.sender.call{value: refund}()
. Low-level calls are generally less safe than alternative methods because they bypass Solidity’s built-in error handling and require careful implementation to avoid silent failures or gas-related issues. The identified issue relates to these low-level calls in both the trickOrTreat
and resolveTrick
functions, which can introduce security risks, such as failed refunds that go unnoticed.
trickOrTreat
and resolveTrick
The contract uses call
to transfer ETH back to the user as a refund in cases where excess ETH has been sent. Specifically, low-level calls like msg.sender.call{value: refund}()
are employed in the following functions:
While call
provides flexibility, it is prone to failure if not handled properly, and it bypasses Solidity's automatic error reporting, potentially leading to silent failures. Additionally, call
forwards all remaining gas, which may open up opportunities for reentrancy attacks or other unintended behavior if not properly guarded
LINE: 102 and 142
forge test -vvv
Result:
forge test -vvv
[⠑] Compiling...
No files changed, compilation skipped
Ran 2 tests for test/test.sol:SpookySwapTest
[PASS] testContract() (gas: 2381)
[PASS] testLowLevelCallIssues() (gas: 219447)
Logs:
=== Testing Low Level Call Issues ===
Test 1: Silent Failure
ETH Actually Received: 0
Balance Change: 100000000000000000
Test 2: Forced Failure
Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 7.28ms (1.11ms CPU time)
Ran 1 test suite in 87.55ms (7.28ms CPU time): 2 tests passed, 0 failed, 0 skipped (2 total tests)
The test proves:
The call can return success even when ETH transfer fails
State changes proceed despite failed transfer
No verification of actual ETH receipt
The vulnerability is proven by:
ETH Received = 0 but state changed
Balance deduction occurred despite transfer failure
Contract continued execution thinking refund succeeded
The primary impact of this vulnerability is twofold:
Silent Failures: The call
function may fail, resulting in the user not receiving their refund. Without proper error handling, this failure can go unnoticed, leading to incorrect balances and possible user dissatisfaction.
Potential Security Risks: Although ReentrancyGuard
is applied throughout the contract, low-level calls can still introduce security risks, such as gas-related issues or unforeseen edge cases in future upgrades. If a recipient contract consumes excessive gas, the refund could fail.
Slither .
Manual Code Review
foundry forge test
Use transfer
Instead of call
: Replace the low-level call
with the safer transfer
function in both trickOrTreat
and resolveTrick
functions, as transfer
automatically reverts on failure and limits gas to 2,300 units, preventing the recipient from executing complex logic.
Example:
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.