Contract events are a key feature of blockchain systems, enabling applications to react to specific state changes in an efficient manner. On peaq network, chain events can be emitted during contract execution and logged to the blockchain. These events can be captured and parsed to build responsive dApps, monitor activity, or trigger off-chain workflows.

This guide provides step-by-step instructions on how to listen to and parse smart contract events on the peaq network.

Prerequisites

  1. You have a working knowledge of Ethereum-based networks and EVM-compatible smart contracts.
  2. You have deployed a smart contract on the peaq network that emits events.
  3. You are using ethers.js or web3.js in a JavaScript or Node.js environment.
  4. You have access to a peaq network RPC endpoint (e.g., https://peaq.api.onfinality.io/public).
  5. You know the ABI of the contract you are monitoring and the address of the deployed contract.

Instructions

1. Set Up Your Project

Ensure your environment is set up with the necessary libraries and RPC configuration:

npm install ethers

2. Initialize Connection to the peaq Network

Connect to the peaq network using ethers.js:

import { ethers } from "ethers";
const provider = new ethers.JsonRpcProvider("https://peaq.api.onfinality.io/public");

3. Load the Smart Contract

To interact with the smart contract, load its address and ABI:

const contractAddress = "0xYourContractAddress"; // Replace with your smart contract's address
const contractABI = [
  // Add your contract's ABI here or import with require() instead
];

const contract = new ethers.Contract(contractAddress, contractABI, provider);

4. Listen for Events

Using ethers.js, you can set up listeners for specific events emitted by the contract:

contract.on("YourEventName", (param1, param2, event) => {
  console.log(`Event received:`);
  console.log(`Param 1: ${param1}`);
  console.log(`Param 2: ${param2}`);
  console.log(`Full Event:`, event);
});

5. Parse Events

Ethers.js provides the raw event log data, which can be parsed for additional details:

provider.on("logs", (log) => {
  try {
    const parsedLog = contract.interface.parseLog(log);
    console.log("Parsed Log:", parsedLog);
  } catch (err) {
    console.log("Not a relevant event:", err);
  }
});

6. Filter Specific Events

To reduce overhead, use filters to listen for specific events:

const filter = contract.filters.YourEventName(); // Add any indexed parameters if applicable

provider.on(filter, (log) => {
  const parsedLog = contract.interface.parseLog(log);
  console.log(`Filtered Event: ${parsedLog.name}`);
  console.log(`Event Data:`, parsedLog.args);
});

7. Testing the Listener

Trigger events in your smart contract (e.g., using a wallet or script) and ensure the listener captures them correctly. Example: Emit an event in your smart contract:

event YourEventName(address indexed user, uint256 value);
emit YourEventName(msg.sender, 1000);

Expected console output:

Console Logs Example
...................
Event received:
Param 1: 0xYourWalletAddress
Param 2: 1000
Full Event: {...}

Example final boilerplate incorporating previous steps, best practices for environment variables, and modular imports:

Putting it all Together

//event-listener.js

import dotenv from 'dotenv';
dotenv.config();

import { ethers } from "ethers";

// Load environment variables
const RPC_URL = process.env.PEAQ_RPC_URL || "https://peaq.api.onfinality.io/public"; // Replace with your preferred peaq RPC URL
const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS; // Replace with your contract address
import CONTRACT_ABI from "./contract_abi.json"; // Replace with the path to your contract ABI JSON file

// Initialize provider and contract
const provider = new ethers.JsonRpcProvider(RPC_URL);
const contract = new ethers.Contract(CONTRACT_ADDRESS, CONTRACT_ABI, provider);

// Function to listen for events
const listenForEvents = () => {
  console.log("Listening for events on contract:", CONTRACT_ADDRESS);

  // Replace "YourEventName" with the event name from your contract ABI
  contract.on("YourEventName", (param1, param2, event) => {
    console.log("Event Received:");
    console.log(`Param 1: ${param1}`);
    console.log(`Param 2: ${param2}`);
    console.log("Raw Event Data:", event);
  });

  // Add a generic log listener (optional)
  provider.on("logs", (log) => {
    try {
      const parsedLog = contract.interface.parseLog(log);
      console.log("Parsed Event Log:");
      console.log(parsedLog);
    } catch (error) {
      // Not a relevant event, ignore
    }
  });
};

// Error handling and script execution
const main = async () => {
  try {
    listenForEvents();
  } catch (error) {
    console.error("Error in event listener:", error);
    process.exit(1);
  }
};

main();

Tips

  1. Error Handling: Always handle potential errors (e.g., connectivity issues, unexpected logs).

  2. Batch Processing: Use a combination of historical event fetching (getLogs) and real-time listeners to ensure no data loss.

  3. Indexed Parameters: Use indexed parameters in your contract events to filter by specific values (e.g., filter by user address). Example:

    event YourEventName(address indexed user, uint256 value);
    

    Then filter by the address:

    const filter = contract.filters.YourEventName("0xSpecificAddress");
    
  4. Environment Variables: Store sensitive information (e.g., RPC URLs) in environment variables using a .env file.

Congratulations! 🎉 You now have the ability to efficiently listen to and parse smart contract events on the peaq network, empowering your dApp to respond dynamically to on-chain activity.