Smart contract interaction utilities providing type-safe interfaces for calling contract methods, handling events, and managing contract deployments with automatic ABI encoding/decoding.
Primary class for interacting with deployed smart contracts through type-safe method calls and event handling.
/**
* Primary interface for smart contract interaction with automatic ABI encoding/decoding
*/
class Contract extends BaseContract {
constructor(target: string, abi: Interface | InterfaceAbi, runner?: ContractRunner);
readonly target: string;
readonly interface: Interface;
readonly runner?: ContractRunner;
readonly filters: Record<string, (...args: any[]) => DeferredTopicFilter>;
// Connection management
connect(runner: ContractRunner): Contract;
attach(target: string): Contract;
// Address resolution
getAddress(): Promise<string>;
getDeployedCode(): Promise<null | string>;
// Event querying
queryFilter(event: ContractEventName, fromBlock?: BlockTag, toBlock?: BlockTag): Promise<Array<EventLog | Log>>;
// Event listening
on(event: ContractEventName, listener: Listener): Promise<Contract>;
off(event: ContractEventName, listener?: Listener): Promise<Contract>;
once(event: ContractEventName, listener: Listener): Promise<Contract>;
removeAllListeners(event?: ContractEventName): Promise<Contract>;
// Dynamic method access
[key: string]: ContractMethod | any;
}
/**
* Base contract class providing core functionality
*/
abstract class BaseContract {
readonly target: string;
readonly interface: Interface;
readonly runner?: ContractRunner;
constructor(target: string, abi: Interface | InterfaceAbi, runner?: ContractRunner);
attach(target: string): BaseContract;
connect(runner: ContractRunner): BaseContract;
getAddress(): Promise<string>;
getDeployedCode(): Promise<null | string>;
}
type ContractRunner = {
provider?: Provider;
call?: (transaction: TransactionRequest) => Promise<string>;
estimateGas?: (transaction: TransactionRequest) => Promise<bigint>;
sendTransaction?: (transaction: TransactionRequest) => Promise<TransactionResponse>;
resolveName?: (name: string) => Promise<null | string>;
};Usage Examples:
import { Contract, JsonRpcProvider } from "ethers";
const provider = new JsonRpcProvider("YOUR-RPC-URL");
// ERC-20 contract example
const erc20Abi = [
"function name() view returns (string)",
"function symbol() view returns (string)",
"function decimals() view returns (uint8)",
"function totalSupply() view returns (uint256)",
"function balanceOf(address) view returns (uint256)",
"function transfer(address, uint256) returns (bool)",
"event Transfer(address indexed from, address indexed to, uint256 value)"
];
const contract = new Contract("0x...", erc20Abi, provider);
// Call view functions
const name = await contract.name();
const balance = await contract.balanceOf("0x...");
// With signer for transactions
const contractWithSigner = contract.connect(signer);
const tx = await contractWithSigner.transfer("0x...", ethers.parseEther("100"));Deploy new contract instances from bytecode with constructor parameter encoding.
/**
* Factory for deploying new contract instances from bytecode
*/
class ContractFactory {
constructor(abi: Interface | InterfaceAbi, bytecode: BytesLike, runner?: ContractRunner);
readonly interface: Interface;
readonly bytecode: string;
readonly runner?: ContractRunner;
// Deployment
deploy(...args: Array<any>): Promise<BaseContract>;
getDeployTransaction(...args: Array<any>): Promise<ContractDeployTransaction>;
// Connection management
connect(runner: ContractRunner): ContractFactory;
// Static utilities
static fromSolidity(output: any, runner?: ContractRunner): ContractFactory;
}
interface ContractDeployTransaction extends TransactionRequest {
data: string;
}Usage Example:
import { ContractFactory } from "ethers";
const abi = [...]; // Contract ABI
const bytecode = "0x..."; // Contract bytecode
const factory = new ContractFactory(abi, bytecode, signer);
// Deploy contract
const contract = await factory.deploy(constructorArg1, constructorArg2);
await contract.waitForDeployment();
console.log("Contract deployed at:", await contract.getAddress());Event handling classes for parsing and managing contract event logs.
/**
* Contract event payload with decoded data
*/
class ContractEventPayload extends EventPayload<ContractEventName> {
readonly contract: BaseContract;
readonly log: EventLog;
constructor(contract: BaseContract, listener: null | Listener, filter: ContractEventName, log: EventLog);
// Event data access
getBlock(): Promise<Block>;
getTransaction(): Promise<TransactionResponse>;
getTransactionReceipt(): Promise<TransactionReceipt>;
}
/**
* Contract event payload for unknown/undecodable events
*/
class ContractUnknownEventPayload extends EventPayload<ContractEventName> {
readonly contract: BaseContract;
readonly log: Log;
constructor(contract: BaseContract, listener: null | Listener, filter: ContractEventName, log: Log);
}
/**
* Decoded event log with parsed arguments
*/
class EventLog extends Log {
readonly interface: Interface;
readonly fragment: EventFragment;
readonly args: Result;
constructor(log: Log, iface: Interface, fragment: EventFragment);
}
/**
* Event log that could not be decoded
*/
class UndecodedEventLog extends Log {
readonly error: Error;
constructor(log: Log, error: Error);
}Type definitions for contract method calls and transaction handling.
/**
* Base contract method interface
*/
interface BaseContractMethod<A extends Array<any> = Array<any>, R = any, D extends R | ContractTransactionResponse = ContractTransactionResponse> {
(...args: ContractMethodArgs<A>): Promise<D>;
name: string;
fragment: FunctionFragment;
// Method variants
populateTransaction(...args: ContractMethodArgs<A>): Promise<ContractTransaction>;
staticCall(...args: ContractMethodArgs<A>): Promise<R>;
send(...args: ContractMethodArgs<A>): Promise<ContractTransactionResponse>;
estimateGas(...args: ContractMethodArgs<A>): Promise<bigint>;
}
/**
* View/Pure contract method (read-only)
*/
interface ConstantContractMethod<A extends Array<any> = Array<any>, R = any> extends BaseContractMethod<A, R, R> {
(...args: ContractMethodArgs<A>): Promise<R>;
}
/**
* State-changing contract method
*/
interface ContractMethod<A extends Array<any> = Array<any>, R = any, D extends R | ContractTransactionResponse = ContractTransactionResponse> extends BaseContractMethod<A, R, D> {
(...args: ContractMethodArgs<A>): Promise<D>;
}
type ContractMethodArgs<A extends Array<any>> = {
[I in keyof A]-?: A[I] | Typed;
} & A;
interface ContractTransaction extends TransactionRequest {
data: string;
to: string;
}Enhanced transaction response with contract-specific functionality.
/**
* Transaction response from contract method calls
*/
class ContractTransactionResponse extends TransactionResponse {
readonly interface?: Interface;
constructor(iface: Interface, provider: Provider, tx: TransactionResponse);
// Contract-specific waiting
wait(confirmations?: number): Promise<null | ContractTransactionReceipt>;
}
/**
* Transaction receipt from contract transactions with event parsing
*/
class ContractTransactionReceipt extends TransactionReceipt {
readonly interface?: Interface;
constructor(iface: Interface, provider: Provider, tx: TransactionReceipt);
// Event parsing
readonly logs: Array<EventLog | UndecodedEventLog>;
}Create and manage event filters for contract event monitoring.
/**
* Deferred topic filter for contract events
*/
interface DeferredTopicFilter {
getTopicFilter(): Promise<TopicFilter>;
fragment: EventFragment;
}
/**
* Contract event types
*/
type ContractEventName = string | ContractEvent<any>;
interface ContractEvent<InputTuple extends ReadonlyArray<any> = ReadonlyArray<any>> {
(...args: Partial<InputTuple>): DeferredTopicFilter;
fragment: EventFragment;
getFragment(): EventFragment;
}
type ContractEventArgs<T extends ReadonlyArray<any>> = {
[K in keyof T]: T[K] | Typed | (T[K] extends ReadonlyArray<any> ? T[K] | Typed : never);
};Usage Examples:
import { Contract } from "ethers";
const contract = new Contract(address, abi, provider);
// Listen to Transfer events
contract.on("Transfer", (from, to, value, event) => {
console.log(`Transfer: ${from} -> ${to}, ${ethers.formatEther(value)} tokens`);
console.log("Transaction hash:", event.log.transactionHash);
});
// Query past events
const filter = contract.filters.Transfer("0x...", null); // From specific address
const events = await contract.queryFilter(filter, -10000); // Last 10000 blocks
// Listen with specific filters
const transferFilter = contract.filters.Transfer("0x123...", "0x456...");
contract.on(transferFilter, (from, to, value) => {
console.log("Specific transfer detected");
});Configure transaction parameters for contract method calls.
/**
* Transaction parameter overrides for contract methods
*/
interface Overrides {
gasLimit?: BigNumberish;
gasPrice?: BigNumberish;
maxFeePerGas?: BigNumberish;
maxPriorityFeePerGas?: BigNumberish;
nonce?: BigNumberish;
type?: number;
accessList?: AccessListish;
customData?: any;
value?: BigNumberish;
blockTag?: BlockTag;
enableCcipRead?: boolean;
// For legacy transactions
from?: string;
}
/**
* Overrides that can be used as the last parameter in method calls
*/
type PostfixOverrides = Partial<Omit<Overrides, 'data' | 'to'>>;Usage Example:
import { Contract, parseEther } from "ethers";
const contract = new Contract(address, abi, signer);
// Send transaction with overrides
const tx = await contract.transfer("0x...", parseEther("100"), {
gasLimit: 100000,
gasPrice: parseUnits("20", "gwei"),
value: parseEther("0.1") // Send ETH along with the call
});type ContractInterface = string | ReadonlyArray<Fragment | JsonFragment | string> | Interface;
interface WrappedFallback {
(overrides?: Omit<TransactionRequest, "to">): Promise<TransactionResponse>;
populateTransaction(overrides?: Omit<TransactionRequest, "to">): Promise<TransactionRequest>;
staticCall(overrides?: Omit<TransactionRequest, "to">): Promise<string>;
send(overrides?: Omit<TransactionRequest, "to">): Promise<TransactionResponse>;
estimateGas(overrides?: Omit<TransactionRequest, "to">): Promise<bigint>;
}