When building smart contracts on Ethereum, one of the most common tasks is transferring Ether (ETH) between accounts. However, not all transfer methods are created equal. Solidity provides three primary functions for sending ETH: send, transfer, and call. While they may seem interchangeable at first glance, each has distinct behaviors, security implications, and use cases.
Understanding the differences between these functions is crucial for writing secure, efficient, and reliable smart contracts. In this guide, we’ll break down how each function works under the hood, their benefits and risks, and when to use them—so you can make informed decisions in your dApp development.
🔹 The send Function: A Legacy Approach
The send function was historically used to transfer Ether to an external owner account (EOA) or a contract address. Its syntax is simple:
address.send(amount)It returns a boolean value: true if the transfer succeeded, false otherwise.
👉 Learn how modern blockchain platforms handle secure transactions today.
How It Works Internally
Under the hood, send uses the EVM’s CALL opcode with a fixed gas stipend of 2300 gas. This amount is enough to emit an event or perform minimal logging but not enough to execute complex operations in the recipient contract (like writing to storage).
Because it doesn’t automatically revert on failure, your contract must manually check the return value and handle errors accordingly.
Example Code
contract SendExample {
function sendEther(address payable _to) public returns (bool) {
bool sent = _to.send(1 ether);
return sent;
}
}⚠️ Note: The recipient address must be marked as payable.Use Case
You might have used send in older contracts where you needed a non-reverting way to send funds—especially when interacting with potentially risky contracts that could run out of gas during fallback execution.
Security Considerations
- No reversion on failure: This makes
sendresistant to re-entrancy attacks but places the burden of error handling on the developer. - Deprecated: As of recent Solidity versions,
sendis considered obsolete due to its unpredictable behavior and limited functionality.
🔹 The transfer Function: Safe and Predictable
The transfer function improves upon send by automatically reverting the entire transaction if the transfer fails. Its syntax is:
address.transfer(amount)It consumes exactly 2300 gas and does not return any value—if it fails, the transaction reverts.
Internal Mechanism
Like send, transfer uses the CALL opcode with a 2300 gas limit. However, instead of returning a boolean, it throws an exception on failure, which reverts the state changes.
Example Code
contract TransferExample {
function transferEther(address payable _to) public {
_to.transfer(1 ether); // Reverts automatically on failure
}
}Use Case
Use transfer when you need guaranteed success or full reversion—ideal for simple payments where you don’t expect complex logic in the receiving contract’s fallback function.
Security Considerations
- ✅ Re-entrancy safe: Due to low gas forwarding, malicious contracts can’t re-enter your function.
- ❌ Fragile with modern contracts: Many newer contracts require more than 2300 gas to process incoming Ether (e.g., updating balances or emitting events), causing transfers to fail unexpectedly.
🔹 The call Function: Full Control and Flexibility
The most powerful and flexible method is call. It allows both Ether transfers and arbitrary function calls to other contracts.
Syntax
(bool success, bytes memory data) = address.call{value: amount}("");It returns a tuple:
success: Whether the call succeeded.data: Any data returned from the called function.
You must explicitly check success and handle failures.
Gas Behavior
Unlike send and transfer, call forwards all available gas by default unless limited. This enables complex interactions but introduces security risks.
Example: Sending Ether via call
contract CallExample {
function sendViaCall(address payable _to) public returns (bool) {
(bool success, ) = _to.call{value: 1 ether}("");
require(success, "Call failed");
return success;
}
}👉 Discover secure ways to interact with decentralized applications using advanced tools.
Use Case
Use call when:
- You need to invoke specific functions in another contract.
- You're working with contracts that require more than 2300 gas to process payments.
- You want maximum flexibility in cross-contract communication.
Security Considerations
- ⚠️ Re-entrancy risk: Because full gas is forwarded, attackers can exploit poorly secured functions to re-enter and drain funds.
- ✅ Mitigation: Always use re-entrancy guards (e.g., OpenZeppelin’s
ReentrancyGuard) or follow the checks-effects-interactions pattern.
🔍 Core Differences at a Glance
| Feature | send | transfer | call |
|---|---|---|---|
| Return Value | bool | none (reverts on fail) | (bool, bytes) |
| Gas Forwarded | 2300 | 2300 | All (unless capped) |
| Reverts on Failure? | No | Yes | No (must be manually handled) |
| Recommended? | ❌ Deprecated | ⚠️ Limited use | ✅ Preferred in modern contracts |
🧩 Underlying Mechanism: The CALL Opcode
All three functions rely on Ethereum’s low-level CALL opcode. Here's what happens behind the scenes:
- Gas: Specifies how much gas to forward.
- Address: Target of the call.
- Value: Amount of ETH sent.
- Input Data: Function selector or empty for plain transfers.
- Output Data: Return data (only relevant for
call).
While send and transfer are abstractions over CALL with strict gas limits, call gives developers full control—making it both powerful and dangerous if misused.
❓ Frequently Asked Questions (FAQ)
Q: Why was send deprecated?
A: Because it doesn't revert on failure and requires manual error handling, leading to potential silent failures. Modern best practices favor explicit success guarantees.
Q: Is transfer still safe to use?
A: It's safe against re-entrancy but increasingly unreliable because many contracts now require more than 2300 gas to handle incoming Ether. Use with caution.
Q: When should I use call instead of transfer?
A: Use call when interacting with modern contracts that perform state changes upon receiving ETH or when you need to call specific functions.
Q: Can I limit gas when using call?
A: Yes! You can specify a gas limit like this:
address.call{value: amount, gas: 5000}("");Q: What is a re-entrancy attack?
A: An exploit where a malicious contract repeatedly calls back into your contract before the initial transaction completes, potentially draining funds.
Q: Are there safer alternatives to raw call?
A: Yes. Libraries like OpenZeppelin provide wrappers and guards. Also, consider using higher-level interfaces or payment libraries designed for safety.
✅ Final Recommendations
As Ethereum evolves, so do best practices:
- Avoid
sendentirely—it’s outdated. - Limit use of
transferto simple, predictable scenarios. - Embrace
callwith proper safeguards for modern dApp development.
Understanding these nuances empowers you to build more resilient and secure decentralized applications.
👉 Stay ahead in blockchain development with cutting-edge crypto tools and insights.
Core Keywords: Ethereum, Solidity, send function, transfer function, call function, smart contract security, re-entrancy attack, CALL opcode