1/5
## Checking for Events: Indexing Smart Contract Interactions Locally This lesson demonstrates how an indexer, specifically rindexer, detects and processes blockchain events emitted from smart contracts. We'll observe this interaction within a local development environment using a dApp frontend, a local blockchain node (Anvil), and a persistent database managed by Docker. We will also troubleshoot a common state synchronization issue that arises when restarting the local blockchain without resetting the indexer's state. ## Setting the Stage: Local Development Environment Before we begin interacting with the smart contracts, ensure the following components are running locally: 1. **dApp Frontend:** A Next.js NFT Marketplace website, accessible at `http://localhost:3000`. 2. **Blockchain Node:** An Anvil instance serving as our local development blockchain. Terminal logs showing `eth_getBlockByNumber` calls confirm it's active. 3. **Indexer:** rindexer, configured to connect to the Anvil node and the database. 4. **Database:** A Postgres database managed via Docker, utilizing a Docker volume named `marketplaceindexer_postgres_data` to persist indexed data and indexer state. ## Understanding Key Concepts Several core concepts underpin this process: * **Blockchain Events:** Smart contracts can emit events to signal that specific actions have occurred (e.g., `ItemListed`, `ItemBought`). These events contain data logged immutably on the blockchain. * **Indexing:** An indexer is a service that monitors a blockchain node for specific events emitted by designated smart contracts. Upon detecting relevant events, it processes the associated data and typically stores it in an optimized off-chain database (like Postgres) for efficient querying by applications. * **Local Development Environment:** This setup simulates the entire blockchain interaction lifecycle locally using tools like Anvil (blockchain simulation), Next.js (frontend framework), rindexer (indexing service), and Docker (containerization for services like the database). * **State Synchronization:** The indexer must maintain a consistent state relative to the blockchain it's indexing. It achieves this by tracking the last block number it successfully processed. * **Chain Reorganization (Reorg):** On live blockchains (and occasionally simulated ones), the perceived "latest" block or sequence of blocks can change. This is known as a reorg. To handle this, indexers often wait for a certain number of "confirmation blocks" (a "safe reorg range") before considering data from a block finalized, preventing the processing of events from blocks that might later be orphaned. * **Docker Volumes:** These provide persistent storage for Docker containers. In our setup, a Docker volume ensures that the data stored by the Postgres container (indexed events and the indexer's last processed block state) survives container restarts. ## Triggering and Observing Events: A Practical Walkthrough Let's interact with the dApp to generate some events and see if the indexer picks them up. **1. Minting an NFT:** * Navigate to the "Cake NFT" section within the local dApp. * Click "Bake a Cake NFT". * Approve the transaction using your wallet (e.g., MetaMask) connected to the local Anvil network. * The UI should confirm successful minting, displaying a `TokenID` (e.g., `TokenID: 5`). **2. Listing the NFT:** * Navigate to the "List Your NFT" page. * Identify the `cakeNft` contract address. This is typically defined in a constants file within the project codebase (e.g., `src/constants.ts` might contain `cakeNft: "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0"` for the local chain ID `31337`). * Enter the `cakeNft` contract address, the `Token ID` you just minted (e.g., `5`), and a desired `Price` (e.g., `10` USDC). * Click "Preview Listing". * First, approve the marketplace contract to handle your NFT by clicking "Approve NFT" and confirming the transaction in your wallet. * Next, click "List NFT for Sale" and confirm the `listItem` transaction in your wallet. * The UI should confirm the successful listing. ## Diagnosing a Common Indexing Issue: State Synchronization Now, let's check the rindexer logs running in the terminal. You might observe repeating messages similar to this: ```log INFO NftMarketplace::ItemListed - LIVE - not in safe reorg block range yet block: 67 > range: 14 INFO NftMarketplace::ItemListed - LIVE - not in safe reorg block range yet block: 67 > range: 15 ... INFO NftMarketplace::ItemListed - LIVE - not in safe reorg block range yet block: 67 > range: 66 ``` **Explanation:** This situation typically occurs if the indexer was run previously, processed blocks up to a certain height (e.g., block 67), and stored this state in its persistent database (via the Docker volume). If you subsequently stopped and restarted the *Anvil node* (which resets its block numbers back to 0) *without* resetting the indexer's database, a state mismatch occurs. The indexer, upon restarting, reads its last known state (block 67) from the database. However, it sees the fresh Anvil chain starting from block 0. Because of the "safe reorg range" mechanism, the indexer waits until the *new* chain height (`range: 14, 15, ...`) surpasses the last *known* height (block 67) before it starts processing "live" events. This ensures it doesn't process potentially orphaned blocks if a reorg were to happen (though less likely on a local dev chain). **Tip:** When restarting a local development chain like Anvil that resets its state, you generally need to reset your indexer's database state as well to ensure accurate indexing from block 0. ## Recovering and Verifying: Correcting the State Assuming enough time has passed for the local Anvil node to mine blocks past the previously recorded height (e.g., past block 67), let's perform the actions again to ensure the indexer captures them correctly now. **1. Mint and List a New NFT:** * Mint another Cake NFT (you'll receive a new `TokenID`, e.g., `6`). * List this new NFT (Token ID 6) for sale (e.g., 10 USDC), completing the Approve and List transactions via your wallet. **2. Check Indexer and Verify Success:** * Examine the rindexer logs again. You should now see a message indicating successful indexing: ```log INFO NftMarketplace::ItemListed - INDEXED - 1 events - blocks: 90 - 90 - network: anvil ``` This log confirms that the `ItemListed` event (for Token ID 6 in this example) occurring in block 90 was detected and processed by the indexer. * Verify the data persistence by checking the output file generated by the indexer, typically located at a path like `marketplaceIndexer/generated_csv/NftMarketplace/nftmarketplace-itemlisted.csv`. * Open this CSV file. You should find a new row added, containing the details (seller, NFT address, token ID, price) of the NFT #6 listing, confirming the indexer successfully processed the event and stored the relevant data. ## The Proper Reset Procedure for Local Development To definitively resolve the state mismatch issue caused by restarting the local blockchain, follow this procedure to reset the indexer and its database state: 1. **Stop the Indexer:** Halt the running `rindexer` process (usually with `Ctrl+C` in its terminal). 2. **(Optional) Clean Output:** Delete any generated output files (e.g., the `generated_csv` directory) if you want a completely clean slate. 3. **Stop and Remove the Database Container:** * Find the container ID or name: `docker ps` (Look for the postgres container, e.g., `marketplaceindexer-postgresql-1`). * Stop the container: `docker kill <container_name_or_id>` (e.g., `docker kill marketplaceindexer-postgresql-1`). * Verify it's stopped: `docker ps -a` (Status should be 'Exited'). * Remove the container definition: `docker rm <container_name_or_id>` (e.g., `docker rm marketplaceindexer-postgresql-1`). 4. **Remove the Database Volume:** This step deletes the persisted data, including the indexer's state. * List volumes: `docker volume ls` (Find the volume name, e.g., `marketplaceindexer_postgres_data`). * Remove the volume: `docker volume rm <volume_name>` (e.g., `docker volume rm marketplaceindexer_postgres_data`). **Caution:** This permanently deletes the data stored in the volume. 5. **Restart the Indexer:** * Run the command to start the indexer again: `rindexer start indexer`. * Observe the logs. rindexer will likely attempt to connect to the database, fail (as it was removed), and then automatically use `docker-compose` (or similar mechanism defined in its configuration) to recreate and start the Postgres container and its volume. It will then initialize the database schema (create tables) and begin indexing from block 0. You might see logs indicating it found historical events if any occurred before block 0 on your fresh Anvil instance, or it might simply start processing from block 0 upwards. This reset procedure ensures that both the local blockchain and the indexer start from a synchronized state (block 0). ## Key Takeaways * Indexers like rindexer listen for smart contract events on the blockchain and store processed data, often in an external database. * In local development using ephemeral chains like Anvil (which reset on restart), it's crucial to synchronize the state of the indexer with the state of the chain. * Failure to reset the indexer's persistent state (stored in the database volume) after restarting the local chain can lead to the indexer delaying processing of new events due to its "safe reorg range" logic, as it waits for the new chain height to surpass its last recorded height. * The reliable way to reset the rindexer's state when using Docker is to stop the indexer, stop and remove the database container (`docker kill`, `docker rm`), remove the associated Docker volume (`docker volume rm`), and then restart the indexer.
A hands-on walkthrough to checking for events: Indexing smart contract interactions locally. See how an indexer detects and stores smart contract events within a local Anvil blockchain environment. Understand and resolve common state synchronization issues that occur when restarting the local chain.
Previous lesson
Previous
Next lesson
Next
Give us feedback
Course Overview
About the course
How to build full-stack web3 applications on ZKsync
JavaScript/TypeScript: viem, wagmi, synpress
Nodejs and pnpm
rindexer
Circle Compliance Engine and USDC
Fleek site hosting and CLI
How to build a static and dynamic React/Next.js site
How to leverage AI to code faster and more securely
Smart Contract Auditor
$100,000 - $200,000 (avg. salary)
DeFi Developer
$75,000 - $200,000 (avg. salary)
Smart Contract Engineer
$100,000 - $150,000 (avg. salary)
Web3 developer
$60,000 - $150,000 (avg. salary)
Web3 Developer Relations
$85,000 - $125,000 (avg. salary)
Security researcher
$49,999 - $120,000 (avg. salary)
Last updated on May 15, 2025
Solidity Developer
Full-Stack Web3 Development Crash CourseDuration: 1h 12min
Duration: 1h 39min
Duration: 3h 08min
Duration: 1h 44min
Course Overview
About the course
How to build full-stack web3 applications on ZKsync
JavaScript/TypeScript: viem, wagmi, synpress
Nodejs and pnpm
rindexer
Circle Compliance Engine and USDC
Fleek site hosting and CLI
How to build a static and dynamic React/Next.js site
How to leverage AI to code faster and more securely
Smart Contract Auditor
$100,000 - $200,000 (avg. salary)
DeFi Developer
$75,000 - $200,000 (avg. salary)
Smart Contract Engineer
$100,000 - $150,000 (avg. salary)
Web3 developer
$60,000 - $150,000 (avg. salary)
Web3 Developer Relations
$85,000 - $125,000 (avg. salary)
Security researcher
$49,999 - $120,000 (avg. salary)
Last updated on May 15, 2025