Solidity offers three primary methods for sending Ether (ETH) to other contracts: transfer(), send(), and call(). Among these, call() is the recommended approach due to its flexibility and lack of gas restrictions.
ReceiveETH Contract Example
First, let’s deploy a ReceiveETH contract capable of accepting ETH transfers. This contract includes:
- A
Logevent to record received ETH amounts and remaining gas. - A
receive()function triggered upon ETH receipt. - A
getBalance()function to check the contract’s ETH balance.
contract ReceiveETH {
event Log(uint amount, uint gas);
receive() external payable {
emit Log(msg.value, gasleft());
}
function getBalance() public view returns (uint) {
return address(this).balance;
}
}After deployment, getBalance() will return 0 since no ETH has been sent yet.
SendETH Contract Implementation
We’ll create a SendETH contract with three methods to send ETH to ReceiveETH.
contract SendETH {
constructor() payable {}
receive() external payable {}
}1. Using transfer()
- Syntax:
recipient.transfer(amount) - Gas Limit: 2300 (sufficient for basic transfers).
- Behavior: Automatically reverts on failure.
function transferETH(address payable _to, uint256 amount) external payable {
_to.transfer(amount);
}Example:
- Fails if
amount > msg.value(reverts). - Succeeds if
amount <= msg.value(updates balance).
2. Using send()
- Syntax:
recipient.send(amount) - Gas Limit: 2300.
- Behavior: Returns
bool(must handle failures manually).
function sendETH(address payable _to, uint256 amount) external payable {
bool success = _to.send(amount);
if (!success) {
revert SendFailed();
}
}Example:
- Fails if
amount > msg.value(reverts after manual check). - Succeeds if
amount <= msg.value.
3. Using call()
- Syntax:
recipient.call{value: amount}("") - Gas Limit: None (flexible for complex logic).
- Behavior: Returns
(bool, bytes)(handle failures manually).
function callETH(address payable _to, uint256 amount) external payable {
(bool success,) = _to.call{value: amount}("");
if (!success) {
revert CallFailed();
}
}Example:
- Fails if
amount > msg.value(reverts after manual check). - Succeeds if
amount <= msg.value.
Key Takeaways
call()- ✅ No gas limits.
- ✅ Most flexible (recommended).
- ❌ Requires manual failure handling.
transfer()- ✅ Auto-reverts on failure.
- ❌ 2300 gas limit (use for simple transfers).
send()- ❌ Rarely used due to manual handling and gas limits.
👉 Explore advanced Solidity techniques to deepen your understanding.
FAQs
Q1: Why is call() preferred over transfer()?
A: call() offers no gas restrictions and greater flexibility, making it suitable for contracts with complex receive() logic.
Q2: What happens if ETH transfer fails with transfer()?
A: The transaction automatically reverts, ensuring no partial state changes.
Q3: How can I check a contract’s ETH balance?
A: Use address(this).balance in a view function like getBalance().
Q4: Can I send ETH to non-payable functions?
A: No. The target contract must have a receive() or payable function.
Q5: What’s the gas cost for ETH transfers?
A: Fixed costs apply (e.g., 21k gas base + additional for logic).
For more Web3 insights, 👉 check out our developer resources.