How to Send ERC-20 Tokens Using Go Ethereum (EIP-1559)

·

Sending ERC-20 tokens on the Ethereum blockchain requires a different approach than sending native ETH. This guide walks you through the process step-by-step using the Go Ethereum (Geth) library and EIP-1559 transactions.

Prerequisites

Before you begin, ensure you have the following installed on your system:

To install these dependencies, run the appropriate commands for your operating system. For macOS users with Homebrew, you can use:

brew tap ethereum/ethereum
brew install ethereum

Project Setup

Start by creating a new Go project and importing the necessary libraries. You'll need to connect to an Ethereum node—we recommend using a service like Infura for reliable access.

Create a .env file to store your sensitive information securely:

INFURA_PROJECT_ID=your_infura_project_id_here
PRIVATE_KEY=your_wallet_private_key_here

Always use environment variables for sensitive data rather than hardcoding it into your application. This practice prevents accidental exposure of your private keys.

Initial Code Structure

Here's the basic structure to get started:

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 Ethereum node
  client, err := ethclient.Dial(fmt.Sprintf("https://goerli.infura.io/v3/%s", os.Getenv("INFURA_PROJECT_ID")))
  if err != nil {
    log.Fatal(err)
  }
}

Understanding ERC-20 Token Transfers

Unlike native ETH transfers, sending ERC-20 tokens requires interacting with smart contracts. Each token has its own contract address and Application Binary Interface (ABI), which defines how to interact with it.

The key difference is that instead of sending value directly to an address, you're calling a function on the token's contract that updates its internal accounting system.

Sending ERC-20 Tokens

Let's use UNI tokens as an example for this tutorial. To send these tokens, you'll need:

  1. The token contract address
  2. The contract's ABI
  3. Proper transaction parameters

Obtaining the Contract ABI

Find your token's contract address and locate its ABI on Etherscan:

  1. Search for the token contract on Etherscan
  2. Navigate to the "Contract" tab
  3. Copy the ABI JSON
  4. Save it as abi.json in your project directory

Token Transfer Implementation

With the ABI available, you can create a bound contract instance:

// Load token contract address
tokenAddress := common.HexToAddress("0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984")

// Create contract instance
contract, err := bind.NewBoundContract(tokenAddress, abi, client, client, client)
if err != nil {
  log.Fatal(err)
}

Building the Transaction

Next, prepare your transaction parameters following EIP-1559 standards:

// Get current nonce
nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
if err != nil {
  log.Fatal(err)
}

// Get suggested gas price
gasPrice, err := client.SuggestGasPrice(context.Background())
if err != nil {
  log.Fatal(err)
}

// Prepare transaction authentication
auth := bind.NewKeyedTransactor(privateKey)
auth.Nonce = big.NewInt(int64(nonce))
auth.Value = big.NewInt(0) // Zero for token transfers
auth.GasLimit = uint64(300000) // Adjust based on contract complexity
auth.GasPrice = gasPrice

// Set recipient and amount
toAddress := common.HexToAddress("RECIPIENT_ADDRESS_HERE")
amount := big.NewInt(1000000000000000000) // 1 token (adjust decimal places)

// Execute transfer
tx, err := contract.Transfer(auth, toAddress, amount)
if err != nil {
  log.Fatal(err)
}

fmt.Printf("Transaction Hash: %s", tx.Hash().Hex())

Complete Code Example

Here's the full implementation for sending ERC-20 tokens:

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 Ethereum node
  client, err := ethclient.Dial(fmt.Sprintf("https://goerli.infura.io/v3/%s", os.Getenv("INFURA_PROJECT_ID")))
  if err != nil {
    log.Fatal(err)
  }

  // Load token contract
  tokenAddress := common.HexToAddress("0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984")
  contract, err := bind.NewBoundContract(tokenAddress, abi, client, client, client)
  if err != nil {
    log.Fatal(err)
  }

  // Prepare transaction parameters
  nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
  if err != nil {
    log.Fatal(err)
  }

  gasPrice, err := client.SuggestGasPrice(context.Background())
  if err != nil {
    log.Fatal(err)
  }

  auth := bind.NewKeyedTransactor(privateKey)
  auth.Nonce = big.NewInt(int64(nonce))
  auth.Value = big.NewInt(0)
  auth.GasLimit = uint64(300000)
  auth.GasPrice = gasPrice

  toAddress := common.HexToAddress("RECIPIENT_ADDRESS_HERE")
  amount := big.NewInt(1000000000000000000)

  // Execute transfer
  tx, err := contract.Transfer(auth, toAddress, amount)
  if err != nil {
    log.Fatal(err)
  }

  fmt.Printf("Transaction Hash: %s", tx.Hash().Hex())
}

Best Practices for Token Transfers

When working with ERC-20 tokens, follow these security and efficiency guidelines:

Frequently Asked Questions

What is the difference between sending ETH and ERC-20 tokens?

Sending ETH involves a direct value transfer between addresses, while ERC-20 token transfers require interacting with a smart contract. The contract updates its internal balance mapping to reflect the transfer between addresses.

Why do I need a contract ABI for token transfers?

The Application Binary Interface (ABI) defines how to interact with a smart contract. It specifies the functions available, their parameters, and return types. Without the ABI, your code wouldn't know how to properly call the transfer function.

How can I estimate the correct gas limit for token transfers?

You can use the EstimateGas method provided by most Ethereum clients. Alternatively, check the token contract on Etherscan to see typical gas costs for transfers, then add a buffer for safety.

What are the security risks when sending ERC-20 tokens?

The main risks include exposing private keys, sending to wrong addresses, and contract vulnerabilities. Always use environment variables for sensitive data, double-check addresses, and research tokens before interacting with them.

Can I use this same approach for other Ethereum-compatible blockchains?

Yes, the same basic approach works for any EVM-compatible blockchain. You'll just need to connect to the appropriate RPC endpoint and ensure you're using the correct token contract address for that network.

What should I do if my token transfer fails?

First, check the transaction hash on a block explorer to see the error message. Common issues include insufficient gas, insufficient token balance, or token contract restrictions. Adjust your parameters accordingly and try again.

Remember to always verify your code and test transactions on testnets before deploying on mainnet. 👉 Get real-time tools for transaction monitoring