How to send ERC-20 tokens using Go
In this support guide we're going to learn how to send ERC-20 tokens using Go and the go-ethereum packages. As usual, if you only need the script's code, feel free to scroll down to the end of the article.
Prerequisites
For this tutorial to work, you will need:
- Go installed on your system
- The go-ethereumpackages (they will be automatically installed when you run the code)
- dotenv(optional, but recommended for better security)
Setting up our project
Let's start by importing the required libraries and creating our .env file, which will contain our account's private key (you can use a test wallet without any real funds on it) and the Infura project ID.
If you need a little more help on creating a .env file, you can check out this repository.
For this tutorial's purposes you don't have to use environmental variables, but it's a good practice to use them so that sensitive information is not easily accessible within the code.
package main
import (
	"context"
	"fmt"
	"log"
	"math/big"
	"os"
	"github.com/ethereum/go-ethereum/accounts/abi/bind"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/ethclient"
)
func main() {
	// Connect to an Ethereum node using Infura
	client, err := ethclient.Dial(fmt.Sprintf("https://goerli.infura.io/v3/%s", os.Getenv("INFURA_API_KEY")))
	if err != nil {
		log.Fatal(err)
	}
Sending ERC-20 tokens
For this tutorial, let's try and send some UNI tokens. Sending ERC-20 tokens differs from sending ETH in that our script will need the contract's ABI. The ABI can be easily found on Etherscan by searching for the contract's address and going to the "Contract" section.
We can navigate to the token's Sepoliascan page, under the "Contract" tab. Then, we can create an abi.json file in our project's directory and store the copied ABI there.
// Load the ABI for the ERC-20 token contract
	tokenAddress := common.HexToAddress("0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984")
	contract, err := bind.NewBoundContract(tokenAddress, abi, client, client, client)
	if err != nil {
		log.Fatal(err)
	}
After fetching the ABI from the local abi.json file, we can create our contract object and pass all these parameters to the send_ERC20 function.
// Create a new transaction
	nonce, err := client.PendingNonceAt(context.Background(), common.HexToAddress("<insert_here_the_signer_address>"))
	if err != nil {
		log.Fatal(err)
	}
	gasPrice, err := client.SuggestGasPrice(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	auth := bind.NewKeyedTransactor(<insert_here_the_signer_private_key>)
	auth.Nonce = big.NewInt(int64(nonce))
	auth.Value = big.NewInt(0)      // in wei
	auth.GasLimit = uint64(300000) // in units
	auth.GasPrice = gasPrice
	toAddress := common.HexToAddress("<insert_here_the_token_destination_address>")
	amount := big.NewInt(1000000000000000000) // in wei
	tx, err := contract.Transfer(auth, toAddress, amount)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Tx Hash: %s", tx.Hash().Hex())
And that's it! Make sure to declare your "from" and "to" addresses in the main function and you should be good to go.
Complete code overview
package main
import (
	"context"
	"fmt"
	"log"
	"math/big"
	"os"
	"github.com/ethereum/go-ethereum/accounts/abi/bind"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/ethclient"
)
func main() {
	// Connect to an Ethereum node using Infura
	client, err := ethclient.Dial(fmt.Sprintf("https://goerli.infura.io/v3/%s", os.Getenv("INFURA_API_KEY")))
	if err != nil {
		log.Fatal(err)
	}
	// Load the ABI for the ERC-20 token contract
	tokenAddress := common.HexToAddress("0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984")
	contract, err := bind.NewBoundContract(tokenAddress, abi, client, client, client)
	if err != nil {
		log.Fatal(err)
	}
	// Create a new transaction
	nonce, err := client.PendingNonceAt(context.Background(), common.HexToAddress("<insert_here_the_signer_address>"))
	if err != nil {
		log.Fatal(err)
	}
	gasPrice, err := client.SuggestGasPrice(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	auth := bind.NewKeyedTransactor(<insert_here_the_signer_private_key>)
	auth.Nonce = big.NewInt(int64(nonce))
	auth.Value = big.NewInt(0)      // in wei
	auth.GasLimit = uint64(300000) // in units
	auth.GasPrice = gasPrice
	toAddress := common.HexToAddress("<insert_here_the_token_destination_address>")
	amount := big.NewInt(1000000000000000000) // in wei
	tx, err := contract.Transfer(auth, toAddress, amount)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("Tx Hash: %s", tx.Hash().Hex())
}