Skip to main content

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() or fallback() 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.