Web3 module to interact with Ethereum smart contracts with TypeScript type safety
—
Event subscription, filtering, and log processing with typed event data based on contract ABI definitions.
Subscribe to contract events using the dynamically generated events property.
class Contract<Abi extends ContractAbi> {
/** Dynamically generated events based on contract ABI */
readonly events: ContractEventsInterface<Abi>;
/**
* Gets past events for this contract from blockchain history
* @param eventName - The event name or 'allEvents' to get all events
* @param filter - Filter options with fromBlock, toBlock, and indexed parameter filters
* @returns Array of past event objects matching the criteria
*/
getPastEvents(eventName?: keyof ContractEvents<Abi> | 'allEvents'): Promise<EventLog[]>;
getPastEvents(filter: Omit<Filter, 'address'>): Promise<EventLog[]>;
getPastEvents(
eventName: keyof ContractEvents<Abi> | 'allEvents',
filter: Omit<Filter, 'address'>
): Promise<EventLog[]>;
}
type ContractEventsInterface<Abi extends ContractAbi> = {
[Name in keyof ContractEvents<Abi> | 'allEvents']: ContractBoundEvent;
} & {
/** Allow access by event signature */
[key: string]: ContractBoundEvent;
};
/**
* Event subscription function
* @param options - Event filtering and subscription options
* @returns ContractLogsSubscription for managing the subscription
*/
type ContractBoundEvent = (options?: ContractEventOptions) => ContractLogsSubscription;Options for filtering and configuring event subscriptions.
interface ContractEventOptions {
/**
* Filter events by indexed parameters
* Example: { filter: { myNumber: [12, 13] } } means all events where myNumber is 12 or 13
*/
filter?: Record<string, unknown>;
/**
* The block number from which to get events on
* Can use 'earliest', 'latest', 'pending', 'safe', 'finalized' or specific block number
*/
fromBlock?: BlockNumberOrTag;
/**
* Manually set the topics for the event filter
* If given, the filter property and event signature (topic[0]) will not be set automatically
*/
topics?: string[];
}Subscription class for managing event subscriptions with Web3 subscription interface.
/**
* ContractLogsSubscription for contract event logs
*
* Supported events:
* - connected: Emitted when subscription is connected
* - data: Fires on each incoming event with event object
* - changed: Fires on each event removed from blockchain (has 'removed: true')
* - error: Fires on each error
*/
class ContractLogsSubscription extends Web3Subscription<
EventLog,
EthExecutionAPI
> {
constructor(
args: {
address?: Address;
topics?: string[];
abi?: ContractAbiWithSignature;
jsonInterface?: ContractAbiWithSignature;
},
options: {
subscriptionManager: Web3SubscriptionManager;
returnFormat?: DataFormat;
}
);
}
// Deprecated alias (use ContractLogsSubscription instead)
const LogsSubscription = ContractLogsSubscription;Structure of event logs returned by subscriptions and past event queries.
interface EventLog {
/** Event name */
event?: string;
/** Event signature hash */
signature?: HexString;
/** Contract address that emitted the event */
address: string;
/** Indexed and non-indexed event parameters */
returnValues: Record<string, unknown>;
/** Array of log topics */
topics: string[];
/** Raw log data */
data: string;
/** Block number where event was emitted */
blockNumber: number;
/** Block hash where event was emitted */
blockHash: string;
/** Transaction hash that triggered the event */
transactionHash: string;
/** Transaction index in the block */
transactionIndex: number;
/** Log index in the block */
logIndex: number;
/** True if event was removed from blockchain */
removed?: boolean;
}import { Contract } from "web3-eth-contract";
const abi = [
{
anonymous: false,
inputs: [
{ indexed: true, name: "from", type: "address" },
{ indexed: true, name: "to", type: "address" },
{ indexed: false, name: "value", type: "uint256" }
],
name: "Transfer",
type: "event"
},
{
anonymous: false,
inputs: [
{ indexed: false, name: "newValue", type: "uint256" }
],
name: "ValueChanged",
type: "event"
}
] as const;
const contract = new Contract(abi, contractAddress);
// Subscribe to specific event
const subscription = await contract.events.Transfer();
subscription.on('connected', (subscriptionId) => {
console.log('Subscription connected:', subscriptionId);
});
subscription.on('data', (event) => {
console.log('Transfer event:', {
from: event.returnValues.from,
to: event.returnValues.to,
value: event.returnValues.value,
blockNumber: event.blockNumber,
transactionHash: event.transactionHash
});
});
subscription.on('changed', (event) => {
console.log('Event removed (chain reorganization):', event);
});
subscription.on('error', (error) => {
console.error('Subscription error:', error);
});// Filter events by indexed parameters
const filteredSubscription = await contract.events.Transfer({
filter: {
from: "0x1234567890123456789012345678901234567890",
to: ["0xabcd...", "0xefgh..."] // Multiple values act as OR
},
fromBlock: 'latest'
});
filteredSubscription.on('data', (event) => {
console.log('Filtered transfer:', event.returnValues);
});
// Filter with specific block range
const historicalSubscription = await contract.events.ValueChanged({
fromBlock: 1000000,
// toBlock: 2000000 // Note: subscriptions don't support toBlock
});// Subscribe to all contract events
const allEventsSubscription = await contract.events.allEvents({
fromBlock: 'latest'
});
allEventsSubscription.on('data', (event) => {
console.log('Event received:', {
eventName: event.event,
data: event.returnValues,
block: event.blockNumber
});
// Handle different event types
switch (event.event) {
case 'Transfer':
console.log('Transfer:', event.returnValues.from, '->', event.returnValues.to);
break;
case 'ValueChanged':
console.log('Value changed to:', event.returnValues.newValue);
break;
default:
console.log('Unknown event:', event.event);
}
});// Advanced filtering with manual topics
const manualSubscription = await contract.events.Transfer({
topics: [
null, // Event signature (automatically filled)
"0x1234567890123456789012345678901234567890", // from address
null // to address (any)
]
});// Store subscription reference
let transferSubscription: ContractLogsSubscription;
async function startEventListening() {
transferSubscription = await contract.events.Transfer();
transferSubscription.on('data', handleTransferEvent);
transferSubscription.on('error', handleError);
}
async function stopEventListening() {
if (transferSubscription) {
await transferSubscription.unsubscribe();
console.log('Unsubscribed from Transfer events');
}
}
function handleTransferEvent(event: EventLog) {
console.log('Transfer event received:', event);
}
function handleError(error: Error) {
console.error('Event subscription error:', error);
}
// Clean up on application exit
process.on('SIGINT', async () => {
await stopEventListening();
process.exit();
});// Get historical events (not part of subscription)
// Note: This uses contract.getPastEvents() method
const pastEvents = await contract.getPastEvents('Transfer', {
filter: {
from: "0x1234567890123456789012345678901234567890"
},
fromBlock: 1000000,
toBlock: 2000000
});
console.log(`Found ${pastEvents.length} past Transfer events`);
pastEvents.forEach((event, index) => {
console.log(`Event ${index + 1}:`, {
from: event.returnValues.from,
to: event.returnValues.to,
value: event.returnValues.value,
block: event.blockNumber
});
});async function subscribeWithErrorHandling() {
try {
const subscription = await contract.events.Transfer({
fromBlock: 'latest'
});
subscription.on('data', (event) => {
try {
processEvent(event);
} catch (error) {
console.error('Error processing event:', error);
}
});
subscription.on('error', (error) => {
console.error('Subscription error:', error);
// Attempt to resubscribe after delay
setTimeout(async () => {
try {
await subscribeWithErrorHandling();
} catch (reconnectError) {
console.error('Failed to reconnect:', reconnectError);
}
}, 5000);
});
} catch (error) {
console.error('Failed to create subscription:', error);
}
}
function processEvent(event: EventLog) {
// Your event processing logic
console.log('Processing event:', event.event);
}// Subscribe to multiple specific events
const events = ['Transfer', 'Approval', 'ValueChanged'] as const;
const subscriptions = await Promise.all(
events.map(eventName => contract.events[eventName]({
fromBlock: 'latest'
}))
);
subscriptions.forEach((subscription, index) => {
subscription.on('data', (event) => {
console.log(`${events[index]} event:`, event.returnValues);
});
});
// Cleanup all subscriptions
async function cleanup() {
await Promise.all(
subscriptions.map(sub => sub.unsubscribe())
);
}// Special constants for event handling (re-exported from web3-eth)
const ALL_EVENTS: string;
const ALL_EVENTS_ABI: AbiEventFragment;Install with Tessl CLI
npx tessl i tessl/npm-web3-eth-contract