Solidity Sending ETH: transfer, send, and call Explained

·

Understanding how to send Ether (ETH) in Solidity is essential for any Ethereum smart contract developer. Whether you're building decentralized applications (dApps), payment systems, or token contracts, knowing the correct and secure way to transfer ETH between addresses is crucial. In this guide, we’ll explore the three primary methods for sending ETH in Solidity: transfer(), send(), and call(). We'll compare their behaviors, gas limitations, error handling mechanisms, and best practices—helping you write safer and more efficient code.


Core Keywords

These keywords naturally align with common search queries from developers learning Solidity or debugging transaction issues. We'll integrate them throughout the article to enhance SEO without compromising readability.


Receiving Ether: The ReceiveETH Contract

Before we can send ETH, we need a contract that can receive it. Let’s start by creating a simple ReceiveETH contract that logs incoming transactions and allows us to check its balance.

contract ReceiveETH {
    // Event to log amount received and remaining gas
    event Log(uint amount, uint gas);

    // Triggered when ETH is sent to the contract
    receive() external payable {
        emit Log(msg.value, gasleft());
    }

    // Returns the current ETH balance of the contract
    function getBalance() public view returns (uint) {
        return address(this).balance;
    }
}

This contract includes:

After deploying this contract, you’ll notice its initial balance is 0 ETH. Once we send funds using one of the sending methods below, this value will update accordingly.

👉 Learn how to securely test ETH transfers on a real blockchain environment.


Sending Ether: The SendETH Contract

To demonstrate sending ETH, we’ll create a SendETH contract with a payable constructor and receive function so it can hold and forward ETH.

contract SendETH {
    constructor() payable {}

    receive() external payable {}
}

With this foundation, we can now implement the three methods of sending ETH: transfer(), send(), and call().


Method 1: Using transfer()

The transfer() function sends a specified amount of ETH to another address with a built-in gas limit of 2300 gas—enough to cover a basic transfer but not enough for complex logic in the recipient’s fallback or receive functions.

Key Features:

Example Code:

function transferETH(address payable _to, uint256 amount) external payable {
    _to.transfer(amount);
}
⚠️ Note: This function will revert if _to is not a valid payable address or if the contract doesn't have sufficient balance.

When testing:

After success, calling getBalance() on ReceiveETH confirms the received amount.

While convenient, transfer() is considered deprecated in modern Solidity due to its rigid gas model and potential for unexpected failures with complex contracts.


Method 2: Using send()

The send() method works similarly to transfer() but returns a boolean instead of reverting automatically on failure.

Key Features:

Example Code:

function sendETH(address payable _to, uint256 amount) external payable {
    bool success = _to.send(amount);
    if (!success) {
        revert SendFailed();
    }
}

Here, we must explicitly check the return value. If send() fails (e.g., due to out-of-gas or invalid recipient), we manually revert using a custom error.

Despite offering more control, send() shares the same limitations as transfer() and is rarely used in new projects because of its inflexibility and safety risks if error handling is missing.


Method 3: Using call() (Recommended)

The most flexible and currently recommended method for sending ETH is call().

Key Features:

Example Code:

function callETH(address payable _to, uint256 amount) external payable {
    (bool success,) = _to.call{value: amount}("");
    if (!success) {
        revert CallFailed();
    }
}

Using .call{value: amount}("") allows full control over gas and ensures compatibility with modern contracts that may perform non-trivial operations upon receiving funds.

Best Practice: Always verify the boolean result and revert if needed. This pattern gives both flexibility and safety.

👉 Discover how advanced developers use call() for cross-contract interactions.


Frequently Asked Questions (FAQ)

Q: Why is call() preferred over transfer() and send()?
A: Because call() removes the rigid 2300 gas stipend, allowing recipients to execute more complex logic. It's future-proof and aligns with current Solidity security recommendations.

Q: Is transfer() still safe to use?
A: While transfer() automatically reverts on failure (a safety feature), it’s discouraged since Solidity 0.8.x due to potential issues with contracts relying on higher gas. Use only if you fully understand the constraints.

Q: What happens if I forget to check the return value of send() or call()?
A: The transaction may proceed even if the ETH transfer failed, leading to silent failures. Always validate the result and revert appropriately.

Q: Can a contract reject incoming ETH?
A: Yes. If a contract lacks a receive() or payable fallback() function, sending ETH will fail. Additionally, these functions can include conditions to reject payments.

Q: How do I test ETH transfers locally before going live?
A: Use development tools like Hardhat or Foundry with local networks or testnets (e.g., Sepolia). Fund your contracts via faucets and simulate various scenarios including failures.

Q: What is the difference between receive() and fallback() functions?
A: The receive() function handles plain ETH transfers with no data. The fallback() handles calls with data or ETH when no other function matches. Only one of each can exist, and both must be external and payable to accept value.


Summary: Choosing the Right Method

MethodGas LimitReverts on Failure?Recommended?Use Case
transfer()2300YesLegacy code only
send()2300No (returns bool)Rare edge cases
call()UnlimitedNo (manual check)All modern applications

In modern Solidity development:

By understanding these differences, you can build more robust and secure smart contracts that interact safely with Ether transfers.

👉 Start building secure Solidity contracts with real-time blockchain tools.