CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-web3-eth-contract

Web3 module to interact with Ethereum smart contracts with TypeScript type safety

Pending
Overview
Eval results
Files

event-handling.mddocs/

Event Handling

Event subscription, filtering, and log processing with typed event data based on contract ABI definitions.

Capabilities

Event Subscription

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;

Event Options

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[];
}

ContractLogsSubscription

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;

Event Log Structure

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;
}

Usage Examples

Basic Event Subscription

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);
});

Event Filtering

// 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
});

All Events Subscription

// 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);
  }
});

Manual Topic Filtering

// Advanced filtering with manual topics
const manualSubscription = await contract.events.Transfer({
  topics: [
    null, // Event signature (automatically filled)
    "0x1234567890123456789012345678901234567890", // from address
    null // to address (any)
  ]
});

Subscription Management

// 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();
});

Getting Past Events

// 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
  });
});

Error Handling

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);
}

Multiple Event Types

// 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())
  );
}

Constants

// 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

docs

contract-deployment.md

contract-management.md

encoding-utilities.md

event-handling.md

index.md

method-execution.md

tile.json