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
payablemodifiers
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.