4 min read

[EthersJs - 5] Transferring ERC20 tokens

[EthersJs - 5] Transferring ERC20 tokens
Photo by Tech Daily / Unsplash

In the last tutorial, we learned how to transfer ether from one account to another, and earlier, we learned how to read balances of different addresses. In this article, we will learn how to transfer tokens.

For this, we need to interact with the ERC20 contract of the token, and therefore, we need to initialize the contract, and to initialize the contract ( with it's address and  ABI), we need a provider.

However, since we will be sending a transaction, in this case, we will be needing a signer as well

So, let's get started on it. We will be transferring LINK token in Goerli network. The main reason for this is that it's easy to get LINK token from chainlink faucet on the Goerli network.

Before we start, get your private key from metamask for your throwaway learning account (P.S: It is really important to use an account that is not your main account, and does not have money. NEVER SHARE PRIVATE KEY OR MNEMONIC WWITH ANYONE). You can look through this tutorial to learn how to get private key from your metamask account if you do not know already.  

Once you have your private key for your throwaway account, and have some test LINK on Goerli, let's get started:

First, we connect to Goerli network like so!

const ethers = require("ethers");      

const sender = '0xd25d2FBF90c6A810361448cDd4fF397A9bE5a703'
const receiver = '0x7f4e3F3178B884F6A9290e08a899306649F5bFf1'
const senderPk = 'sender-private-key-here'

const provider = new ethers.providers.InfuraProvider(
    "goerli",        
    "infura-api-key"
)

const signer = new ethers.Wallet(senderPk, provider)

We define, the sender and the receiver addresses, and then we connect to goerli using the infura provider, details of which were also covered in one of the older tutorials.

We also instantiate a signer with the private key of the account that owns link and will be sending some link.

Then, we get a generic ERC20 ABI, and link's smart contract address. Easiest way of doing it just going to coingecko.com.

const ERC20_ABI = [
    "function name() view returns (string)",
    "function symbol() view returns (string)",
    "function totalSupply() view returns (uint256)",
    "function balanceOf(address) view returns (uint)",
    "function transfer(address to, uint amount)",
    "event Transfer(address indexed from, address indexed to, uint amount)"
];

const address = '0x326C977E6efc84E512bB9C30f76E30c160eD06FB' // LINK
const contract = new ethers.Contract(address, ERC20_ABI, signer)
const token_amount = "1"

With access to ABI, link address and the signer , we can easily instantiate the contract object with const contract = new ethers.Contract(address, ERC20_ABI, signer)

We also define the amount of token we will be sending in the variable token_amount, which we will be using later.

Once we have all this, we are ready to send the token from sender to receiver with the transfer function defined in the contract.

contract.transfer(receiver, numberOfTokens)

Note: that the numberOfTokens passed into the function is in wei, and we use ether's in-built helper to transform token_amount to this format:

const numberOfTokens = ethers.utils.parseUnits(token_amount, 18)

The 18 here is the decimal number of the token, and is the smallest unit of the token. If we want to send 1 token ( as is the case here) the helper it to 1 * 10^18 units.

Most ERC20 tokens have a decimal of 18, however some like USDC has decimal of 6. It is important to know the decimal of the token, when dealing with it. You can find it easily in etherscan

Or, reading the standard decimals getter method on all ERC20 contracts:
contract.decimals().  We are not really using this functionality in this tutorial.

Moreover, contract.transfer(receiver, numberOfTokens) takes time, and for that we need to use async/await functionality. We will also have to wait for the transaction to be mined with tx.wait() before we can read token balances.

Putting all these together, we get:

const ethers = require("ethers");      

const sender = '0xd25d2FBF90c6A810361448cDd4fF397A9bE5a703'
const receiver = '0x7f4e3F3178B884F6A9290e08a899306649F5bFf1'
const senderPk = 'sender-private-key-here'

const provider = new ethers.providers.InfuraProvider(
    "goerli",        
    "infura-api-key"
)
const signer = new ethers.Wallet(senderPk, provider)

const ERC20_ABI = [
    "function name() view returns (string)",
    "function symbol() view returns (string)",
    "function totalSupply() view returns (uint256)",
    "function balanceOf(address) view returns (uint)",
    "function transfer(address to, uint amount)",
    "event Transfer(address indexed from, address indexed to, uint amount)"
];

const address = '0x326C977E6efc84E512bB9C30f76E30c160eD06FB' // LINK 
const signer = new ethers.Wallet(senderPk, provider)
const contract = new ethers.Contract(address, ERC20_ABI, signer)
const token_amount = "1"

const main = async () => {
    const senderBalanceBefore = await contract.balanceOf(sender)
    const receiverBalanceBefore = await contract.balanceOf(receiver)

    console.log("Sender Balance",ethers.utils.formatEther(senderBalanceBefore))
    console.log("Receiver Balance",ethers.utils.formatEther(receiverBalanceBefore))

    const numberOfTokens = ethers.utils.parseUnits(token_amount, 18)
    console.log("number of tokens to be transferred", ethers.utils.formatEther(numberOfTokens))

    const tx = await contract.transfer(receiver, numberOfTokens)
    await tx.wait()
    console.log(tx)

    const senderBalanceAfter = await contract.balanceOf(sender)
    const recieverBalanceAfter = await contract.balanceOf(receiver)

    console.log("-----------After Transaction ------------")

    console.log("Sender Balance",ethers.utils.formatEther(senderBalanceAfter))
    console.log("Receiver Balance",ethers.utils.formatEther(recieverBalanceAfter))
}

main()

So, as usual we wrote a the asynchronous parts of the code inside async main() so that we can use await freely.

We also check for balances before and after the transfer of tokens. We do discuss more on it because we already covered that in one of our earlier tutorials.

Running this, we get something like this:

Note: The numbers will be different based on the tokens on yours accounts. But you can clearly see that the receiver gained 1 token.

If you see this, you have mastered yet one more skill!

So, in this tutorial, you have learned:

  1. How to connect to ERC20 smart contract
  2. How the decimals in ERC20 work
  3. How to transfer tokens
  4. How to wait for a transaction to be mined in ethers