Incorrectly Payable
What it detects
This detector flags functions marked with the payable
modifier that do not actually require or handle incoming ether or tokens. In Solidity, adding payable
allows a function to receive ETH along with the call, but if the function does not make use of msg.value
or transfer the funds, it may indicate a design oversight or latent bug.
Marking unnecessary functions as payable
can lead to:
- Users mistakenly sending ETH thinking it's required or used
- Funds getting stuck in the contract without any way to withdraw them
- Ambiguity in function intent, reducing contract readability and auditability
- Potential for denial-of-service if the contract balance grows uncontrollably
Typical symptoms
- The function accepts ETH but never accesses or uses
msg.value
- There is no
receive()
orfallback()
to handle plain ether transfers - No mechanism exists to recover or refund mistakenly sent ETH
- Contract balance grows unintentionally over time
- Audit tools or static analyzers warn of unused
payable
modifiers
Solidity snippet (v0.8.25)
pragma solidity ^0.8.25;
contract PayableMistake {
uint256 public stored;
// Not supposed to accept ether; user might assume ETH is needed
function setValue(uint256 x) external payable {
stored = x;
}
}
Corrected Version
function setValue(uint256 x) external {
stored = x;
}
Why it matters on EVM
The Ethereum Virtual Machine will forward ether to any payable
function, regardless of whether the value is used. If a user mistakenly sends ETH to a payable
function that doesn’t utilize it, the contract will retain the ether (potentially indefinitely), unless a withdrawal mechanism is present.
Consequences include:
- User funds being irrecoverably locked in the contract
- Attackers exploiting misleading payable functions to trick users (e.g., phishing-like patterns)
- Increased difficulty in auditing and understanding the contract's true behavior
- Accidental protocol behavior like inflation of contract balance without accounting
To maintain clarity and safety, only functions that genuinely need to receive ether should be marked payable
. Any function that does not consume msg.value
should omit the modifier.