Solidity — Part 4- Transfer, Send, and Call

Shishir Singh
4 min readJun 29, 2023

--

Solidity is a programming language used to write smart contracts on the Ethereum blockchain. Smart contracts are self-executing contracts that allow for the automatic transfer of digital assets when certain conditions are met. In this series, we will cover some of the more tricky areas of Solidity, aimed at the Intermediate level of Solidity skills.

In Part 3 we covered Inheritance, Virtual, Override, and Super.

This article will delve into the three primary methods for sending Ether: transfer, send, and call. We'll discuss their differences, use cases, and the recommended practices for advanced Solidity engineers.

Sending Ether: Transfer, Send, and Call

Solidity provides three methods to send Ether from one contract to another:

  1. transfer: This method allows you to send Ether, consuming 2300 gas units. If the operation fails, it throws an error.
  2. send: Similar to transfer, send also consumes 2300 gas units but returns a boolean value indicating the success or failure of the operation.
  3. call: This method is more flexible, allowing you to forward all gas or set a specific gas limit. Like send, it returns a boolean value indicating the operation's success or failure.

When to Use Transfer, Send, or Call

The choice between transfer, send, and call depends on the specific requirements of your contract and the level of control you need over the transaction.

  • transfer: This was once the simplest and safest way to send Ether, as it automatically reverts the transaction if the call fails. However, it only forwards a fixed amount of gas (2300), which may not be enough for more complex operations in the receiving contract. This limitation has led to transfer being considered less safe, as it can lead to unexpected behavior if the receiving contract's fallback function consumes more than 2300 gas.
  • send: This method is similar to transfer but returns false instead of throwing an exception when the call fails. This allows you to handle the failure in your contract. However, like transfer , it also only forwards 2300 gas.
  • call: This is the most flexible method and is recommended for sending Ether as of Solidity 0.6.0. It allows you to forward all remaining gas or specify a certain amount. This flexibility makes it suitable for more complex operations. However, it also requires you to handle the possibility of reentrancy attacks and manually check the return value.

Here’s an example of how each method can be used in a contract:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract SendEther {
function sendViaTransfer(address payable _to) public payable {
_to.transfer(msg.value); // Automatically reverts on failure
}
function sendViaSend(address payable _to) public payable {
bool sent = _to.send(msg.value); // Returns false on failure
require(sent, "Failed to send Ether");
}
function sendViaCall(address payable _to) public payable {
(bool sent, bytes memory data) = _to.call{value: msg.value}(""); // Returns false on failure
require(sent, "Failed to send Ether");
}
}

Receiving Ether: Receive and Fallback Functions

A contract that is meant to receive Ether must implement at least one of the following functions:

  1. receive() external payable: This function is triggered when the contract receives plain Ether (without data).
  2. fallback() external payable: This function is triggered when the received message contains data or when no other function matches the function identifier.

The receive function was introduced in Solidity 0.6.0 as a new and more explicit way to mark a contract as being able to receive Ether. If a contract has a receive function, it will be called whenever a callto the contract is made with empty calldata. However, if there is data in the calldata, or if the contract does not have a receive function, the fallback function will be called. If the contract neither has a receive nor a fallback function, it cannot receive Ether through regular transactions and will throw an exception.

Here’s a simple contract that can receive Ether:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract ReceiveEther {
receive() external payable {}
fallback() external payable {}
function getBalance() public view returns (uint) {
return address(this).balance;
}
}

Handling Errors: Revert, Assert, and Require

Solidity provides error-handling mechanisms like revert, assert, and require that you can use to handle errors in your contracts.

  • revert: This function aborts execution and reverts state changes but consumes all remaining gas and provides an error message.
  • assert: This function should only be used to test for internal errors and to check invariants. If the condition inside assert fails, it causes a runtime error, which leads to the transaction being reverted.
  • require: This function should be used to ensure valid conditions, such as inputs, or contract state variables are met, or to validate return values from calls to external contracts.

In the context of sending Ether, you can use require to ensure that the send or call the operation was successful:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract SendEther {
function sendViaSend(address payable _to) public payable {
bool sent = _to.send(msg.value);
require(sent, "Failed to send Ether"); // Reverts transaction if send fails
}
function sendViaCall(address payable _to) public payable {
(bool sent, bytes memory data) = _to.call{value: msg.value}("");
require(sent, "Failed to send Ether"); // Reverts transaction if call fails
}
}

Guarding Against Re-entrancy

Repentance is a common attack vector in smart contracts where an attacker can drain a contract of its Ether. To guard against re-entrance, you should:

  • Make all state changes before calling other contracts.
  • Use a re-entrance guard modifier.

As of December 2019, the recommended method for sending Ether is call in combination with a re-entrance guard.

Conclusion

Understanding the nuances of sending and receiving Ether is crucial for writing secure, efficient smart contracts in Solidity. While transfer and send were once the standard, the flexibility and safety of call have made it the recommended method for sending Ether as of recent Solidity versions. Always remember to guard against reentrancy attacks when writing your contracts. Happy coding!

In Part 5 we cover Functions, Call & Return Parameters.

--

--

Shishir Singh

Digital Assets, Blockchains, DLTs, Tokenization & Protocols & AI Intersection