When building Ethereum smart contracts, one of the most fundamental operations is transferring Ether (ETH) between accounts or within contract logic. As a developer, choosing the right method to send Ether isn't just about functionality—it's a critical decision that impacts security, gas efficiency, and overall contract reliability.
Solidity provides three primary functions for Ether transfers: send, transfer, and call. While they may appear interchangeable at first glance, each comes with distinct behaviors, limitations, and best practices. Understanding these differences is essential for writing robust and secure decentralized applications (dApps).
What Is Ether and Why Transfer It?
Ether (ETH) is the native cryptocurrency of the Ethereum blockchain. It serves multiple purposes, including paying for transaction fees (gas) and facilitating value exchange within smart contracts.
There are numerous scenarios where your contract might need to transfer Ether:
- Distributing rewards to users in a staking protocol
- Refunding users after a failed operation
- Facilitating peer-to-peer payments in a decentralized marketplace
- Managing funds during a crowdfunding campaign
👉 Learn how to securely manage digital assets while developing smart contracts.
The way you handle these transfers can significantly affect your contract’s behavior—especially in edge cases like failed transactions or malicious interactions.
The Three Ether Transfer Methods in Solidity
1. send: Simple but Limited
The send function is one of the oldest methods for transferring Ether. It's straightforward to use and returns a boolean value indicating success or failure.
bool success = recipient.send(1 ether);
if (!success) {
// Handle failure manually—e.g., log an event or trigger fallback logic
}Key Characteristics:
- Sends exactly 2300 gas, sufficient only for basic account operations (like logging).
- Does not revert on failure; instead, it returns
false. - Requires manual error handling.
Because send doesn’t automatically revert the transaction, it allows your contract to continue execution even if the transfer fails. This can be useful in non-critical scenarios, but it also increases the risk of silent failures.
Best Use Cases:
- Sending ETH to external owner accounts where failure is tolerable.
- Situations where you want to attempt a payment without halting the entire transaction.
However, due to its limited gas and manual error handling, send is largely considered outdated for modern dApp development.
2. transfer: Safe by Design (But Deprecated)
Prior to Solidity 0.6.0, transfer was the go-to method for secure Ether transfers. Like send, it forwards only 2300 gas, but unlike send, it reverts the entire transaction if the transfer fails.
function payOwner() public payable {
owner.transfer(msg.value); // Reverts automatically if failed
}Key Characteristics:
- Fixed gas stipend of 2300 units.
- Automatically reverts on failure—ensures atomicity.
- Built-in safety against certain types of exploits.
This made transfer ideal for simple, secure payments—especially when interacting with externally owned accounts (EOAs), which don’t require additional gas to receive funds.
However, starting with Solidity 0.8.0, transfer has been deprecated because the fixed 2300 gas limit proved too restrictive even for some legitimate contract receivers.
Best Use Case:
- Legacy projects still using older Solidity versions.
- Simple contracts where interaction is limited to EOAs.
👉 Discover tools that help test and deploy secure Solidity contracts efficiently.
3. call: Flexible and Future-Proof
The call method is now the recommended approach for Ether transfers in modern Solidity development (0.6.0+). It offers full control over gas usage and supports complex contract interactions.
function sendViaCall(address payable _to) public payable {
(bool sent, ) = _to.call{value: msg.value}("");
require(sent, "Failed to send Ether");
}Key Characteristics:
- Can forward all available gas unless specified otherwise.
- Returns a boolean (
true/false)—you must manually check success. - Allows setting custom gas amounts:
_to.call{value: amount, gas: 5000}(""). - Most flexible but requires careful coding to prevent reentrancy attacks.
A reentrancy attack occurs when a malicious contract calls back into your function before it completes execution—potentially draining funds. This vulnerability famously led to the DAO hack in 2016.
To mitigate risks:
- Use the Checks-Effects-Interactions pattern
- Apply reentrancy guards (e.g., OpenZeppelin’s
ReentrancyGuard) - Avoid state changes after external calls
Best Use Cases:
- Interacting with complex smart contracts that require more than 2300 gas.
- Building DeFi protocols, NFT marketplaces, or any system needing flexibility.
- Modern dApps developed with Solidity 0.8.x or later.
Comparison Summary
| Feature | send | transfer | call |
|---|---|---|---|
| Gas forwarded | 2300 | 2300 | Up to all remaining (configurable) |
| Failure behavior | Returns false | Reverts transaction | Returns false |
| Error handling | Manual | Automatic | Manual |
| Security | Moderate | High (but outdated) | High (with proper safeguards) |
| Flexibility | Low | Low | High |
| Recommended? | ❌ No | ❌ Deprecated | ✅ Yes |
Best Practices for Secure Ether Transfers
- Use
callfor new projects – It’s the only future-proof option in modern Solidity. - Always validate return values – Never assume a
callsucceeded without checking. - Follow the Checks-Effects-Interactions pattern – Update state before making external calls.
- Limit gas when necessary – Use
{gas: X}to restrict gas if you're unsure about receiver logic. - Prevent reentrancy – Employ mutex locks or trusted libraries like OpenZeppelin.
Frequently Asked Questions
Q: Why was transfer deprecated?
A: Because the fixed 2300 gas limit became too restrictive—even some legitimate contract fallback functions require more gas to execute safely.
Q: Can I still use send in new contracts?
A: Technically yes, but it's discouraged due to poor error visibility and lack of automatic reverts.
Q: Is call always safer than transfer?
A: Not inherently—while more flexible, call shifts responsibility to the developer to handle failures and prevent reentrancy.
Q: How do I protect against reentrancy when using call?
A: Use reentrancy guards, avoid state changes after external calls, and follow secure design patterns.
Q: Does call work with all Ethereum accounts?
A: Yes—it works with both externally owned accounts (EOAs) and smart contracts.
Q: Should I use inline assembly for better performance?
A: Generally not recommended unless absolutely necessary. Stick to high-level safety patterns unless optimizing for specific gas-sensitive scenarios.
Final Thoughts
Choosing the correct Ether transfer method in Solidity is more than a technical detail—it’s a core aspect of secure smart contract design. While send and transfer offered simplicity in earlier days, call is now the standard for modern development.
Your choice affects not only functionality but also resilience against exploits and compatibility with evolving Ethereum standards.
👉 Start building secure, high-performance dApps with reliable blockchain infrastructure today.
Take time to audit existing contracts. Replace deprecated patterns, implement proper error handling, and ensure your Ether transfers follow current best practices. Small improvements can dramatically enhance the security and reliability of your decentralized applications.