Skip to main content

Rust ethers-rs: How to send ERC-20 tokens

In this tutorial we'll send ERC-20 tokens from one address to another, a process which essentially consists of calling the transfer method of that token contract.

If you're new to Rust, make sure to check the previous article on how to install and set up the project.

Before jumping into the code:

  1. Get some test ERC-20 tokens - we'll be using some test LINK from https://faucets.chain.link/ on Ethereum's Sepolia testnet.
  2. Create a ct.json file in your project and paste the ABI from ChainLink Token (LINK) Token Tracker on Sepoliascan.

Find the full code below:

use ethers::{
middleware::SignerMiddleware,
providers::{Http, Middleware, Provider},
signers::{LocalWallet, Signer},
types::{Address, U256},
contract::abigen
};
use eyre::Result;
use std::convert::TryFrom;
use std::sync::Arc;
use serde_json::Value;

#[tokio::main]
async fn main() -> Result<()> {

// connect to the network, don't forget to replace your INFURA_API_KEY
let provider = Provider::<Http>::try_from("https://sepolia.infura.io/v3/INFURA_API_KEY")?;

let chain_id = provider.get_chainid().await?;

let contract_address = "0x779877A7B0D9E8603169DdbD7836e478b4624789".parse::<Address>()?;

// define a `ERC20Contract` struct from the ABI
abigen!(ERC20Contract, "./ct.json",);

let to_address = "0xF1B792820b52e6503208CAb98ec0B7b89ac64D6A".parse::<Address>()?;

// Create the contract instance to let us call methods of the contract and let it sign transactions with the sender wallet.
// It's always recommended to load it from an .env file or external vault
let wallet: LocalWallet = "SIGNER_PRIVATE_KEY"
.parse::<LocalWallet>()?
.with_chain_id(chain_id.as_u64());

let signer = Arc::new(SignerMiddleware::new(provider, wallet.with_chain_id(chain_id.as_u64())));
let contract = ERC20Contract::new(contract_address, signer);

// Fetch the decimals used by the contract so we can compute the decimal amount to send.
let whole_amount: u64 = 1;
let decimals = contract.decimals().call().await?;
let decimal_amount = U256::from(whole_amount) * U256::exp10(decimals as usize);

// Transfer the desired amount of tokens to the `to_address`
let tx = contract.transfer(to_address, decimal_amount);
let pending_tx = tx.send().await?;
let _mined_tx = pending_tx.await?;

println!("Transaction Receipt: {}", serde_json::to_string(&_mined_tx)?);

// Extract the tx hash for printing
let json_str = serde_json::to_string(&_mined_tx)?;
let json: Value = serde_json::from_str(&json_str)?;

if let Some(transaction_hash) = json["transactionHash"].as_str() {
println!("\n URL: https://sepolia.etherscan.io/tx/{}", transaction_hash);
} else {
println!("Transaction Hash not found");
}

Ok(())
}
Was this helpful?
Connect MetaMask to provide feedback
What is this?
This is a trial feedback system that uses Verax to record your feedback as onchain attestations on Linea Mainnet. When you vote, submit a transaction in your wallet.