A Practical Guide to Unchained: Index Events and Token Prices on Any EVM Chain

Pouya Eghbali
7 min readFeb 27, 2024

Unchained is a decentralized network for creating, indexing, validating, analyzing, and processing data. It can run cooperatively with multiple peers who join forces and attest to data validity or process it as a grid.

It can also run independently or as a part of a bigger architecture. In either mode, it can be used to index data, relay messages from one chain to another, or run computationally heavy tasks off-chain and deliver the results to the blockchain.

We introduced Unchained earlier this year in this blog post. However, the focus was mainly on the core concepts of the project rather than a practical introduction. If you want to get your hands dirty and start building with Unchained, this article is for you.

Though Unchained is not stable and production-ready yet, plenty of its features can be used for a test drive. This article highlights the features available in v0.11.12 of Unchained, the latest version when writing this article.

Pre-requisites

Unchained has official Docker releases, but not all features are exposed in the Docker version. Since this article is aimed at developers, let’s go bleeding edge: download the latest Unchained release from GitHub and move it to $PATH:

wget https://github.com/KenshiTech/unchained/releases/download/v0.11.12/unchained.linux.amd64
chmod +x unchained.linux.amd64
mv unchained.linux.amd64 /usr/local/bin/unchained

Hint: Replace linux with macos or windows in the above command if you’re not on Linux. Replace amd64 with arm64 if you are using an ARM CPU. You may need to modify the commands for Windows.

You’ll also need PostgreSQL. The choice is yours: set it up locally, go with Docker, or use a DBaaS service. Once you have these, you’re ready to move to the next step.

Starting Simple: Index the KNS Token Price

We will use worker mode to index the price of the KNS token. Let’s write a config file that tells the Unchained client we are interested in doing that:

rpc:
arbitrum:
- https://1rpc.io/arb

plugins:
uniswap:
schedule:
arbitrum: 100

tokens:
- name: kenshi
chain: arbitrum
pair: "0x68c685fd52a56f04665b491d491355a624540e85"
delta: 0
invert: true
unit: ETH

Here is a quick explanation of each of the above options, leaving out the more obvious options:

  • rpc.arbitrum : A list of RPC endpoints for Arbitrum. Provide multiple options to auto-rotate. Unchained works with any EVM chain.
  • plugins.uniswap.schedule.arbitrum : Specifies the frequency of requests to the Arbitrum network. Here, we are telling Unchained to look for new data on Arbitrum every 100 milliseconds.
  • plugins.uniswap.schedule.tokens : A list of tokens that have liquidity on UniSwap V3. Support for more exchanges and protocol versions will be added in future releases.
  • plugins.uniswap.schedule.tokens.$.delta : If the tokens in the provided pair have different decimal places, the delta between them should be configured here.
  • plugins.uniswap.schedule.tokens.$.pair : UniSwap V3 Pool address.
  • plugins.uniswap.schedule.tokens.$.invert : Our pair is an ETH <> KNS pool; it gives us the price of ETH in KNS. Setting invert to true means we want an inverse calculation here, meaning we want the KNS price in ETH, not the ETH price in KNS. See this link for more information.

Save the above config in a file named conf.yaml, then cd to the directory where the file is located, and run:

unchained worker

You should see an output similar to this picture:

KNS price in ETH

Pretty cool. But what if we want the KNS price in USD and not ETH? The UniSwap plugin in Unchained supports crossing prices from multiple sources. Modify your config file to the following and restart your node:

rpc:
arbitrum:
- https://1rpc.io/arb
ethereum:
- https://1rpc.io/eth

plugins:
uniswap:
schedule:
arbitrum: 100
ethereum: 5000

tokens:
- id: ethereum
name: ethereum
chain: ethereum
pair: "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640"
delta: 12
invert: true
unit: USDT

- name: kenshi
chain: arbitrum
pair: "0x68c685fd52a56f04665b491d491355a624540e85"
delta: 0
invert: true
unit: USDT
cross:
- ethereum

In the config above, we are telling Unchained to get the USD price of ETH from the ETH <> USD pool on the Ethereum blockchain. We are also instructing Unchained to cross the KNS price with ETH <> USD to get the USD price of KNS. If you run Unchained again, you’ll see the KNS price in USD:

KNS price in USDT

Storing and Querying

Only displaying the KNS price is not going to be very useful. Let’s store the price in our Postgres database and query it with GraphQL! Unchained supports standalone indexing and multi-node indexing modes. In this article, we’ll set up Unchained in multi-node indexing mode, but only with one worker.

To do that, we need a broker. You might have noticed that we launched the unchained CLI tool with worker command. Workers do the actual work: indexing, validating, and processing. Brokers, on the other hand, handle network messages, data consumers, and storage.

Let’s create a new config file named conf.broker.yaml with the following content:

rpc:
arbitrum:
- https://1rpc.io/arb

database:
url: postgresql://user:password@host/dbName

plugins:
uniswap:
tokens:
- name: kenshi
chain: arbitrum
pair: "0x68c685fd52a56f04665b491d491355a624540e85"
delta: 0
invert: true
unit: USDT

