In this two-part guide, we’ll walk through how to build an onchain data indexing pipeline from scratch using The Indexing Company’s infrastructure. By the end of this series, you’ll have a live pipeline deployed in The Neighborhood, the distributed data network built by The Indexing Company, and streaming onchain events from the Aave protocol into your own database or webhook.

We’ll also provide a Postman collection so you can follow along step-by-step. For each command in this guide, we’ll reference its name as found in the Postman collection.

In Part 1, we’ll focus on:

  • Setting up a contract address filter
  • Writing transformation code to decode key contract events
  • Testing and registering the transformation for pipeline use

In Part 2, we’ll:

  • Set up a target database or webhook
  • Create a data schema using transformation output
  • Deploy the full pipeline and verify that the data is flowing

The Example Contract: Aave Pool

For this tutorial, we’ll be indexing events from the Aave Pool contract, which emits all the core supply, borrow, repay, and liquidation events. You can find its documentation and ABI here:

You can extract event signatures from either the ABI or directly from Etherscan. We recommend verifying or formatting them with an LLM to ensure they match the expected Solidity format used by The Indexing Company. Follow this Event Extraction Example for tips.


Step 1: Set up a contract address filter

Postman Command Name: 1. Create Contract Filter

The first step is to define which contracts your pipeline should listen to. If no filter is applied, the transformation code will be applied to all blocks. In our case, we’ll be indexing events from the Aave Pool contract.

To register this contract as a filter, send the following curl request:

curl --location 'https://app.indexing.co/dw/filters/aave_example_filter' \
--header 'X-API-KEY: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{"values": [
    "0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2"
]}'

This creates a named filter called aave_example_filter that targets the Aave Pool contract. You can confirm it was created by calling:

Postman Command Name: 2. Get Contract Filters

curl --location 'https://app.indexing.co/dw/filters/aave_example_filter' \
--header 'X-API-KEY: YOUR_API_KEY'

If you need to remove the filter later:

Postman Command Name: 3. Delete Contract Filter

curl --location --request DELETE 'https://app.indexing.co/dw/filters/aave_example_filter' \
--header 'X-API-KEY: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{"values": [
    "0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2"
]}'


Step 2: Write transformation code

JavaScript Filename: AavePool.js

Now that the contract filter is set up, it’s time to define the logic to extract relevant onchain events. The Indexing Company supports transformation functions written in JavaScript, which operate on each block. We will decode logs and format events for downstream use. For an instruction to find and transform these events visit our (Event Extraction Example)[/examples/evm_event_sigs].

Below is the transformation code we’ll use to extract key Aave Pool events:

function AaveEvents(block) {
  const events = [];

  for (const tx of block.transactions || []) {
    for (const log of tx.receipt?.logs || []) {
      const decodedWithMetadata = utils.evmDecodeLogWithMetadata(log, [
        "event Supply(address indexed reserve, address user, address indexed onBehalfOf, uint256 amount, uint16 indexed referralCode)",
        "event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount)",
        "event Borrow(address indexed reserve, address user, address indexed onBehalfOf, uint256 amount, uint8 interestRateMode, uint256 borrowRate, uint16 indexed referralCode)",
        "event Repay(address indexed reserve, address indexed user, address indexed repayer, uint256 amount, bool useATokens)",
        "event FlashLoan(address indexed target, address initiator, address indexed asset, uint256 amount, uint8 interestRateMode, uint256 premium, uint16 indexed referralCode)",
        "event UserEModeSet(address indexed user, uint8 categoryId)"
      ]);

      if (decodedWithMetadata) {
        events.push({
          chain: block._network,
          block: block.number,
          transaction_hash: tx.hash,
          log_index: log.logIndex,
          contract_address: log.address?.toLowerCase(),
          decoded: decodedWithMetadata.decoded,
          event_name: decodedWithMetadata.metadata?.name.replace(/^event\s+/, "") || "UnknownEvent"
        });
      }
    }
  }

  return events;
}

We’ll test this transformation in the next step using a historical block and verify that it extracts the correct Aave Pool events before registering it into the system.


Step 3: Test the transformation

Postman Command Name: 4a. Test Transformation (JS code upload) or 4b. Test Transformation (JS code as Text)

Before registering your transformation into a live pipeline, it’s essential to test it against real onchain data. The Indexing API provides a /transformations/test endpoint that lets you simulate a run using a specific block number and your contract filter. We are using this transaction to test on. To get the networkyou look up the Network Key here. We are testing this on Ethereum at block 22282149 (called beat in the API).

Use the following curl to test the transformation code:

curl --location --globoff 'https://app.indexing.co/dw/transformations/test?network=ethereum&beat=22282149&filter=aave_example_filter&filterKeys[0]=contract_address' \
--header 'X-API-KEY: YOUR_API_KEY' \
--form 'code=@"AavePool.js"'

If you’re using Postman:

  • Select POST
  • Set the URL with the correct query parameters
  • Go to Body > form-data, set key to code, type Text, and paste in your transformation code OR set type File and upload the AavePool.js file.

This test will return a JSON response with decoded logs for that block. Make sure your expected events (e.g., Supply, Borrow, etc.) show up correctly.

For block 22282149 on Ethereum, you should receive an output similar to the following:

[
    {
        "chain": "ETHEREUM",
        "block": 22282149,
        "transaction_hash": "0xc0814c035946d6889497a82d6515647f939f1dfe5d86d46c4294b3c6b127bad7",
        "log_index": 534,
        "contract_address": "0x87870bca3f3fd6335c3f4ce8392d69350b4fa4e2",
        "decoded": {
            "reserve": "0xD533a949740bb3306d119CC777fa900bA034cd52",
            "onBehalfOf": "0x3f3B17da678a495EbEc8a061657Cb6CFa4901531",
            "referralCode": 0,
            "user": "0x3f3B17da678a495EbEc8a061657Cb6CFa4901531",
            "amount": "15000000000000000000000"
        },
        "event_name": "Supply"
    }
]


Step 4: Register the transformation

Postman Command Name: 5a. Register Transformation (JS code upload) or 5b. Register Transformation (JS code as Text)

Once you’ve confirmed your transformation code works correctly, you can register it for production use. This saves the code under a unique transformation ID, allowing you to reference it in the pipeline setup.

Use the following curl to register the transformation:

curl --location 'https://app.indexing.co/dw/transformations/aave_example' \
--header 'X-API-KEY: YOUR_API_KEY' \
--form 'code="function AaveEvents(block) { ... }"'  # paste the full function here

In Postman:

  • Select POST
  • Use the same endpoint URL
  • Under Body > form-data, key should be code, set type to Text, and paste your transformation function OR set type File and upload the AavePool.js file.

Once registered, your transformation is ready to be used in a full indexing pipeline in The Neighborhood.


What’s Next?

In Part 2, we’ll continue by wiring up the destination: setting up a database or webhook, generating a schema from your transformation output, and deploying the full pipeline.

Need help getting started or want to integrate indexing into your stack?