A Practical Guide to Unchained: Index Events and Token Prices on Any EVM Chain
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. Settinginvert
totrue
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:
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:
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:
Awesome! Now, let’s query the indexed data. When you run the Unchained client in broker mode, you get two HTTP endpoints exposed:
/gql/query
: A GraphQL API endpoint to query the data using your favorite GraphQL client./gql
: A GraphQL playground with code completion.
Head to http://localhost:9123/gql and you should see:
Let’s write a sample query and run it:
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:
You can query the indexed DAI data like this:
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:
- 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.
- 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.
- 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!