We can run Unchained in broker mode using the following command:

unchained broker -c conf.broker.yaml

Now, we should instruct our worker node to connect to the broker. We should modify our worker configuration (conf.yaml) to the following:

rpc:
arbitrum:
- https://1rpc.io/arb
ethereum:
- https://1rpc.io/eth

broker:
uri: ws://localhost:9123

plugins:
uniswap:
schedule:
arbitrum: 100
ethereum: 5000

tokens:

- id: ethereum
name: ethereum
chain: ethereum
pair: "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640"
delta: 12
invert: true
unit: USDT

- name: kenshi
chain: arbitrum
pair: "0x68c685fd52a56f04665b491d491355a624540e85"
delta: 0
invert: true
unit: USDT
send: true
cross:
- ethereum

Notice the plugins.uniswap.tokens.send option that tells Unchained we want to send the price of this token to the broker. Running the Unchained client in worker mode now should give us the following:

KNS price is being sent to our broker

Awesome! Now, let’s query the indexed data. When you run the Unchained client in broker mode, you get two HTTP endpoints exposed:

  1. /gql/query : A GraphQL API endpoint to query the data using your favorite GraphQL client.
  2. /gql : A GraphQL playground with code completion.

Head to http://localhost:9123/gql and you should see:

Unchained Playground

Let’s write a sample query and run it:

Querying the KNS Price in the Unchained GraphQL Playground

Indexing the DAI Smart Contract Transfer Events

Unchained also allows you to index any event emitted from any smart contract on any EVM chain! Support for non-EVM chains will be added in future releases. Let’s see how we can index the Transfer events of the DAI smart contract. Modify your worker config file to the following:

rpc:
arbitrum:
- https://1rpc.io/arb
ethereum:
- https://1rpc.io/eth

broker:
uri: ws://localhost:9123

plugins:
logs:
schedule:
ethereum: 5000

events:
- name: DAI
chain: ethereum
abi: ./abi/ERC20.json
event: Transfer
address: "0x6B175474E89094C44Da98b954EedeAC495271d0F"
send: true

uniswap:
schedule:
arbitrum: 100
ethereum: 5000

tokens:

- id: ethereum
name: ethereum
chain: ethereum
pair: "0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640"
delta: 12
invert: true
unit: USDT

- name: kenshi
chain: arbitrum
pair: "0x68c685fd52a56f04665b491d491355a624540e85"
delta: 0
invert: true
unit: USDT
send: true
cross:
- ethereum

Note the plugins.logs.events.$.abi option above; the ABI of the smart contract, resolved from the current working directory. You can get the ERC20 abi from here. You should add the same event in your broker config so your broker knows which incoming events it is allowed to store:

rpc:
arbitrum:
- https://1rpc.io/arb
ethereum:
- https://1rpc.io/eth

database:
url: postgresql://user:password@host/dbName

plugins:
logs:
events:
- name: DAI
chain: ethereum
abi: ./abi/ERC20.json
event: Transfer
address: "0x6B175474E89094C44Da98b954EedeAC495271d0F"

uniswap:
tokens:
- name: kenshi
chain: arbitrum
pair: "0x68c685fd52a56f04665b491d491355a624540e85"
delta: 0
invert: true
unit: USDT

Restart both the broker and the worker nodes, and you’ll have the following:

Unchained worker output for DAI events

You can query the indexed DAI data like this:

DAI data was queried with GraphQL using Unchained Playground

Multi-Worker Mode

The Unchained broker can accept data from multiple workers. What’s the point of that? Sometimes, you’d need to aggregate data from numerous sources or create a network of peers who read data from a specific source, analyze it, and attest to its correctness.

The multi-worker mode comes into play in these specific types of situations. Currently, on the Unchained testnet, more than 350 validators are indexing and attesting to the price of Bitcoin, Ethereum, and Arbitrum tokens. However, the functionality will not be limited to only token prices.

One use case of this would be sending data on-chain and verifying the signatures of all those who attested to the correctness of data. This way, super secure cross-chain bridges or oracles can be created.

The Future: Our Next Milestones

As I mentioned earlier, Unchained isn’t production-ready yet. We have a few milestones before we can declare the project stable:

  1. Separation of functions: We are working to separate various functionalities of Unchained into separate modules. For example, we will remove storage and GraphQL functionalities from the broker mode and add them to their independent consumer modules. This way, custom consumers can store data in Postgres, MongoDB, IPFS, ICP, or EVM.
  2. Multi-broker mode: Earlier versions of Unchained used a p2p networking model. The newer versions are federated. Unchained itself is decentralized, but its networking model isn’t yet. We want to change that before releasing v1.0.0.
  3. Bugs, FIXME, and TODOs: We are developing Unchained in a crunch. Multiple areas in the code are tagged with FIXME and TODO notations. We aren’t handling errors properly either; you’ll see a lot of panic in the code, which needs to be replaced.

Join the Community

Check the source code on GitHub and give it a star, or join our Telegram group and discuss the project with other developers like yourself. We’d be glad to answer any of your questions!

--

--

Pouya Eghbali

Founder at Kenshi, Clio programming language, and CTO at Equip.