Web3 module to interact with Ethereum smart contracts with TypeScript type safety
—
Contract method calls for both read-only operations and state-changing transactions with full TypeScript type safety based on contract ABI.
Different method object interfaces depending on the contract method's state mutability.
/**
* Interface for non-payable contract methods (view, pure, nonpayable)
*/
interface NonPayableMethodObject<Inputs = unknown[], Outputs = unknown[]> {
/** The arguments passed to this method */
arguments: Inputs;
/**
* Call a method and execute its smart contract method in the EVM without sending any transaction
* Note: calling cannot alter the smart contract state
* @param options - The options used for calling
* @returns Promise that resolves to the method return values
*/
call(options?: NonPayableCallOptions): Promise<Outputs>;
/**
* Estimate the gas required for this method
* @param options - The options used for gas estimation
* @returns Promise that resolves to estimated gas amount
*/
estimateGas(options?: NonPayableCallOptions): Promise<number>;
/**
* Create an access list for this method call
* @param options - The options used for access list creation
* @returns Promise that resolves to access list result
*/
createAccessList(options?: NonPayableCallOptions): Promise<AccessListResult>;
/**
* Encode the ABI for this method including function signature and parameters
* @returns The encoded ABI byte code to send via a transaction or call
*/
encodeABI(): string;
/**
* Decode raw result of method call into readable values
* @param data - The data to decode
* @returns The decoded data
*/
decodeData(data: HexString): Inputs;
/**
* Populate transaction object for this method call
* @param options - Transaction options
* @param contractOptions - Contract default options
* @returns Transaction call object
*/
populateTransaction(options?: NonPayableCallOptions, contractOptions?: ContractOptions): TransactionCall;
}
/**
* Interface for payable contract methods
*/
interface PayableMethodObject<Inputs = unknown[], Outputs = unknown[]>
extends NonPayableMethodObject<Inputs, Outputs> {
/**
* Send a transaction to execute this method and change contract state
* @param options - The options used for sending the transaction
* @returns PromiEvent that resolves to transaction receipt with events
*/
send(options?: PayableCallOptions): Web3PromiEvent<TransactionReceipt, SendTransactionEvents>;
}Access contract methods through the dynamically generated methods property.
class Contract<Abi extends ContractAbi> {
/** Dynamically generated methods based on contract ABI */
readonly methods: ContractMethodsInterface<Abi>;
}
type ContractMethodsInterface<Abi extends ContractAbi> = {
[MethodAbi in FilterAbis<Abi, AbiFunctionFragment & { type: 'function' }> as MethodAbi['name']]: ContractBoundMethod<MethodAbi>;
} & {
/** Allow access by method signature for overloaded methods */
[key: string]: ContractBoundMethod<any>;
};
type ContractBoundMethod<Abi extends AbiFunctionFragment> = (
...args: ContractMethodInputParameters<Abi['inputs']>
) => Abi['stateMutability'] extends 'payable' | 'pure'
? PayableMethodObject<ContractMethodInputParameters<Abi['inputs']>, ContractMethodOutputParameters<Abi['outputs']>>
: NonPayableMethodObject<ContractMethodInputParameters<Abi['inputs']>, ContractMethodOutputParameters<Abi['outputs']>>;Options for method calls and transactions.
// From web3-types
interface NonPayableCallOptions {
from?: Address;
gas?: Numbers;
gasPrice?: Numbers;
maxFeePerGas?: Numbers;
maxPriorityFeePerGas?: Numbers;
nonce?: Numbers;
block?: BlockNumberOrTag;
}
interface PayableCallOptions extends NonPayableCallOptions {
value?: Numbers;
}
type NonPayableTxOptions = NonPayableCallOptions;
type PayableTxOptions = PayableCallOptions;import { Contract } from "web3-eth-contract";
const abi = [
{
inputs: [],
name: "getValue",
outputs: [{ name: "", type: "uint256" }],
stateMutability: "view",
type: "function"
},
{
inputs: [{ name: "account", type: "address" }],
name: "balanceOf",
outputs: [{ name: "", type: "uint256" }],
stateMutability: "view",
type: "function"
}
] as const;
const contract = new Contract(abi, contractAddress);
// Simple call without options
const value = await contract.methods.getValue().call();
console.log("Current value:", value);
// Call with specific options
const balance = await contract.methods.balanceOf("0x1234...").call({
from: "0xabcd...",
block: "latest"
});
console.log("Balance:", balance);
// Multi-return method
const abi2 = [{
inputs: [],
name: "getInfo",
outputs: [
{ name: "name", type: "string" },
{ name: "value", type: "uint256" }
],
stateMutability: "view",
type: "function"
}] as const;
const contract2 = new Contract(abi2, contractAddress);
const result = await contract2.methods.getInfo().call();
// result.name and result.value are properly typed
console.log("Name:", result.name, "Value:", result.value);
// Also accessible by index: result[0], result[1]const abi = [
{
inputs: [{ name: "newValue", type: "uint256" }],
name: "setValue",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{ name: "to", type: "address" },
{ name: "amount", type: "uint256" }
],
name: "transfer",
outputs: [{ name: "", type: "bool" }],
stateMutability: "nonpayable",
type: "function"
}
] as const;
const contract = new Contract(abi, contractAddress);
// Simple transaction
const receipt = await contract.methods.setValue(42).send({
from: "0x1234567890123456789012345678901234567890",
gas: 100000,
gasPrice: "20000000000"
});
console.log("Transaction hash:", receipt.transactionHash);
console.log("Gas used:", receipt.gasUsed);
// Transaction with event monitoring
const promiEvent = contract.methods.transfer("0xabcd...", 1000).send({
from: "0x1234...",
gas: 50000
});
promiEvent
.on('transactionHash', (hash) => {
console.log("Transaction sent:", hash);
})
.on('receipt', (receipt) => {
console.log("Transaction confirmed:", receipt);
})
.on('error', (error) => {
console.error("Transaction failed:", error);
});
const receipt = await promiEvent;const abi = [{
inputs: [],
name: "deposit",
outputs: [],
stateMutability: "payable",
type: "function"
}] as const;
const contract = new Contract(abi, contractAddress);
// Send ether with the transaction
const receipt = await contract.methods.deposit().send({
from: "0x1234...",
value: web3.utils.toWei("1", "ether"), // Send 1 ETH
gas: 100000
});// Estimate gas before sending transaction
const gasEstimate = await contract.methods.setValue(42).estimateGas({
from: "0x1234567890123456789012345678901234567890"
});
console.log("Estimated gas:", gasEstimate);
// Use estimated gas with buffer
const receipt = await contract.methods.setValue(42).send({
from: "0x1234567890123456789012345678901234567890",
gas: Math.floor(gasEstimate * 1.2) // Add 20% buffer
});// Create access list for EIP-2930 transactions
const accessList = await contract.methods.setValue(42).createAccessList({
from: "0x1234567890123456789012345678901234567890"
});
console.log("Access list:", accessList.accessList);
console.log("Gas used:", accessList.gasUsed);
// Use access list in transaction
const receipt = await contract.methods.setValue(42).send({
from: "0x1234567890123456789012345678901234567890",
accessList: accessList.accessList,
type: 1 // EIP-2930 transaction type
});// For overloaded methods, access by signature
const abi = [
{
inputs: [{ name: "value", type: "uint256" }],
name: "set",
outputs: [],
stateMutability: "nonpayable",
type: "function"
},
{
inputs: [
{ name: "key", type: "string" },
{ name: "value", type: "uint256" }
],
name: "set",
outputs: [],
stateMutability: "nonpayable",
type: "function"
}
] as const;
const contract = new Contract(abi, contractAddress);
// Access by method signature for overloaded methods
await contract.methods["set(uint256)"](42).send({ from: account });
await contract.methods["set(string,uint256)"]("key", 42).send({ from: account });
// Or by index if there are naming conflicts
await contract.methods.set(42).send({ from: account }); // First definition// Transaction receipt type
interface TransactionReceipt {
transactionHash: string;
transactionIndex: number;
blockHash: string;
blockNumber: number;
from: string;
to: string;
gasUsed: number;
cumulativeGasUsed: number;
logs: EventLog[];
status: boolean;
// ... other receipt fields
}
// PromiEvent for transaction sending
type ContractMethodSend = Web3PromiEvent<
FormatType<TransactionReceipt, DataFormat>,
SendTransactionEvents<DataFormat>
>;
// Access list result
interface AccessListResult {
accessList: AccessList;
gasUsed: string;
}Install with Tessl CLI
npx tessl i tessl/npm-web3-eth-contract