1/5
## Understanding the Challenge: Displaying Active NFT Listings When building a decentralized application (dApp) front-end, especially for an NFT marketplace, a common requirement is to display items currently available for sale. In our case, we want to show "Recently Listed NFTs" based on data originating from our `NftMarketplace.sol` smart contract. The core challenge lies in efficiently retrieving a list of NFTs that have been listed but *not yet* sold or cancelled by the seller. ## Why Smart Contract Mappings Aren't Enough Our `NftMarketplace.sol` contract utilizes a `mapping` (specifically, `mapping(address => mapping(uint256 => Listing)) private s_listings;`) to store the details of each listed NFT. Mappings in Solidity function as key-value stores. If you know the key (in this instance, the combination of `nftAddress` and `tokenId`), you can retrieve the corresponding `Listing` data very efficiently using the `getListing` function. However, mappings have a significant limitation in this context: they are not directly iterable. You cannot simply ask the smart contract for a list of all keys or values stored within a mapping without already knowing all possible keys. Consequently, there's no straightforward way to implement an on-chain function like `getAllActiveListings()` that returns a dynamic list of every currently available NFT. We need an alternative approach to gather this information for our front-end. ## The Solution: Leveraging Smart Contract Events While we cannot iterate over mappings directly, Solidity smart contracts provide a crucial mechanism for broadcasting information about state changes: **events**. Our `NftMarketplace.sol` contract is designed to emit specific events whenever the status of a listing changes: * `event ItemListed(address indexed seller, address indexed nftAddress, uint256 indexed tokenId, uint256 price);` * `event ItemCanceled(address indexed seller, address indexed nftAddress, uint256 indexed tokenId);` * `event ItemBought(address indexed buyer, address indexed nftAddress, uint256 indexed tokenId, uint256 price);` These events are recorded as logs on the blockchain and contain all the necessary details about the transaction (seller, NFT details, price, buyer). Critically, these event logs are accessible to off-chain services. By monitoring these events, an external service can reconstruct the history and current state of NFT listings. ## Introducing the Indexer: `rindexer` The process of listening to blockchain events, processing them, and storing the relevant data in an easily queryable format off-chain is called **indexing**. An **indexer** is the service that performs this task. Indexers are vital pieces of infrastructure for building responsive and feature-rich dApp front-ends, as they bridge the gap between on-chain data structures (like non-iterable mappings) and the querying needs of user interfaces. For this project, we utilize `rindexer` ([https://github.com/joshstevens19/rindexer](https://github.com/joshstevens19/rindexer)), a powerful, Rust-based framework designed for creating EVM (Ethereum Virtual Machine) indexers with minimal code. It allows developers to quickly set up a service that monitors contract events and populates a database. `rindexer` is a promising tool gaining traction in the web3 ecosystem. ## Configuring `rindexer` for Your Marketplace To instruct `rindexer` on which contract to monitor and what data to store, we use a configuration file, typically named `rindexer.yaml`. Key sections within this file include: 1. **`networks`**: Defines the blockchain networks `rindexer` should connect to. This includes specifying details like the network name (e.g., `anvil` for local development) and the RPC endpoint URL (e.g., `http://127.0.0.1:8545`). 2. **`storage`**: Configures the database where the indexed event data will be stored. `rindexer` supports various backends, including Postgres. You would enable and configure your chosen storage option here (e.g., `postgres: enabled: true`). 3. **`contracts`**: Specifies the smart contracts to be indexed. For each contract, you define: * `name`: A logical identifier for the contract (e.g., `NftMarketplace`). * `network`: The network alias (defined in the `networks` section) where the contract is deployed. * `address`: The actual deployed address of the smart contract instance. * `abi`: The path to the contract's Application Binary Interface (ABI) JSON file, which tells `rindexer` how to interpret the contract's events and functions. * `include_events`: A list of the specific events emitted by this contract that `rindexer` should listen for and record (e.g., `ItemListed`, `ItemCanceled`, `ItemBought`). Alongside the `rindexer.yaml` configuration, you'll typically manage the underlying database (like Postgres) using tools such as Docker and `docker-compose.yaml` for ease of setup and management. Once running, `rindexer` automatically listens for the specified events and populates the configured database tables accordingly. ## Querying Indexed Data: The Front-End Approach With `rindexer` running and populating our Postgres database with event data, the front-end (`src/components/RecentlyListed.tsx` in our example) now has a reliable source to query for listing information. Instead of attempting impossible direct calls to the smart contract's mapping, the front-end interacts with the indexer's data store. `rindexer` conveniently exposes a **GraphQL API** endpoint. GraphQL is a flexible query language for APIs, well-suited for front-end development as it allows clients to request exactly the data they need. Our front-end application will send GraphQL queries to this endpoint to fetch the processed event data. ## Implementing the Front-End Logic with GraphQL To display the active listings, the front-end needs to perform the following steps: 1. **Define a GraphQL Query:** Construct a query (e.g., `GET_RECENT_NFTS`) to fetch the necessary event data from the `rindexer` GraphQL API. This query typically requests: * The most recent `ItemListed` events, often with pagination and ordering (e.g., `first: 20, orderBy: [BLOCK_NUMBER_DESC, TX_INDEX_DESC]`). Include fields like `seller`, `nftAddress`, `tokenId`, and `price`. * A list of `ItemCanceled` events, primarily needing the `nftAddress` and `tokenId` to identify cancelled items. * A list of `ItemBought` events, also needing `nftAddress` and `tokenId` to identify purchased items. ```graphql # Example GraphQL Query Structure query GetActiveListings { allItemListeds(first: 20, orderBy: [BLOCK_NUMBER_DESC, TX_INDEX_DESC]) { nodes { # Required fields: seller, nftAddress, tokenId, price... } } allItemCanceleds { # Potentially filter/paginate these too in production nodes { nftAddress tokenId } } allItemBoughts { # Potentially filter/paginate these too in production nodes { nftAddress tokenId } } } ``` 2. **Fetch and Process Data:** Implement logic (often within a React custom hook like `useRecentlyListedNFTs`) to execute the GraphQL query and process the results: * Receive the response containing lists of listed, cancelled, and bought items. * Create efficient lookup structures (like JavaScript `Set` objects) for the cancelled and bought items. Store unique identifiers (e.g., a string combining `nftAddress-tokenId`) in these sets. * Filter the array of `ItemListed` items. For each listed item, check if its unique identifier exists in the `cancelledNFTs` set OR the `boughtNFTs` set. * Keep only those `ItemListed` items that are *not* found in either the cancelled or bought sets. This filtered list represents the NFTs that are currently active and available for sale. 3. **Render the UI:** Use the final, filtered list of active NFT listings (`recentNFTs` or `nftDataList`) to render the user interface components (e.g., mapping over the list to display individual `NFTBox` components). ## Key Takeaways and Best Practices * **Understand the Flow:** Recognize the end-to-end data flow: Smart Contract Events -> Indexer (`rindexer`) -> Database (Postgres) -> GraphQL API -> Front-End GraphQL Query -> Front-End Processing Logic -> User Interface. * **Events are Crucial:** Smart contract events are the standard mechanism for communicating state changes to the off-chain world. Use them when off-chain systems need to react to on-chain activity, especially for data not easily queryable directly (like mappings). * **Indexers are Essential:** For non-trivial dApps requiring efficient querying of blockchain data (especially historical or aggregated data), indexers are fundamental infrastructure. Tools like `rindexer` simplify their creation. * **GraphQL for Flexibility:** GraphQL provides an efficient way for front-ends to fetch precisely the data needed from indexers. * **Code Comprehension is Non-Negotiable:** Whether using AI assistance (like DeepSeek) or writing code manually, you *must* understand every line. AI tools may not be aware of specialized tools like `rindexer` or optimal patterns. Blindly copy-pasting code without understanding *why* it works (like the event filtering logic using sets) will lead to bugs and maintenance nightmares. Always validate, test, and comprehend any code integrated into your application, treating AI suggestions as input requiring careful review, similar to code from a junior team member.
An essential walkthrough to indexing smart contract events for NFT marketplaces. Overcome the limitations of Solidity mappings by leveraging events and an off-chain indexer (`rindexer`). See how to configure indexing and use GraphQL to efficiently display currently active NFT listings in your dApp.
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