TypeChain target for generating TypeScript bindings compatible with ethers-v5
—
This guide shows how to use the TypeScript contract bindings generated by @typechain/ethers-v5 with ethers.js v5.
The target generates several types of files for each contract:
ContractName.ts): Type-safe contract interface with all methods and eventsContractName__factory.ts): Factory for deploying or connecting to contractscommon.ts): Shared utility types used across all contractsindex.ts): Convenient re-exports of all generated typesimport { ethers } from 'ethers';
import { MyToken, MyToken__factory } from './types/ethers-contracts';
const provider = new ethers.providers.JsonRpcProvider();
const signer = provider.getSigner();
// Deploy with type-safe constructor arguments
const factory = new MyToken__factory(signer);
const contract: MyToken = await factory.deploy(
"My Token", // name parameter
"MTK", // symbol parameter
18 // decimals parameter
);
await contract.deployed();
console.log(`Contract deployed at: ${contract.address}`);import { SimpleStorage, SimpleStorage__factory } from './types/ethers-contracts';
const factory = new SimpleStorage__factory(signer);
const contract: SimpleStorage = await factory.deploy();import { MyToken, MyToken__factory } from './types/ethers-contracts';
const contractAddress = "0x1234...";
// Connect using factory (recommended)
const contract: MyToken = MyToken__factory.connect(contractAddress, signer);
// Or connect using instance method
const factory = new MyToken__factory();
const contract2: MyToken = factory.attach(contractAddress).connect(signer);import { ethers } from 'ethers';
import { MyToken, MyToken__factory } from './types/ethers-contracts';
// Create contract instance with interface
const contract = new ethers.Contract(
contractAddress,
MyToken__factory.abi,
signer
) as MyToken;// Type-safe method calls with return type inference
const totalSupply: BigNumber = await contract.totalSupply();
const balance: BigNumber = await contract.balanceOf(userAddress);
const name: string = await contract.name();
// Function overloads are supported
const allowance1: BigNumber = await contract.allowance(owner, spender);
const allowance2: BigNumber = await contract["allowance(address,address)"](owner, spender);// Methods automatically include overrides parameter
const tx: ContractTransaction = await contract.transfer(
recipientAddress,
ethers.utils.parseEther("100")
);
await tx.wait(); // Wait for confirmation
// With transaction overrides
const txWithOverrides: ContractTransaction = await contract.transfer(
recipientAddress,
ethers.utils.parseEther("100"),
{
gasLimit: 100000,
gasPrice: ethers.utils.parseUnits("20", "gwei")
}
);// Payable functions accept PayableOverrides
const tx: ContractTransaction = await contract.buyTokens(amount, {
value: ethers.utils.parseEther("1.0"), // Send ETH with transaction
gasLimit: 200000
});// Create type-safe event filters
const transferFilter = contract.filters.Transfer(null, userAddress, null);
const allTransfersFilter = contract.filters.Transfer();
// Query past events
const events = await contract.queryFilter(transferFilter, -10000); // Last 10k blocks
// Typed event objects
events.forEach(event => {
console.log(`Transfer: ${event.args.from} -> ${event.args.to}: ${event.args.value}`);
});// Listen for events with type safety
contract.on("Transfer", (from: string, to: string, value: BigNumber, event) => {
console.log(`Transfer from ${from} to ${to}: ${ethers.utils.formatEther(value)} tokens`);
});
// Listen for specific event instances
contract.on(transferFilter, (event) => {
// Event is fully typed based on filter
console.log("User received tokens:", event.args.value);
});All contract methods are available as static calls for gas estimation and simulation:
// Estimate gas for transaction
const estimatedGas: BigNumber = await contract.estimateGas.transfer(
recipientAddress,
ethers.utils.parseEther("100")
);
// Simulate transaction without sending
const result = await contract.callStatic.complexCalculation(inputValue);
// Get populated transaction data
const populatedTx = await contract.populateTransaction.transfer(
recipientAddress,
ethers.utils.parseEther("100")
);TypeChain generates typed errors for custom contract errors:
try {
await contract.restrictedFunction();
} catch (error) {
// Handle specific contract errors
if (error.errorName === "Unauthorized") {
console.log("User not authorized for this action");
}
}For contracts that use external libraries:
import { MyLibraryContract__factory } from './types/ethers-contracts';
// Deploy with library addresses
const factory = new MyLibraryContract__factory({
"MyLibrary": libraryAddress,
"AnotherLibrary": anotherLibraryAddress
}, signer);
const contract = await factory.deploy();The generated types work seamlessly with TypeScript:
// Inferred return types
const balance = await contract.balanceOf(userAddress); // Type: BigNumber
const isApproved = await contract.approved(tokenId); // Type: boolean
// Type-safe function parameters
await contract.transfer(
"0x1234...", // address
ethers.utils.parseEther("100") // uint256
);
// Compile-time error for wrong parameter types
await contract.transfer(
123, // ❌ Error: Expected string, got number
"invalid" // ❌ Error: Expected BigNumberish, got string
);Install with Tessl CLI
npx tessl i tessl/npm-typechain--ethers-v5