Learn how to interact with smart contracts using Ethers.js.
Smart contracts are programs on the Ethereum blockchain. You interact with them by calling their functions. Ethers.js abstracts away the complexity of encoding function calls and decoding return values.
To interact with a contract, you need the contract address and its ABI (Application Binary Interface).
import { ethers } from 'ethers';
// Contract ABI (defines function signatures)
const ABI = [
"function name() public view returns (string)",
"function symbol() public view returns (string)",
"function balanceOf(address) public view returns (uint256)",
"function transfer(address to, uint256 amount) public returns (bool)"
];
// USDC token on mainnet
const contractAddress = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
// Create read-only contract instance (with provider)
const provider = new ethers.JsonRpcProvider('https://eth.llamarpc.com');
const contract = new ethers.Contract(
contractAddress,
ABI,
provider
);
// Get token name
const name = await contract.name();
console.log('Token name:', name); // "USD Coin"
// Get user balance
const balance = await contract.balanceOf('0x...');
console.log('Balance:', ethers.formatUnits(balance, 6)); // USDC has 6 decimalsView functions don't change state and don't require transactions. They're free to call.
import { ethers } from 'ethers';
const ABI = [
"function balanceOf(address account) public view returns (uint256)",
"function totalSupply() public view returns (uint256)",
"function allowance(address owner, address spender) public view returns (uint256)"
];
const contract = new ethers.Contract(
'0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
ABI,
provider
);
// Read-only calls don't need a signer
const totalSupply = await contract.totalSupply();
console.log('Total supply:', ethers.formatUnits(totalSupply, 6));
const balance = await contract.balanceOf('0x...');
const allowance = await contract.allowance('0x...', '0x...');Functions that modify state require a signer and cost gas.
import { ethers } from 'ethers';
const ABI = [
"function transfer(address to, uint256 amount) public returns (bool)",
"function approve(address spender, uint256 amount) public returns (bool)"
];
// Connect with signer to call write functions
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const contract = new ethers.Contract(
'0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
ABI,
signer // Pass signer to enable writes
);
// Transfer tokens
try {
const tx = await contract.transfer(
'0x742d35Cc6634C0532925a3b844Bc9e7595f42171',
ethers.parseUnits('100', 6) // 100 USDC
);
console.log('Transaction sent:', tx.hash);
// Wait for transaction
const receipt = await tx.wait();
console.log('Transaction mined!');
} catch (error) {
console.error('Transfer failed:', error);
}Contracts emit events when certain actions happen. You can listen for these events.
import { ethers } from 'ethers';
const ABI = [
"event Transfer(address indexed from, address indexed to, uint256 value)",
"function transfer(address to, uint256 amount) public returns (bool)"
];
const contract = new ethers.Contract(
'0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
ABI,
provider
);
// Listen for Transfer events
contract.on('Transfer', (from, to, value, event) => {
console.log(`Transfer from ${from} to ${to}`);
console.log('Amount:', ethers.formatUnits(value, 6));
});
// Listen for events from past blocks
const pastEvents = await contract.queryFilter(
contract.filters.Transfer(userAddress, null),
-1000 // Last 1000 blocks
);
pastEvents.forEach(event => {
console.log(event.args);
});
// Remove listeners
contract.removeAllListeners('Transfer');import { ethers } from 'ethers';
async function approveAndSwap() {
// Setup
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const usdcAddress = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
const swapRouterAddress = '0xE592427A0AEce92De3Edee1F18E0157C05861564';
const ABI = [
"function approve(address spender, uint256 amount) public returns (bool)",
"function balanceOf(address account) public view returns (uint256)"
];
const usdc = new ethers.Contract(usdcAddress, ABI, signer);
// 1. Check balance
const balance = await usdc.balanceOf(await signer.getAddress());
console.log('Balance:', ethers.formatUnits(balance, 6), 'USDC');
// 2. Approve router to spend USDC
const approveTx = await usdc.approve(
swapRouterAddress,
ethers.parseUnits('1000', 6) // Approve 1000 USDC
);
console.log('Approval sent:', approveTx.hash);
// 3. Wait for approval
await approveTx.wait();
console.log('Approval confirmed!');
// Now ready to swap...
}