Feb 19, 2024

Hands-on to create a Chainsight Snapshot Indexer ICP and build a mechanism to collect data in Internet Computer.

by Megared

Last time we created a hands-on article on creating a component to get Ethereum price data from an Ethereum contract. The Snapshot Indexer EVM uses ABI as its interface to retrieve data from EVM-compatible chains. In this article, we will try to retrieve data from Internet Computer by using Candid, an interface on the Internet Computer where Chainsight is running. In this article, we will try to create a component that collects data in Internet Computer using Snapshot indexer ICP.

This project is a Price Snapshot Indexer ICP that collects ICP prices using a DEX contract on Internet Computer. As of February 2024, 1 ICP is trading at around $12.

Completed Component Definition

ICPSwap, one of the well-known DEXes on the Internet Computer, is used to obtain ICP prices. The ICPSwap contract (canister) is called periodically to collect ICP price data. Snapshot Indexer is constructed with a collection interval of 1 hour.

chainsight_deepdive_into_showcase-handson-snapshot_indexer_icp.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 sample_icp
Feb 06 02:00:18.697 INFO Start creating new project 'sample_icp'...
Feb 06 02:00:18.698 INFO Project 'sample_icp' created successfully
Feb 06 02:00:18.698 INFO You can add components with:

  cd sample_icp && csx add

Then use csx add to add the Snapshot Indexer EVM. snapshot_indexer_icp type should be selected.

% csx add
✔ Please input Component Name to add · icp_snapshot_indexer
✔ Please select Component Type to add · snapshot_indexer_icp
Feb 06 02:01:11.560 INFO Start creating new component 'icp_snapshot_indexer'...
Feb 06 02:01:11.560 INFO SnapshotIndexerICP component 'icp_snapshot_indexer' added successfully

Let's update this manifest that we have added so that we can call Canister already deployed on Internet Computer. In this case, we will use the Canister used in ICPSwap.

CanisterId: 2ixw4-taaaa-aaaag-qcpdq-cai

Untitled.png

Source: https://info.icpswap.com/swap/token/details/ryjl3-tyaaa-aaaaa-aaaba-cai

In the datasource field, specify the canister and its function to call this time.

Specify the ID of the canister to call in the location.id field. In this case, set 2ixw4-taaaa-aaaag-qcpdq-cai. Next, in datasource.method, specify the canister function to call. In method.identifier, specify the function in Candid format, which is the interface notation of Internet Computer.

Candid is an interface description language. Its primary purpose is to describe the public interface of a service, usually in the form of a program deployed as a canister that runs on the Internet Computer. One of the key benefits of Candid is that it is language-agnostic, and allows inter-operation between services and frontends written in different programming languages, including Motoko, Rust, and JavaScript.

Source: https://internetcomputer.org/docs/current/developer-docs/backend/candid/candid-concepts

Internet Computer makes it easy to see the interface of the canister by using the Dashboard.

Untitled.png Untitled.png

Source: https://dashboard.internetcomputer.org/canister/2ixw4-taaaa-aaaag-qcpdq-cai

This is the canister's Candid Interface that we will call. The declarations in the service section are the interfaces provided by the canister, and in this case we will use the get_exchange_rates function described here. There are two things we need to do to specify this function.

Specify a function declaration in method.identifier that includes arguments and response information: get_exchange_rates: () -> (vec ExchangeRate__1). Since this declaration uses a custom type that is not a Candid builtin, create a .did file that includes the Candid Interface so that it can be referenced, and specify that file in method.interface. With Candid selected in the Canister Interface of the Dashboard screen mentioned earlier, click "Copy" in the upper right corner to copy the declaration with the interface. Then, create target_interface.did under the interfaces folder and paste what you just copied into its contents. The actual folder and file will look like this

% ls interfaces 
target_interface.did
% cat interfaces/target_interface.did 
type ExchangeRate__1 = 
 record {
   base_class: text;
   base_symbol: text;
   decimals: nat32;
   error: opt Error;
   quote_class: text;
...

Once the file is created, specify interfaces/target_interface.did in method.interface. At this point, the datasource field should look like the following

datasource:
  location:
    id: 2ixw4-taaaa-aaaag-qcpdq-cai
  method:
    identifier: 'get_exchange_rates: () -> (vec ExchangeRate__1)'
    interface: interfaces/target_interface.did
    args: []

Finally, add a setting for this component to call canisters outside of Chainsight Platform. is_target_component should be set to false. If this flag is not set and is true, the component is to call a component within Chainsight Platform. This completes the update of the manifest. The completed manifest is as follows

version: v1
metadata:
  label: ICP Snapshot Indexer by handson
  type: snapshot_indexer_icp
  description: 'Collect ICP prices. This is a sample of the components that will be built in the Hands-on article.'
  tags:
  - Price Snapshot Indexer
  - ICP
  - "Hands-on Sample"
datasource:
  location:
    id: 2ixw4-taaaa-aaaag-qcpdq-cai
  method:
    identifier: 'get_exchange_rates: () -> (vec ExchangeRate__1)'
    interface: interfaces/target_interface.did
    args: []
is_target_component: false
lens_targets: null
interval: 3600
cycles: null

For metadata, any arbitrary setting is fine. Check the datasource field and is_target_component again. If you have successfully modified the code up to this point, let's generate the code immediately.

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 06 07:22:01.688 INFO Start code generation for project 'sample_icp'
Feb 06 07:22:01.691 INFO [icp_snapshot_indexer] Start processing...
Feb 06 07:22:01.694 INFO [icp_snapshot_indexer] Generate interfaces (.did files) ...
Feb 06 07:22:14.300 INFO [icp_snapshot_indexer] Succeeded: Generate interfaces (.did files)
Feb 06 07:22:14.300 INFO Project 'sample_icp' codes/resources generated successfully

As with the Snapshot Indexer EVM, no additional customization is required for this interface in the Snapshot Indexer ICP. The build process can begin without any additional customization.

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

% csx build
Feb 07 14:28:14.403 INFO Start building project 'sample_icp'
Feb 07 14:28:14.403 INFO Start code generation for project 'sample_icp'
Feb 07 14:28:14.406 INFO [icp_snapshot_indexer] Start processing...
Feb 07 14:28:14.410 INFO [icp_snapshot_indexer] Skip creating logic project: './src/logics/icp_snapshot_indexer' already exists
Feb 07 14:28:14.411 INFO [icp_snapshot_indexer] Generate interfaces (.did files) ...
Feb 07 14:28:19.094 INFO [icp_snapshot_indexer] Succeeded: Generate interfaces (.did files)
Feb 07 14:28:19.094 INFO Project 'sample_icp' codes/resources generated successfully
Feb 07 14:28:19.094 INFO Start building...
Feb 07 14:28:19.099 INFO Compile canisters...
Feb 07 14:28:25.620 INFO Succeeded: Compile canisters
Feb 07 14:28:25.620 INFO Shrink/Optimize modules...
Feb 07 14:28:25.693 INFO Succeeded: Shrink/Optimize modules
Feb 07 14:28:25.693 INFO Add metadata to modules...
Feb 07 14:28:25.755 INFO Succeeded: Add metadata to modules
Feb 07 14:28:25.755 INFO Project 'sample_icp' built successfully

The build will be completed in less than a few minutes. This command generates the WASM module and candid interface. For a more in-depth understanding of these behaviors, please refer to the following articles

https://medium.com/@Chainsight_Network/behind-chainsight-how-modules-are-created-pt-2-4cf29234b98b

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 icp_snapshot_indexer
csx exec --network ic --component icp_snapshot_indexer

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

% csx deploy --network ic --component icp_snapshot_indexer
Feb 07 14:43:27.188 INFO Checking environments...
Feb 07 14:43:27.188 INFO Running command: 'dfx ping ic'
Feb 07 14:43:27.828 INFO Suceeded: dfx ping ic
Feb 07 14:43:27.828 INFO Running command: 'dfx identity whoami --network ic'
Feb 07 14:43:27.910 INFO Suceeded: dfx identity whoami --network ic
Feb 07 14:43:27.910 INFO Running command: 'dfx identity get-principal --network ic'
Feb 07 14:43:27.987 INFO Suceeded: dfx identity get-principal --network ic
Feb 07 14:43:27.987 INFO Running command: 'dfx identity get-wallet --network ic'
Feb 07 14:43:28.063 INFO Suceeded: dfx identity get-wallet --network ic
Feb 07 14:43:28.063 INFO Checking environments finished successfully
Feb 07 14:43:28.063 INFO Start deploying project 'sample_icp'...
Feb 07 14:43:28.063 INFO Running command: 'dfx canister create --network ic icp_snapshot_indexer'
Feb 07 14:43:32.757 INFO Suceeded: dfx canister create --network ic icp_snapshot_indexer
Feb 07 14:43:32.757 INFO Running command: 'dfx build --network ic icp_snapshot_indexer'
Feb 07 14:43:32.945 INFO Suceeded: dfx build --network ic icp_snapshot_indexer
Feb 07 14:43:32.945 INFO Running command: 'dfx canister install --network ic icp_snapshot_indexer'
Feb 07 14:43:43.151 INFO Suceeded: dfx canister install --network ic icp_snapshot_indexer
Feb 07 14:43:43.151 INFO Running command: 'dfx canister update-settings --add-controller xkzfe-fiaaa-aaaal-qcnuq-cai --network ic icp_snapshot_indexer'
Feb 07 14:43:50.150 INFO Suceeded: dfx canister update-settings --add-controller xkzfe-fiaaa-aaaal-qcnuq-cai --network ic icp_snapshot_indexer
Feb 07 14:43:51.507 INFO Current deployed status:
Canister Name: icp_snapshot_indexer
  Canister Id: {"ic": "lqf7x-4yaaa-aaaal-qdg7q-cai"}
  Controllers: 5naws-oiaaa-aaaal-qbxuq-cai k245a-2cvyi-rcnzj-cpq6h-dcy7o-qrg4b-325bv-5c3om-jfceo-ikmjz-aqe xkzfe-fiaaa-aaaal-qcnuq-cai
  Module Hash: 0xfa98d0d929e40968a31f9da12485805b512ed538ae6b74ec41364057546d1ce3

% csx exec --network ic --component icp_snapshot_indexer
Feb 07 14:43:59.998 INFO Execute canister processing...
Feb 07 14:43:59.998 INFO Start processing for commands generation...
Feb 07 14:44:00.001 INFO Script for Component "icp_snapshot_indexer" generated successfully
Feb 07 14:44:00.001 INFO Entrypoint Script generated successfully
Feb 07 14:44:00.001 INFO Start processing for commands execution...
Feb 07 14:44:00.001 INFO Run scripts to execute commands for deployed components
Feb 07 14:44:00.001 INFO Selected component is 'icp_snapshot_indexer'
Feb 07 14:44:44.155 INFO Executed './scripts/entrypoint.sh' successfully
Feb 07 14:44:44.155 INFO Project "sample_icp" 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.


The components that can be created in this hands-on session are also actually running! (We have changed the description and execution interval a bit.)

Untitled.png

Source: https://beta.chainsight.network/explore/components/lqf7x-4yaaa-aaaal-qdg7q-cai

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

Written by Megared@Chainsight