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:
- Get some test ERC-20 tokens - we'll be using some test LINK from https://faucets.chain.link/ on Ethereum's Sepolia testnet.
- Create a
ct.jsonfile 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(())
}