This example demonstrates the functionality of the Laminar chain’s oracle pallet including the aggregation of price values.
make init
will setup the environment by downloading and installing all necessary dependencies and the required toolchain.
make build
builds the chain’s code so we can execute it.
After that copy the chainSpec.json
file from this repository into the chain’s directory. More on the spec file will be explained a little bit later.
/tmp
for each node (its base directory). If the base directory doesn’t exist it will be created but it is necessary to purge all data of a specific node on subsequent launches:
y
.
Bootstrap the network
We are going to launch Alice’s node first:
--alice
will make Alice the node’s authority uses her predefined key pair.
bootnodes
parameter which specifies the network and identity information of Alice’s node.
CurrencyId
as key type. That means it can be one of the predefined values FBTC, FETH, FEUR, etc. The value uses the type Price
.
In this example we are going to use an off-chain worker to feed currency prices and fetch them afterwards.
Our off-chain worker
The component we create to simulate new price reports and oracle queries lives off-chain and will be written in Typescript. It is completely separate from anything stored on the Laminar chain itself. We rather interact with the chain through specific calls made using the Polkadot API for JavaScript. This API defines the technical aspects of how we can interact with the chain (and specifically with the oracle), i. e. how to build transactions and make RPCs. For the specific parameters regarding the Laminar chain we also make use of the Laminar chain JS SDK.
Let’s start by switching the directory
package.json
file:
index.ts
. We import the required types
//[Name]
. We derive those key pairs from the keyring we create specifying the key pair format SR25519.
api.tx.
the code completion shows us the available extrinsics but the oracle isn’t one of them. That’s because the default methods of the Substrate runtime are known at compile time but not the custom chain specific extrinsics that might be part of the runtime. Let’s find out what’s really available by querying the node:
laminarOracle
. The list tells us even more about the oracle: the available query method feedValues
. This is exactly what we needed to know.x
Now Alice reports a current BTC price of 1, i. e. she feeds the oracle with the corresponding key-value pair:
tx
for transaction). The available extrinsics depend on the used chain. In this case we address the oracle pallet of the laminar chain which is addressed by laminarOracle
.
feedValues
in order to submit key-value pairs that should be fed into the oracle. In this case we just submit a single key-value pair for the Bitcoin price.
oracle_getAllValues
and oracle_getValue
. That means we have the two functions getAllValues
and getValue
available for the RPC oracle.
Now we query the current BTC price:
oracle
) and query the value to the key associated with the Bitcoin price (which is FBTC
). The output is a pair of two values: The current price and the timestamp of the transaction that contained the feeding operation.
That’s it! Now we can run the program again with yarn start
. The oracle will be fed and queried and the output is the expected BTC price of 1.
Data aggregation
In the previous section we fed the oracle with one single data point and queried the oracle to retrieve a value. It isn’t surprising at all that we read the value that we had written into the oracle just before. But it requires a closer look to what value is actually returned by getValue
. The oracle pallet states that this method returns a “combined value” which indicates that there is some data aggregation happening.
In fact the way the prices of each currency are aggregated is entirely up to the chain implementation. Currently the Laminar chain doesn’t implement any aggregation algorithm but the oracle pallet comes with a default implementation: It takes all fed values of a specific currency submitted within the last 3 days and returns the median value. If there aren’t any values to calculate the median from it just returns the most recent value.
Let’s try it out. Disable the lines of the previous section that interacted with the oracle (via tx and RPC) and add the following:
feedPrice
which takes a price value and a sender and triggers the transaction accordingly. We use the subscription API of Polkadot JS in order to make sure that we wait for every transaction to be finalized in a block. Now our three users submit their values: