Three Ways to Send ETH in Solidity

·

When building smart contracts in Solidity, one of the most common operations is transferring native cryptocurrency—Ether (ETH)—to another address or contract. However, not all transfer methods are created equal. There are three primary ways to send ETH in Solidity: transfer, send, and call. Each comes with distinct behaviors, gas implications, security considerations, and use cases.

Understanding these differences is crucial for writing secure, efficient, and future-proof smart contracts. This guide breaks down each method, compares their gas usage, highlights best practices, and answers common developer questions.

Using the transfer Method

The transfer function is one of the simplest and most intuitive ways to send ETH from a contract to a payable address.

Code Example

pragma solidity ^0.8.26;

contract SendEther {
    function transferEther(address payable recipient, uint256 amount) public {
        require(address(this).balance >= amount, "Insufficient balance");
        recipient.transfer(amount);
    }
}

Key Features

👉 Discover how modern blockchain platforms handle secure fund transfers with advanced tooling.

Ideal Use Cases

Use transfer when:

Limitations

While safe, transfer consumes more gas due to its revert mechanism and lacks error-handling granularity. It's also being phased out in favor of call in many modern codebases due to inflexibility.

Using the send Method

send is similar to transfer but offers slightly more control by returning a success status instead of reverting automatically.

Code Example

pragma solidity ^0.8.26;

contract SendEther {
    function sendViaSend(address payable _to) public payable returns (bool) {
        bool sent = _to.send(msg.value);
        if (!sent) {
            return false;
        }
        return true;
    }
}

Key Features

Ideal Use Cases

Use send when:

Limitations

Despite offering better control than transfer, send still suffers from the same gas limitation. Plus, forgetting to check the return value can lead to silent failures—a common security pitfall.

Using the call Method

The most powerful and recommended method today is low-level .call.

Code Example

pragma solidity ^0.8.26;

contract SendEther {
    function callEther(address payable recipient, uint256 amount) public returns (bool) {
        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Transfer failed.");
        return success;
    }
}

Key Features

👉 See how leading developers test and deploy secure ETH transfer logic using integrated tools.

Ideal Use Cases

Use .call when:

Limitations

With great power comes great responsibility:

Gas Consumption Comparison

Understanding gas costs helps optimize contract efficiency and avoid unexpected failures.

1. transfer

2. send

3. call

⚠️ Important: Starting from Solidity 0.8.0+, both transfer and send are marked as deprecated in many community standards because of their rigid gas model. .call is now the preferred approach when used safely.

Summary and Best Practices

Aspecttransfersendcall
SafetyHigh (auto revert)MediumLow-Medium (manual handling)
FlexibilityLowMediumHigh
Gas ControlFixed (2,300)Fixed (2,300)Full/customizable
Return TypeNone (throws)bool(bool, bytes)
Recommended?❌ Not advised❌ Avoid✅ Yes (with care)

Current Best Practice:

Always use .call with proper checks and state updates following the Checks-Effects-Interactions pattern to prevent reentrancy.

Frequently Asked Questions (FAQ)

Q: Why was transfer deprecated?
A: Because it hardcodes a 2,300 gas stipend, which may be insufficient for modern contracts. This rigidity leads to unpredictable failures and limits composability.

Q: Is .call unsafe?
A: Not inherently—but improper use can expose your contract to reentrancy attacks. Always update state before making external calls and validate return values.

Q: Can I send ETH to any address using these methods?
A: Yes, but only payable addresses can receive funds. Non-payable addresses or contracts without fallback/receive functions will cause transfers to fail.

Q: What happens if I forget to check the result of .call?
A: The transaction continues even if the transfer fails—potentially leading to incorrect state updates or lost funds. Always wrap .call results in a require() statement.

Q: How do I limit gas when using .call?
A: Use syntax like recipient.call{value: amount, gas: 5000}("") to restrict gas usage and reduce attack surface.

Q: Should I still use send in new projects?
A: No. While functional, it's outdated and offers no advantages over .call. Modern development favors explicit control and safety patterns enabled by .call.

👉 Learn how top-tier blockchain engineers implement secure ETH transfers using real-world tooling.

Final Thoughts

Choosing the right method to send ETH in Solidity depends on your security requirements, interaction complexity, and error-handling strategy. While transfer and send offer simplicity, they lack adaptability in today’s evolving ecosystem.

The .call method stands out as the most versatile and widely adopted solution—provided developers follow secure coding practices. As blockchain applications grow more sophisticated, mastering low-level calls becomes essential for robust, scalable smart contract design.

By understanding these core mechanisms—core keywords: Solidity ETH transfer, send ETH in smart contract, transfer vs send vs call, gas-efficient Ethereum transactions, secure ETH sending methods, fallback function handling, reentrancy prevention, low-level call in Solidity—you position yourself to write safer, more effective decentralized applications.