Step-by-step creating an EVM Price Oracle with Chainsight
Tags: blockchain, icp, smartcontract, defi
Published: 2024-01-10 03:05:55


When we start a defi project, we might need a price oracle to get the price of the token. Usually we use Chainlink or Band Protocol for a famous token.
But if we want to get the price of a new token or a token that is not famous, we might need to create our own price oracle.
In this article, I will show you how to create an EVM oracle with Chainsight.

## What is Chainsight?
[Chainsight](https://chainsight.network/) is a composable-oracle platform that a developer can create an oracle for their own project easily.
It is a permissionless oracle network that anyone can create an oracle for their own project. And its components can be used by other developers to create their own oracle.


Blockchains are state machines and are not designed to handle large amounts of data. DApps running on the blockchain are basically designed to be simple state management. This is quite straightforward in designing the protocol. However, it is difficult when trying to develop slightly more complex applications. As a result, the future that World Computer originally envisioned, where any kind of applications keep working sustainably, has not yet arrived.


Chainsight provides a data processing layer to develop applications that go one step ahead. It is an extension layer that leverages historical data, constantly updated data, and cross-chain data to compute the information you need for your application. A user can use a privacy layer for sensitive transactions, a scaling layer for high-volume transactions, and a data processing layer for computation with large amounts of data. With Chainsight, on-chain applications will be able to address more use cases that were previously difficult to achieve.s

Chainsight's smart contracts is deployed on the [Internet Computer](https://dfinity.org/) and called canisters. Using Internet Computer's [Chain key criptography](https://support.dfinity.org/hc/en-us/articles/360057605551-What-is-chain-key-cryptography-), Chainsight can securely connect to other blockchains and get the data from them. And the data can be used by other canisters on the Internet Computer.

## Prerequisite

In this example, we will use the following tools.
- [Node.js](https://nodejs.org/en/)
- [Yarn](https://yarnpkg.com/)
- [Solidity](https://docs.soliditylang.org/en/)
- [Rust](https://www.rust-lang.org/)
- [dfx](https://sdk.dfinity.org/docs/index.html)
- [Make](https://www.gnu.org/software/make/)
- Some SepoliaETH for executing the contract on the testnet
    - [Get SepoliaETH](https://sepolia-faucet.pk910.de/)

## Install Chainsight SDK(csx)

To install Chainsight SDK(csx), run the following command.

```bash
git clone https://github.com/horizonx-tech/chainsight-cli.git
cd chainsight-cli
cargo install --path .
```

## Project overview we will create

In this article, we will create a project that gets the price of ETH from a chainlink contract on the Ethereum network and relay it to the Sepolia network.

## Create a project

At first, we need to create a Chainsight project. To create a project, run the following command.
    
```bash
csx new price_oracle
```

This command will create a project named `price_oracle` in the current directory.

You can see the project structure like this.

```bash
price_oracle
├── components
    |── sample_algorithm_indexer.yaml
    |── sample_algorithm_lens.yaml
    |── sample_event_indexer.yaml
    |── sample_relayer.yaml
    |── sample_snapshot_indexer_evm.yaml
    |── sample_snapshot_indexer_https.yaml
    |── sample_snapshot_indexer_icp.yaml
├── interfaces
├── .chainsight
├── .env
├── project.yaml
```

Not all of the files are necessary for this project. So we will delete some of them.

First of all, let's enter the project directory.

```bash
cd price_oracle
```

## Build and deploy the project for the first time

To build the project, run the following command.

```bash
csx build
```


Just as a test, let's deploy the project to dfx local network. To start dfx local network, run the following command.

```bash
dfx start --clean
```


To deploy the project, run the following command.

```bash
csx deploy
```

You can see the log like this.

```bash
Jan 09 06:41:11.227 INFO Current deployed status:
Canister Name: __Candid_UI
  Canister Id: {"local": "avqkn-guaaa-aaaaa-qaaea-cai"}
  Controllers: yuk46-o67cx-p3ddo-vzb27-o3cp4-owe4v-u4r3g-htj3u-4v2h6-sne4q-wqe
  Module Hash: 0x934756863c010898a24345ce4842d173b3ea7639a8eb394a0d027a9423c70b5c

Canister Name: sample_algorithm_indexer
  Canister Id: {"local": "bkyz2-fmaaa-aaaaa-qaaaq-cai"}
  Controllers: 7fpuj-hqaaa-aaaal-acg7q-cai bnz7o-iuaaa-aaaaa-qaaaa-cai yuk46-o67cx-p3ddo-vzb27-o3cp4-owe4v-u4r3g-htj3u-4v2h6-sne4q-wqe
  Module Hash: 0x35b7fd5e5a57044b50b44007a4a5a275abe9c88464c6b8848294eb4e90291417

Canister Name: sample_algorithm_lens
  Canister Id: {"local": "bd3sg-teaaa-aaaaa-qaaba-cai"}
  Controllers: 7fpuj-hqaaa-aaaal-acg7q-cai bnz7o-iuaaa-aaaaa-qaaaa-cai yuk46-o67cx-p3ddo-vzb27-o3cp4-owe4v-u4r3g-htj3u-4v2h6-sne4q-wqe
  Module Hash: 0xdf5b0e5a6a61192cc3659ecaddf5f1866b67c8e6ee9544b55cb7b7cf51b7c4b2

Canister Name: sample_event_indexer
  Canister Id: {"local": "be2us-64aaa-aaaaa-qaabq-cai"}
  Controllers: 7fpuj-hqaaa-aaaal-acg7q-cai bnz7o-iuaaa-aaaaa-qaaaa-cai yuk46-o67cx-p3ddo-vzb27-o3cp4-owe4v-u4r3g-htj3u-4v2h6-sne4q-wqe
  Module Hash: 0x06a6afd2c985c268864ec0b2d13e058a24f395d2196db8349e536df17312f733

Canister Name: sample_relayer
  Canister Id: {"local": "br5f7-7uaaa-aaaaa-qaaca-cai"}
  Controllers: 7fpuj-hqaaa-aaaal-acg7q-cai bnz7o-iuaaa-aaaaa-qaaaa-cai yuk46-o67cx-p3ddo-vzb27-o3cp4-owe4v-u4r3g-htj3u-4v2h6-sne4q-wqe
  Module Hash: 0x76de26fc1016eea97d8b342bc60a74eb878a7fee85216b08103fc4855a50ebb8

Canister Name: sample_snapshot_indexer_evm
  Canister Id: {"local": "bw4dl-smaaa-aaaaa-qaacq-cai"}
  Controllers: 7fpuj-hqaaa-aaaal-acg7q-cai bnz7o-iuaaa-aaaaa-qaaaa-cai yuk46-o67cx-p3ddo-vzb27-o3cp4-owe4v-u4r3g-htj3u-4v2h6-sne4q-wqe
  Module Hash: 0x36cfd3c2816d7a27d8d1cfd77953c5404c1c87e42b78363d4114c65b86384fd3

Canister Name: sample_snapshot_indexer_https
  Canister Id: {"local": "b77ix-eeaaa-aaaaa-qaada-cai"}
  Controllers: 7fpuj-hqaaa-aaaal-acg7q-cai bnz7o-iuaaa-aaaaa-qaaaa-cai yuk46-o67cx-p3ddo-vzb27-o3cp4-owe4v-u4r3g-htj3u-4v2h6-sne4q-wqe
  Module Hash: 0x7871ed8328b75f26a51a1587545a67a15a9b9fecc1a1cbfba8a7df5233c8a724

Canister Name: sample_snapshot_indexer_icp
  Canister Id: {"local": "by6od-j4aaa-aaaaa-qaadq-cai"}
  Controllers: 7fpuj-hqaaa-aaaal-acg7q-cai bnz7o-iuaaa-aaaaa-qaaaa-cai yuk46-o67cx-p3ddo-vzb27-o3cp4-owe4v-u4r3g-htj3u-4v2h6-sne4q-wqe
  Module Hash: 0xe5f2c9c5627f668cf52e8d975baf17c56bbab654f61d78a7396815db20c71d80


Jan 09 06:41:11.227 INFO Project 'price_oracle' deployed successfully
```

Okay, we have deployed the canisters successfully. Next, we will create canisters for our project.

## Update the project

Before updating the project, we need to know Chainsight's canister types. There are 7 canister types in Chainsight.

| Canister type | Description |
| --- | --- |
| Event Indexer | An indexer that indexes smart contract events on EVM blockchains |
| Snapshot Indexer EVM | An indexer that indexes smart contract states on EVM blockchains. You can call the smart contract's view function from this canister and store the result. |
| Snapshot Indexer HTTPS | An indexer that indexes data from an external API. You can call the GET Web API from this canister and store the result. |
| Snapshot Indexer ICP | An indexer that indexes data from other chainsight canisters. You can call other chainsight canister's function from this canister and store the result. |
| Algorithm Indexer | An indexer that indexes data from other canisters. You can call other chainsight canister's function from this canister and store the result. |
| Algorithm Lens | A canister that provides a view function for other chainsight canisters. You can call this canister's view function from other chainsight canisters. You can implement any logic in this canister.|
| Relayer | A canister that relays data from other chainsight canisters to smart contracts on EVM blockchains. |

In this project, we will use the following canisters.

| Canister type | Usage |
| --- | --- |
| Snapshot Indexer EVM | To get the price of ETH from a mock chainlink contract on the Scroll Sepolia network |
| Algorithm Lens | To get the price from the Snapshot Indexer EVM canister and convert it into one with 18 digits |
| Relayer | To relay the price to the smart contract on the Sepolia network |


Scenario:

![Scenario](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/asq37o4csgj13m6xdj9l.png)


Let's update the project.
At first, we will delete the unnecessary canisters.
Canisters that we will deploy is defined in `project.yaml` and `components/*.yaml`. So lets' update/delete them.


project.yaml
``` yaml
version: v1
label: price_oracle
components:
- component_path: components/sample_event_indexer.yaml
- component_path: components/sample_algorithm_indexer.yaml
- component_path: components/sample_snapshot_indexer_evm.yaml
- component_path: components/sample_snapshot_indexer_icp.yaml
- component_path: components/sample_relayer.yaml
- component_path: components/sample_algorithm_lens.yaml
- component_path: components/sample_snapshot_indexer_https.yaml

```

Let's update the `project.yaml` like this.

``` yaml
version: v1
label: price_oracle
components:
- component_path: components/sample_snapshot_indexer_evm.yaml
- component_path: components/sample_algorithm_lens.yaml
- component_path: components/sample_relayer.yaml
```

note: `components/sample_algorith_lens.yaml` needs to be later than `components/sample_snapshot_indexer_evm.yaml` because `components/sample_algorithm_lens.yaml` uses `components/sample_snapshot_indexer_evm.yaml` as a datasource. Also, `components/sample_relayer.yaml` needs to be later than `components/sample_algorithm_lens.yaml` for the same reason.


And delete the following files.


```bash
rm components/sample_algorithm_indexer.yaml
rm components/sample_event_indexer.yaml
rm components/sample_snapshot_indexer_https.yaml
rm components/sample_snapshot_indexer_icp.yaml
rm -rf src/logics/*
```

Next, we will update the `components/sample_snapshot_indexer_evm.yaml` file.
We need to get data from [Mock price feed](https://sepolia.scrollscan.dev/address/0x9dA09CA893b089774DEb20AA1e375E613BA48cE9#code) at address `0x9dA09CA893b089774DEb20AA1e375E613BA48cE9` on the Scrol Sepolia network and need to call `latestAnswer()` function to get a mock price of ETH.
So let's update the file like this.

```yaml
# yaml-language-server: $schema=https://raw.githubusercontent.com/horizonx-tech/chainsight-cli/main/resources/schema/snapshot_indexer_evm.json
version: v1
metadata:
  label: Sample Snapshot Indexer Evm
  type: snapshot_indexer_evm
  description: ''
  tags:
  - ERC-20
  - Ethereum
  - DAI
datasource:
  location:
    id: 0x9dA09CA893b089774DEb20AA1e375E613BA48cE9 # Mock Chainlink price feed
    args:
      network_id: 534351 # Scroll sepolia network id
      rpc_url: https://sepolia-rpc.scroll.io
  method:
    identifier: latestAnswer():(uint256) # Call latestAnswer() function. 
    interface: MockOracle.json # Interface file. We can get it from https://sepolia.scrollscan.dev/address/0x9dA09CA893b089774DEb20AA1e375E613BA48cE9#code
    args: []
interval: 100 # Interval to call the function
cycles: null
```

We need a abi file for the mock price feed contract. So let's create a file named `MockOracle.json` in the `interfaces` directory and copy the abi from [here](https://sepolia.scrollscan.dev/address/0x9dA09CA893b089774DEb20AA1e375E613BA48cE9#code).
```json
[
    {
      "inputs": [],
      "name": "latestAnswer",
      "outputs": [
        {
          "internalType": "uint256",
          "name": "",
          "type": "uint256"
        }
      ],
      "stateMutability": "pure",
      "type": "function"
    }
  ]
```


Okay, it's done. Next, let's update the `sample_algorithm_lens`.
As a default, the datasource of the `sample_algorithm_lens` is `sample_snapshot_indexer_icp`. But we will use `sample_snapshot_indexer_evm` as a datasource. So let's update the `sample_algorithm_lens` like this.

```yaml
# yaml-language-server: $schema=https://raw.githubusercontent.com/horizonx-tech/chainsight-cli/main/resources/schema/algorithm_lens.json
version: v1
metadata:
  label: Sample Algorithm Lens
  type: algorithm_lens
  description: ''
  tags:
    - Price
    - Chainlink
datasource:
  methods:
  - id: sample_snapshot_indexer_evm # Use sample_snapshot_indexer_evm as a datasource
    identifier: 'get_last_snapshot_value : () -> (text)' # Function to get the last indexed value from the canister. You can find it in artifacts/sample_snapshot_indexer_evm.did.
    candid_file_path: null
cycles: null
```

And build the project again:

``` bash
csx build
```

Okay, next, le'ts implement the lens calculation function in `src/logics/sample_algorithm_lens.rs`.

As a default, the function is like this.


```rust
use sample_algorithm_lens_accessors::*;
#[derive(Clone, Debug, Default, candid :: CandidType, serde :: Deserialize, serde :: Serialize)]
pub struct LensValue {
    pub dummy: u64,
}
pub async fn calculate(targets: Vec<String>) -> LensValue {
    let _result = get_get_last_snapshot_value_in_sample_snapshot_indexer_evm(
        targets.get(0usize).unwrap().clone(),
    )
    .await;
    todo!()
}

```

We want to return the price of ETH as u128 and with 18 digits. So let's update the function like this.

```rust
use sample_algorithm_lens_accessors::*;
const PRECISION: u32 = 18;
pub type LensValue = u128;
pub async fn calculate(targets: Vec<String>) -> LensValue {
    let ethusd = get_get_last_snapshot_value_in_sample_snapshot_indexer_evm(targets.get(0usize).unwrap().clone())
        .await
        .unwrap()
        .parse::<u128>()
        .unwrap();
    format_ethusd(ethusd)
}

// The raw data is 8 digits precision, so we need to convert it into 18 digits
fn format_ethusd(ethusd: u128) -> u128 {
    ethusd * 10u128.pow(PRECISION - 8)
}

#[cfg(test)]
pub mod tests {
    use super::*;
    #[test]
    fn test_format_ethusd() {
        let ethusd = 2956575400000;
        let formated = format_ethusd(ethusd);
        assert_eq!(formated, 29565754000000000000000);
    }
}
```

It's almost done. Finally, we need to update the `sample_relayer` canister.


As a default, the `sample_relayer` canister has `sample_snapshot_indexer_evm` as a datasource. But we will use `sample_algorithm_lens` as a datasource. And, the destination network is polygon-mumbai. We need to update the `sample_relayer` like this.

```yaml
# yaml-language-server: $schema=https://raw.githubusercontent.com/horizonx-tech/chainsight-cli/main/resources/schema/relayer.json
version: v1
metadata:
  label: Sample Relayer
  type: relayer
  description: ""
  tags:
    - Oracle
    - Sepolia
datasource:
  location:
    id: sample_algorithm_lens # This is the id of the algorithm lens
  method:
    identifier: "get_result : (vec text) -> (nat)" # This is the function to get the result from the algorithm lens. You can find it in artifacts/sample_algorithm_lens.did.
    interface: null
    args: []
destination:
  network_id: 11155111 # This is the network id of the Sepolia network
  type: uint256
  oracle_address: 0xb6cC49fA578bb155F6811f735437c74495905C1d # This is the address of the oracle contract on the Sepolia network
  rpc_url: https://rpc.sepolia.org # This is the rpc url of the Sepolia network
  method_name: null
  interface: null
interval: 100 # This is the interval to relay the data to the Sepolia network
lens_targets:
  identifiers:
    - sample_snapshot_indexer_evm # This is the id of the snapshot indexer evm canister. The lens canister(Algorithm Lens) will get the data from this canister.
cycles: null


```

It's done. Let's build the project again.

```bash
csx build
```


## Deploy the project again

Re-run the dfx local network and deploy the project again.
```bash
dfx stop
dfx start --clean
```

log:
```
$ dfx start --clean
Running dfx start for version 0.15.1
```


```bash
csx deploy
```

You can see the log like this.

```bash
Jan 09 08:03:29.922 INFO Current deployed status:
Canister Name: __Candid_UI
  Canister Id: {"local": "br5f7-7uaaa-aaaaa-qaaca-cai"}
  Controllers: yuk46-o67cx-p3ddo-vzb27-o3cp4-owe4v-u4r3g-htj3u-4v2h6-sne4q-wqe
  Module Hash: 0x934756863c010898a24345ce4842d173b3ea7639a8eb394a0d027a9423c70b5c

Canister Name: sample_algorithm_lens
  Canister Id: {"local": "bkyz2-fmaaa-aaaaa-qaaaq-cai"}
  Controllers: 7fpuj-hqaaa-aaaal-acg7q-cai bnz7o-iuaaa-aaaaa-qaaaa-cai yuk46-o67cx-p3ddo-vzb27-o3cp4-owe4v-u4r3g-htj3u-4v2h6-sne4q-wqe
  Module Hash: 0xd19f567c8a783ca7b74c5756ae6e836657390d7e69c5f6d570ec9ebd4e03bd31

Canister Name: sample_relayer
  Canister Id: {"local": "bd3sg-teaaa-aaaaa-qaaba-cai"}
  Controllers: 7fpuj-hqaaa-aaaal-acg7q-cai bnz7o-iuaaa-aaaaa-qaaaa-cai yuk46-o67cx-p3ddo-vzb27-o3cp4-owe4v-u4r3g-htj3u-4v2h6-sne4q-wqe
  Module Hash: 0x327df98ec3bf2f07fb247362edd7ae4f53bef3a9a24abfdc2333900852766b2c

Canister Name: sample_snapshot_indexer_evm
  Canister Id: {"local": "be2us-64aaa-aaaaa-qaabq-cai"}
  Controllers: 7fpuj-hqaaa-aaaal-acg7q-cai bnz7o-iuaaa-aaaaa-qaaaa-cai yuk46-o67cx-p3ddo-vzb27-o3cp4-owe4v-u4r3g-htj3u-4v2h6-sne4q-wqe
  Module Hash: 0x36cfd3c2816d7a27d8d1cfd77953c5404c1c87e42b78363d4114c65b86384fd3


Jan 09 08:03:29.922 INFO Project 'price_oracle' deployed successfully
```

## Run the project

So let's run the project.
To run the project locally, we need to run [chainsight-management-canisters](https://github.com/horizonx-tech/chainsight-management-canisters.git) locally. To run the canisters, run the following command.

```bash
git clone https://github.com/horizonx-tech/chainsight-management-canisters.git
cd chainsight-management-canisters
make local port=${YOUR_DFX_PORT} # The port can be seen in the dfx start log. In this case, it's 39749.
cd ..
```

This may take a few minutes. After the canisters are deployed, run the following command to run the canisters locally. This also may take a few minutes.

```bash
csx exec
```

## Supply the SepoliaETH to the relayer

To relay the data to the Sepolia network, we need to supply the SepoliaETH to the relayer canister. The source EVM account address is determined when the relayer canister is deployed. So at first, let's get the address.
```bash
cd artifacts
dfx canister call sample_relayer get_ethereum_address
```

You can see the address like this
```bash
$ dfx canister call sample_relayer get_ethereum_address
("0xe43e266dce52a16be2ae6ad6d10dacf59fc4d659")
```
This address is the sender address of the relayer canister. Send some SepoliaETH to the address on the Sepolia network. It's sufficient to send about 0.1 SepoliaETH for 1 transaction.
The key of the address is generated by ICP's chain key criptography. So you can't get the private key of the address. If you want to know more about the chain key criptography, please check [here](https://internetcomputer.org/how-it-works/threshold-ecdsa-signing/).


## Get the price from the destination contract

Then, let's get the price from the destination contract on the Sepolia network.
https://sepolia.etherscan.io/address/0xb6cC49fA578bb155F6811f735437c74495905C1d#readContract
Read `readAsUint256` with the `sample_relayer` address as a parameter.


![Read contract](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yqtt454ofds8rksvot6b.png)


You will see ` 10000000000000000000000000000` as a result.
This is the relayed price from the mock price feed contract on the Scroll Sepolia network.


## Conclusion

In this article, we have created an EVM oracle with Chainsight. We have created a project that gets the price of ETH from a mock chainlink contract on the Scroll Sepolia network and relay it to the Sepolia network.
A complete example can be found [here](https://github.com/hide-yoshi/evm-price-oracle). Please check it out.
As we can see, Chainsight is a very powerful tool for creating an secure and reliable oracle. And it's very easy to use. I hope this article will help you to create your own oracle.
Let's enjoy building a defi project with Chainsight!


## Links
- [Chainsight](https://chainsight.network/)
- [Internet Computer](https://dfinity.org/)
- [Chain key criptography](https://internetcomputer.org/how-it-works/threshold-ecdsa-signing/)
- [Chainsight (Twitter)] (https://twitter.com/Chainsight_)
- [My personal Twitter account] (https://twitter.com/_hide_yoshi_)