Feb 26, 2024

Hands-on to create a Chainsight Event Indexer and build a mechanism to subscribe on-chain events

by Megared

So far we have introduced three components focused on the purpose of "collecting data. The component we present here, Event Indexer, follows the same objective, but with a slightly different means. It subscribes to events issued on-chain and stores the confirmed event information in the component.

The Event Indexer specifies events issued from EVM-compatible blockchain contracts, and subscribes to and stores those events. It is possible to specify a block number to start subscribing to and collect all events issued in subsequent blocks. This component allows monitoring of the various actions that occur in the contract itself, which is difficult to collect with Snapshot Indexer. To learn more about Events in Ethereum, please see below.

https://ethereum.org/developers/docs/smart-contracts/anatomy#events-and-logs

Let's build an Event Indexer that subscribes to ERC-20's Transfer event, which is typical of smart contracts.

https://eips.ethereum.org/EIPS/eip-20#events

Completed Component Definition

Target the DAI of ERC20 token, Ethereum's stable coin, and build an Event Indexer that collects DAI transfers.

chainsight_deepdive_into_showcase-handson-event_indexer.png

Prerequisite

The Chainsight CLI requires several tools and a csx installation. Please refer to the following articles for this preliminary preparation.

https://dev.to/hide_yoshi/step-by-step-creating-an-evm-price-oracle-with-chainsight-469g

Create a project & add a manifest template

As before, build from an empty project; use csx new --no-samples to create a template.

% csx new --no-samples event_indexer_pj 
Feb 13 12:56:29.488 INFO Start creating new project 'event_indexer_pj'...
Feb 13 12:56:29.489 INFO Project 'event_indexer_pj' created successfully
Feb 13 12:56:29.489 INFO You can add components with:

  cd event_indexer_pj && csx add

Then use csx add to add the Event Indexer, selecting the event_indexer type.

% cd event_indexer_pj
% csx add
✔ Please input Component Name to add · dai_transfer_subscriber
✔ Please select Component Type to add · event_indexer
Feb 13 12:57:22.481 INFO Start creating new component 'dai_transfer_subscriber'...
Feb 13 12:57:22.484 INFO EventIndexer component 'dai_transfer_subscriber' added successfully

From here, we will update the manifest so that DAI Transfer Events can be collected. The fields to be configured are a bit similar to those in the Snapshot Indexer EVM. The datasource field is important for setting the events to be subscribed to. Let's take a look at the generated manifest.

datasource:
  id: '0000000000000000000000000000000000000000'
  event:
    identifier: EventIdentifier
    interface: null
  network:
    rpc_url: https://polygon-mumbai.infura.io/v3/${INFURA_MUMBAI_RPC_URL_KEY}
    chain_id: 80001
  from: 37730337
  contract_type: ERC-20
  batch_size: null

The parameters that need to be modified this time are the id, event, network and from fields. Specify the blockchain network and its contract to be subscribed to in id, network, the RPC URL of the Ethereum blockchain, the chain ID, and the contract address of the DAI.

datasource:
  id: 0x6B175474E89094C44Da98b954EedeAC495271d0F
  event: ...
  network:
    rpc_url: https://polygon-mumbai.infura.io/v3/${INFURA_MUMBAI_RPC_URL_KEY}
    chain_id: 80001
  ...

Specify the event to be retrieved in event, the name to identify the event, and the ABI of the contract. Obtain the ABI for ERC20, which will be called this time, from the link below and create interfaces/ERC20.json.

https://etherscan.io/token/0x6b175474e89094c44da98b954eedeac495271d0f#code

Then from specifies the block at the start of the subscription.

Finally, event and from should be specified as follows.

datasource:
  id: ...
  event:
    identifier: Transfer
    interface: ERC20.json
  network: ...
  from: 17660942

The final manifest is as follows

version: v1
metadata:
  label: Dai Transfer Subscriber by Hands-on
  type: event_indexer
  description: 'Collect Transfer Event of DAI token. This is a sample of the components that will be built in the Hands-on article.'
  tags:
datasource:
  id: 0x6B175474E89094C44Da98b954EedeAC495271d0F
  event:
    identifier: Transfer
    interface: ERC20.json
  network:
    rpc_url: https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}
    chain_id: 1
  from: 17660942
  contract_type: ERC-20
  batch_size: null
interval: 1800 # 30min
cycles: null

Generate a code & build a module

Let's generate the canister code with csx generate. If you actually run this command, you will get the following output.

% csx generate
Feb 15 01:24:21.749 INFO Start code generation for project 'event_indexer_pj'
Feb 15 01:24:21.749 INFO Load env file: "./.env"
Feb 15 01:24:21.752 INFO [dai_transfer_subscriber] Start processing...
Feb 15 01:24:21.752 INFO [dai_transfer_subscriber] Interface file 'ERC20.json' copied from builtin interface
Feb 15 01:24:21.753 INFO [dai_transfer_subscriber] Generate interfaces (.did files) ...
Feb 15 01:24:23.007 INFO [dai_transfer_subscriber] Succeeded: Generate interfaces (.did files)
Feb 15 01:24:23.007 INFO Project 'event_indexer_pj' codes/resources generated successfully

There is no need to implement any additional logic in Event Indexer. The build process can be executed as is.

You can generate modules from code with csx build. Let's run it.

