How ETH Is Received in Smart Contracts

How ETH Is Received in Smart Contracts — receive()
, fallback()
, and Common Mistakes
Smart contracts on Ethereum can receive ETH directly. But what actually happens when someone sends ETH to a contract? And why do some transactions fail silently while others go through?
Let’s dive deep into how Ether is handled in Solidity, the differences between receive()
and fallback()
, and some key developer mistakes you should avoid.
ETH Transfers 101
When you send ETH to a contract, one of two functions will be triggered based on the context:
receive() external payable
fallback() external payable
These are special functions in Solidity that aren’t called directly in code. They get triggered under very specific circumstances.
When Does receive()
Run?
The receive()
function runs when:
A contract receives ETH with empty calldata
The
receive()
function is marked payable
Example:
receive() external payable {
emit Received(msg.sender, msg.value);
}
If someone calls the contract with just ETH (e.g., using .transfer()
or sending ETH directly via MetaMask), this function will run.
Use case: Simple ETH deposits with no logic or function calls.
When Does fallback()
Run?
The fallback()
function is more general. It runs when:
A function is called that doesn’t exist
OR when calldata is not empty
fallback() external payable {
emit FallbackCalled(msg.sender, msg.value, msg.data);
}
You can also define a non-payable fallback if you want to reject ETH:
solidityCopyEditfallback() external {
revert("ETH not accepted");
}
Common Developer Mistakes
1. Not defining receive()
or fallback()
If your contract is meant to accept ETH but has neither, any direct ETH transfer will revert.
Fix: Add at least one payable
function (receive()
or fallback()
).
2. Heavy Logic in Fallbacks
Since fallback functions can get triggered unintentionally, avoid writing logic-heavy operations that might increase gas use or create vulnerabilities.
3. Not Handling msg.data
Sometimes, users (or attackers) send ETH with extra calldata. If you're only using receive()
, those transactions will revert. Always consider a fallback for maximum safety.
Real-World Example: Accepting ETH in a Treasury Contract
solidityCopyEdit// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;
contract Treasury {
address public owner;
event Received(address sender, uint256 amount);
event FallbackCalled(address sender, uint256 amount, bytes data);
constructor() {
owner = msg.sender;
}
receive() external payable {
emit Received(msg.sender, msg.value);
}
fallback() external payable {
emit FallbackCalled(msg.sender, msg.value, msg.data);
}
function withdraw() external {
require(msg.sender == owner, "Only owner");
payable(owner).transfer(address(this).balance);
}
}
Try sending ETH from your wallet or via Remix — both receive and fallback will log different events based on how the ETH was sent.
What About Vyper?
Vyper handles this similarly but doesn’t use receive()
or fallback()
keywords directly. It uses:
vyperCopyEdit@external
@payable
def __default__():
# Code here runs on unknown function call or direct ETH
Everything is handled in __default__
, so you must manually manage what to accept and reject.
Best Practices
Always define
receive()
if your contract is meant to receive ETH directly.Use a
fallback()
to catch unexpected calls or ETH sent with data.Don’t rely on these functions for business logic.
Always emit logs for transparency (
emit Received(...)
) for off-chain monitoring.
Final Words
Many developers forget to test how their contracts behave with unexpected inputs or ETH sent manually. A missing receive()
function can easily break dApps like fundraising contracts, treasuries, or NFT mints.
When building production-grade contracts, always include proper ETH handling, and test them via .call
, .transfer
, or even raw transactions.
Want me to cover gas optimizations, reentrancy, or cross-chain bridging next? Stay tuned.
Subscribe to my newsletter
Read articles from Aref XV directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Aref XV
Aref XV
Solidity & Vyper in learning | security researcher