Exploring the different methods of transferring ERC20 tokens on the Ethereum Network

NwachukwuNwachukwu
5 min read

When building a decentralized application, sending ERC20 tokens plays a vital role in the chain of events as it is one of the most basic functionalities implemented in modern-day Decentralized applications. This article aims to provide a comprehensive guide on the different ways developers can implement the transfer of value.

Sending ERC20 tokens 💰

Transferring ERC20 tokens on the Ethereum network can be accomplished through various methods, each offering unique advantages and considerations for users. In this guide, we will explore these methods, possible bottlenecks associated with any of them, and general practice.

The major methods available for transferring value are the following:

  1. Send()

  2. Transfer()

  3. Call()

Send()

The send function transfers tokens to a recipient and returns false if it fails. It does not automatically revert the transaction when it fails. The send function sends 2300 gas units with the transaction, which is enough to log an event but often not enough for complex computations.

Now Let's take a look at how to use the send function to transfer value.

// Using send
function sendToken(address payable _to, uint256 _amount) external payable {
    // Sends 2300 gas, returns false on failure
    bool success = _to.send(_amount);
    require(success, "Failed to send Token");
}

This function sends a token to a user's address which is specified as an argument in the function, the address is payable to be able to accept the sent Token. The line _to.send(msg.value); uses the send() function to transfer _amount which is the value of Ether sent with the transaction.

The function will not revert automatically on failure but will rather return true or false, the line require(success, "Failed to send Tokens"); Requires that success translates to true else it throws the associated error "Failed to send Tokens".

Despite the send method providing a return value, it is no longer recommended for sending value due to

  • potential re-entrancy attacks

  • and the limited Gas provided which is often unable for more complex computations.

Transfer()

Similar to the send() method, the transfer() method transfers value to the recipient. This was once the simplest and safest way to send value, as it automatically reverts the transaction if the call fails. but throws an exception if the transfer fails to complete, this ensures that the transaction is reverted automatically on failure.

Just like the send() method it only forwards a fixed amount of gas (2300), which may not be enough for more complex operations in the receiving contract.

The transfer() method was a staple method before solidity 0.6.0 after which the call() method was introduced.

Let's take a look at how we can use the transfer method to transfer value in a dapp.

// Using transfer
function transferTokens(ddress payable _to, uint256 _amount) external payable {
    // Sends 2300 gas, reverts on failure
    _to.transfer(_amount);
}

This function sends Tokens to a user's address which is specified as an argument in the function, the address is payable to be able to accept the sent Tokens. The line _to.transfer(msg.value); uses the transfer() function to transfer the tokens.

Although the transfer method is easy to use, the gas 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 and results in loss of gas for the users interacting with it

Call()

The call method is a low-level call that can be used to send Tokens as well as invoke functions from other contracts. It stands as the most versatile way to send value and interact with other contracts. It allows for forwarding all available gas or specifying a gas limit, unlike the send and transfer methods the call also provides a boolean (true or false) return value that indicates the transfer's success or failure along with any data returned by the function.

Here’s an example of using the call method:

// Using call
function callTokens(address _to, uint256 gasLimit) external {
    // No gas limit, returns boolean on success
    (bool success, bytes memory data) = _to.call{value: msg.value, gas: gasLimit}("");
    require(success, "Call failed.");
}

This function sends value to a user's address which is specified as an argument in the function. It is important to note that the address or function must not be marked as payable to be able to accept the sent tokens as the call() method automatically fills in the "blank spaces". So the transfer will go through regardless of whether it is marked as payable or not.

(bool sent, bytes memory data) - the variable sent is responsible for returning a true or false value based on the status of the transaction, while the variable data holds any information that the function may return.

The line _to.call(msg.value, gas: gasLimit); uses the call() function to transfer value which is msg.value and takes in an optional argument of gas which is being passed from the function arguments. Here you can see the flexibility that the call function provides because now we can specify as much gas as we can.

Why use Call?

After December 2019, the call method became the recommended way to send Ether. This is because:

  • Unlike send and transfer, call allows you to specify the amount of gas you want to forward to the recipient providing more control over gas usage for complex interactions between contracts.

  • It gives you more control over error handling by checking the success boolean and taking appropriate action.

  • It returns any data returned by the function call, giving us the power to utilize any such information.

Transferring ERC20 tokens on the Ethereum network can be achieved through various methods, each with its own set of advantages and limitations. The send() and transfer() methods, while straightforward, come with significant gas limitations and potential security risks, making them less favorable for complex transactions. On the other hand, the call() method offers greater flexibility and control over gas usage and error handling, making it the recommended approach for sending ERC20 tokens in modern decentralized applications. Developers can make informed decisions to ensure secure and efficient value transfers within their applications.

0
Subscribe to my newsletter

Read articles from Nwachukwu directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Nwachukwu
Nwachukwu

I am a skilled full-stack developer experienced in JavaScript, TypeScript, react, PHP, and Laravel. I create visually appealing and responsive user interfaces. I have a strong background in back-end development with proficiency in Laravel, enabling me to build robust and scalable server-side solutions. My skills extend to data management using tools like SQL, and POSGRESQL for efficient storage and real-time communication. Additionally, I actively contribute to open-source projects and have a knack for BAAS like supabase.