% csx build          
Feb 13 13:17:44.320 INFO Start building project 'event_indexer_pj'
Feb 13 13:17:44.320 INFO Load env file: "./.env"
Feb 13 13:17:44.321 INFO Start code generation for project 'event_indexer_pj'
Feb 13 13:17:44.321 INFO Load env file: "./.env"
Feb 13 13:17:44.321 INFO [dai_transfer_subscriber] Start processing...
Feb 13 13:17:44.321 INFO [dai_transfer_subscriber] Interface file 'ERC20.json' copied from builtin interface
Feb 13 13:17:44.389 INFO [dai_transfer_subscriber] Generate interfaces (.did files) ...
Feb 13 13:18:12.780 INFO [dai_transfer_subscriber] Succeeded: Generate interfaces (.did files)
Feb 13 13:18:12.780 INFO Project 'event_indexer_pj' codes/resources generated successfully
Feb 13 13:18:12.780 INFO Start building...
Feb 13 13:18:12.781 INFO Compile canisters...
Feb 13 13:18:51.182 INFO Succeeded: Compile canisters
Feb 13 13:18:51.182 INFO Shrink/Optimize modules...
Feb 13 13:18:51.304 INFO Succeeded: Shrink/Optimize modules
Feb 13 13:18:51.304 INFO Add metadata to modules...
Feb 13 13:18:51.416 INFO Succeeded: Add metadata to modules
Feb 13 13:19:14.065 INFO Project 'event_indexer_pj' built successfully

If you get logs like the above for this one as well, you are done. Finally, deploy and start the component!

Deploy a component

Deploy this component on the Chainsight Platform with csx deploy and csx exec as in previous hands-on sessions.

csx deploy --network ic --component dai_transfer_subscriber
csx exec --network ic --component dai_transfer_subscriber

When you run these, you should get the following logs.

% csx deploy && csx exec
Feb 13 13:19:15.970 INFO Checking environments...
Feb 13 13:19:15.970 INFO Running command: 'dfx ping http://127.0.0.1:4943'
Feb 13 13:19:16.033 INFO Suceeded: dfx ping http://127.0.0.1:4943
Feb 13 13:19:16.033 INFO Running command: 'dfx identity whoami'
Feb 13 13:19:16.093 INFO Suceeded: dfx identity whoami
Feb 13 13:19:16.093 INFO Running command: 'dfx identity get-principal'
Feb 13 13:19:16.167 INFO Suceeded: dfx identity get-principal
Feb 13 13:19:16.167 INFO Running command: 'dfx identity get-wallet'
Feb 13 13:19:20.479 INFO Suceeded: dfx identity get-wallet
Feb 13 13:19:20.479 INFO Checking environments finished successfully
Feb 13 13:19:20.479 INFO Start deploying project 'event_indexer_pj'...
Feb 13 13:19:20.480 INFO Running command: 'dfx canister create --all'
Feb 13 13:19:23.138 INFO Suceeded: dfx canister create --all
Feb 13 13:19:23.138 INFO Running command: 'dfx build --all'
Feb 13 13:19:23.402 INFO Suceeded: dfx build --all
Feb 13 13:19:23.402 INFO Running command: 'dfx canister install --all'
Feb 13 13:19:28.884 INFO Suceeded: dfx canister install --all
Feb 13 13:19:28.885 INFO Running command: 'dfx canister update-settings --add-controller 7fpuj-hqaaa-aaaal-acg7q-cai dai_transfer_subscriber'
Feb 13 13:19:31.795 INFO Suceeded: dfx canister update-settings --add-controller 7fpuj-hqaaa-aaaal-acg7q-cai dai_transfer_subscriber
Feb 13 13:19:31.981 INFO Current deployed status:
Canister Name: __Candid_UI
  Canister Id: {"local": "b77ix-eeaaa-aaaaa-qaada-cai"}
  Controllers: k245a-2cvyi-rcnzj-cpq6h-dcy7o-qrg4b-325bv-5c3om-jfceo-ikmjz-aqe
  Module Hash: 0x934756863c010898a24345ce4842d173b3ea7639a8eb394a0d027a9423c70b5c

Canister Name: dai_transfer_subscriber
  Canister Id: {"local": "bw4dl-smaaa-aaaaa-qaacq-cai"}
  Controllers: 7fpuj-hqaaa-aaaal-acg7q-cai br5f7-7uaaa-aaaaa-qaaca-cai k245a-2cvyi-rcnzj-cpq6h-dcy7o-qrg4b-325bv-5c3om-jfceo-ikmjz-aqe
  Module Hash: 0x80b8cbaa5c666cbd3dce384cf964d883828d78ac2ee2f2ffa1f9287b521499f8

Feb 13 13:19:32.030 INFO Execute canister processing...
Feb 13 13:19:32.030 INFO Load env file: "./.env"
Feb 13 13:19:32.030 INFO Start processing for commands generation...
Feb 13 13:19:32.031 INFO Script for Component "dai_transfer_subscriber" generated successfully
Feb 13 13:19:32.031 INFO Entrypoint Script generated successfully
Feb 13 13:19:32.031 INFO Start processing for commands execution...
Feb 13 13:19:32.031 INFO Run scripts to execute commands for deployed components
Feb 13 13:20:02.789 INFO Executed './scripts/entrypoint.sh' successfully
Feb 13 13:20:02.789 INFO Project "event_indexer_pj" canisters executed successfully

The deployment and operation start instruction is now complete! Now all you have to do is wait for the interval of the automatic execution and you can refer to the data.

Event Indexer has the following interface to retrieve data. Try using these to call components.

events_from_to : (nat64, nat64) -> (vec record { nat64; vec Transfer }) query;
events_latest_n : (nat64) -> (vec record { nat64; vec Transfer }) query;
get_last_indexed : () -> (nat64) query;

This concludes our hands-on with Event Indexer. We will continue to create and publish new hands-on sessions in the future, so please look forward to them!

Written by Megared@Chainsight