BandChain oracle
The BandChain oracle communication module has built-in compliance using IBC protocol that can query data points of various types from BandChain.
Other chains can query this oracle module for real-time information.
BandChain has multiple scripts deployed into the network. You can request any data using the script id.
High-level overview
Steps to scaffold an IBC BandChain query oracle to request real-time data from BandChain scripts in a specific IBC-enabled Cosmos SDK module.
IBC module packet scaffold
BandChain oracle queries can be scaffolded only in IBC modules.
The basic syntax to scaffold a band oracle module is:
ignite scaffold band [queryName] --module [moduleName]
Customize your band oracle with flags:
- --module string - name of the new IBC Module to add the packets to
- --path string - path of the app, default is the current directory (
"."
) - --signer string - signer label, default is
creator
Acknowledgement messages
The BandChain oracle returns the ack messages with the request id. The last request id is saved for future queries.
Files and directories
When you scaffold a BandChain oracle module, the following files and directories are created and modified:
proto
: oracle request and response data.x/module_name/keeper
: IBC hooks, gRPC message server.x/module_name/types
: message types, IBC events.x/module_name/client/cli
: CLI command to broadcast a transaction containing a message with a packet.x/module_name/oracle.go
: BandChain oracle packet handlers.
Scaffold a BandChain oracle chain
First, scaffold a chain but don't scaffold a default module:
ignite scaffold chain oracle --no-module
Next, change to the new oracle
directory and scaffold an IBC-enabled module named consuming
:
cd oracle
ignite scaffold module consuming --ibc
Finally, scaffold a BandChain query oracle that can request real-time data:
ignite scaffold band coinRates --module consuming
So far, you have scaffolded:
- A new
oracle
chain without a default module - A new IBC-enabled
consuming
module - A new
coinRates
BandChain query oracle
Now it's time to change the data.
Update version
The output of the ignite scaffold band coinRates --module consuming
command prompts you to update the keys.go
file.
In the x/consuming/types/keys.go
file, update the Version
variable in the const
block to the required version that the IBC module supports:
const (
// ...
// Version defines the current version the IBC module supports
Version = "bandchain-1"
// ...
)
Start your chain in development
To run the chain from the oracle
directory:
ignite chain serve
Keep this terminal window open.
Configure and connect the Ignite CLI relayer
If you previously used the Ignite CLI relayer, it is a good idea to remove existing relayer and Ignite CLI configurations:
Stop your blockchains.
Delete previous configuration files:
rm -rf ~/.ignite/relayer
Restart your blockchains.
In another terminal tab, configure the Ignite CLI relayer:
ignite relayer configure -a \
--source-rpc "http://rpc-laozi-testnet4.bandchain.org:80" \
--source-faucet "https://laozi-testnet4.bandchain.org/faucet" \
--source-port "oracle" \
--source-gasprice "0uband" \
--source-gaslimit 5000000 \
--source-prefix "band" \
--source-version "bandchain-1" \
--target-rpc "http://localhost:26657" \
--target-faucet "http://localhost:4500" \
--target-port "consuming" \
--target-gasprice "0.0stake" \
--target-gaslimit 300000 \
--target-prefix "cosmos" \
--target-version "bandchain-1"
When prompted, press Enter to accept the default source and target accounts.
The command output confirms the relayer is successfully configured:
? Source Account default
? Target Account default
🔐 Account on "source" is default(band1dscvlx0mhpys9fazuk7ej9z4cq7qknzn09pjpq)
|· received coins from a faucet
|· (balance: 10000000uband)
🔐 Account on "target" is default(cosmos1dscvlx0mhpys9fazuk7ej9z4cq7qknznk2pseg)
|· received coins from a faucet
|· (balance: 100000stake,5token)
⛓ Configured chains: band-laozi-testnet4-oracle
Connect the relayer:
ignite relayer connect
You can see the paths of the oracle
port on the testnet and the consuming
port on your local oracle module in the relayer connection status that is output to the terminal:
------
Paths
------
band-laozi-testnet4-oracle:
band-laozi-testnet4 > (port: oracle) (channel: channel-405)
oracle > (port: consuming) (channel: channel-0)
------
Listening and relaying packets between chains...
------
Leave this terminal tab open so you can monitor the relayer.
Make a request transaction
In another terminal tab, use the oracled
binary to make a request transaction. Because BandChain has multiple scripts already deployed into the network, you can request any data using the BandChain script id. In this case, use script 37 for Coin Rates:
# Coin Rates (script 37 into the testnet)
oracled tx consuming coin-rates-data 37 4 3 --channel channel-0 --symbols "BTC,ETH,XRP,BCH" --multiplier 1000000 --fee-limit 30uband --prepare-gas 600000 --execute-gas 600000 --from alice --chain-id oracle
You can check the last request id that was returned by ack:
oracled query consuming last-coin-rates-id
# output: request_id: "101276"
Now you can check the data by request id to receive the data packet:
oracled query consuming coin-rates-result 101276
Multiple oracles
You can scaffold multiples oracles by module. After scaffold, you must change the Calldata
and Result
parameters in the proto file moduleName.proto
and then adapt the request in the cli/client/tx_module_name.go
file.
To create an example for the gold price bridge:
ignite scaffold band goldPrice --module consuming
In the proto/consuming/gold_price.proto
file:
syntax = "proto3";
package oracle.consuming;
option go_package = "oracle/x/consuming/types";
message GoldPriceCallData {
uint64 multiplier = 2;
}
message GoldPriceResult {
uint64 price = 1;
}
In the x/consuming/cli/client/tx_gold_price.go
file:
package cli
import (
"strconv"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/spf13/cobra"
"oracle/x/consuming/types"
)
// CmdRequestGoldPriceData creates and broadcast a GoldPrice request transaction
func CmdRequestGoldPriceData() *cobra.Command {
cmd := &cobra.Command{
Use: "gold-price-data [oracle-script-id] [requested-validator-count] [sufficient-validator-count]",
Short: "Make a new GoldPrice query request via an existing BandChain oracle script",
Args: cobra.ExactArgs(3),
RunE: func(cmd *cobra.Command, args []string) error {
// retrieve the oracle script id.
uint64OracleScriptID, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
return err
}
oracleScriptID := types.OracleScriptID(uint64OracleScriptID)
// retrieve the requested validator count.
askCount, err := strconv.ParseUint(args[1], 10, 64)
if err != nil {
return err
}
// retrieve the sufficient(minimum) validator count.
minCount, err := strconv.ParseUint(args[2], 10, 64)
if err != nil {
return err
}
channel, err := cmd.Flags().GetString(flagChannel)
if err != nil {
return err
}
// retrieve the multiplier for the symbols' price.
multiplier, err := cmd.Flags().GetUint64(flagMultiplier)
if err != nil {
return err
}
calldata := &types.GoldPriceCallData{
Multiplier: multiplier,
}
// retrieve the amount of coins allowed to be paid for oracle request fee from the pool account.
coinStr, err := cmd.Flags().GetString(flagFeeLimit)
if err != nil {
return err
}
feeLimit, err := sdk.ParseCoinsNormalized(coinStr)
if err != nil {
return err
}
// retrieve the amount of gas allowed for the prepare step of the oracle script.
prepareGas, err := cmd.Flags().GetUint64(flagPrepareGas)
if err != nil {
return err
}
// retrieve the amount of gas allowed for the execute step of the oracle script.
executeGas, err := cmd.Flags().GetUint64(flagExecuteGas)
if err != nil {
return err
}
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
msg := types.NewMsgGoldPriceData(
clientCtx.GetFromAddress().String(),
oracleScriptID,
channel,
calldata,
askCount,
minCount,
feeLimit,
prepareGas,
executeGas,
)
if err := msg.ValidateBasic(); err != nil {
return err
}
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}
cmd.Flags().String(flagChannel, "", "The channel id")
cmd.MarkFlagRequired(flagChannel)
cmd.Flags().Uint64(flagMultiplier, 1000000, "Multiplier used in calling the oracle script")
cmd.Flags().String(flagFeeLimit, "", "the maximum tokens that will be paid to all data source providers")
cmd.Flags().Uint64(flagPrepareGas, 200000, "Prepare gas used in fee counting for prepare request")
cmd.Flags().Uint64(flagExecuteGas, 200000, "Execute gas used in fee counting for execute request")
flags.AddTxFlagsToCmd(cmd)
return cmd
}
Make the request transaction:
# Gold Price (script 33 into the testnet)
oracled tx consuming gold-price-data 33 4 3 --channel channel-0 --multiplier 1000000 --fee-limit 30uband --prepare-gas 600000 --execute-gas 600000 --from alice --chain-id oracle
Check the last request id that was returned by ack:
oracled query consuming last-gold-price-id
# output: request_id: "101290"
Request the package data:
oracled query consuming gold-price-result 101290