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
- Automatic Revert on Failure: If the transfer fails (e.g., due to out-of-gas or invalid recipient),
transferautomatically reverts the entire transaction. - Fixed Gas Stipend: It forwards exactly 2,300 gas, which is enough to emit an event but not sufficient for complex logic in fallback functions.
- No Return Value: It doesn’t return a boolean; instead, it throws an exception if unsuccessful.
👉 Discover how modern blockchain platforms handle secure fund transfers with advanced tooling.
Ideal Use Cases
Use transfer when:
- You need guaranteed success or complete rollback.
- Interacting with simple wallets that don’t execute complex fallback logic.
- Prioritizing safety over flexibility.
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
- Returns Boolean: Unlike
transfer,sendreturnstrueon success andfalseon failure. - Fixed 2,300 Gas: Like
transfer, it limits forwarded gas to 2,300 units. - Manual Error Handling Required: Developers must explicitly check the return value and decide how to respond.
Ideal Use Cases
Use send when:
- You want to attempt a transfer without reverting the whole transaction.
- Graceful degradation or retry logic is needed upon failure.
- Working with external addresses where partial failure is acceptable.
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
- Full Gas Forwarding: By default,
.callforwards nearly all remaining gas unless specified otherwise. - Customizable Gas Limits: You can cap gas usage like
{value: amount, gas: 5000}to mitigate risks. - Returns Success Status: Returns a tuple
(bool success, bytes memory data)allowing precise handling. - Supports Function Calls: Can invoke specific functions on recipient contracts by including function signature in calldata.
👉 See how leading developers test and deploy secure ETH transfer logic using integrated tools.
Ideal Use Cases
Use .call when:
- Sending ETH to contracts that require more than 2,300 gas in fallback/receive functions.
- Building upgradable or modular systems needing dynamic interactions.
- You need maximum flexibility and performance.
Limitations
With great power comes great responsibility:
- Increased risk of reentrancy attacks if state changes aren’t properly ordered.
- Requires manual validation and proper error handling via
require(success)or similar checks.
Gas Consumption Comparison
Understanding gas costs helps optimize contract efficiency and avoid unexpected failures.
1. transfer
- Gas Forwarded: Fixed at 2,300 gas
- Purpose: Prevents reentrancy by limiting execution depth in recipient logic.
- Risk: May fail if the recipient requires more gas (e.g., logging events or updating storage).
2. send
- Gas Forwarded: Also limited to 2,300 gas
- Behavior: Same gas restriction as
transfer, but returns a boolean instead of reverting. - Best For: Lightweight payments where failure doesn't require full rollback.
3. call
- Gas Forwarded: Defaults to forwarding all available gas
- Flexibility: Allows optional gas capping:
.call{value: amount, gas: 10000}("") - Efficiency: Most efficient for complex interactions but demands careful security practices.
⚠️ Important: Starting from Solidity 0.8.0+, bothtransferandsendare marked as deprecated in many community standards because of their rigid gas model..callis now the preferred approach when used safely.
Summary and Best Practices
| Aspect | transfer | send | call |
|---|---|---|---|
| Safety | High (auto revert) | Medium | Low-Medium (manual handling) |
| Flexibility | Low | Medium | High |
| Gas Control | Fixed (2,300) | Fixed (2,300) | Full/customizable |
| Return Type | None (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.