Course Content

Chapter 0: Introduction



Chapter 2: How to use



Chapter 2

How to use

Estimated time: 1 hr 30 mins


How to import pricefeed module to your Cosmos SDK app

This chapter serves as a brief guide on how to utilize the pricefeed module in your Cosmos SDK app.

Prerequisites

Be sure you have met the prerequisites before you follow this tutorial.

Operating systems

  • Ubuntu 22.04

Go (for ignite and consumer chain)

  • 1.19.5 or higher

Ignite CLI

  • 0.27.2 or higher

Rust (for Hermes Relayer)

  • 1.65.0 or higher

Installation

Installing build-essential package in Ubuntu
sudo apt update && sudo apt install build-essential
Creating a new blockchain

To create a new blockchain project with Ignite, you will need to run the following command:

ignite scaffold chain example

The ignite scaffold chain command will create a new blockchain in a new directory example.

Config proposal voting period

To expedite the testing of the pricefeed module, modify the default voting period to 40 seconds by incorporating this code in example/config.yml.

...
genesis:
  app_state:
    gov:
      params:
        voting_period: "40s"
Step 1: Import pricefeed module to your cosmos app
Install pricefeed package
go install github.com/bandprotocol/oracle-consumer
Add pricefeed in the proposal handler
import (
    ...
    pricefeedclient "github.com/bandprotocol/oracle-consumer/x/pricefeed/client"
)

func getGovProposalHandlers() []govclient.ProposalHandler {
    var govProposalHandlers []govclient.ProposalHandler
    
    govProposalHandlers = append(
        ...
        pricefeedclient.ProposalHandler,
    )

    return govProposalHandlers
}
Add pricefeed module basic
import (
    ...
    pricefeed "github.com/bandprotocol/oracle-consumer/x/pricefeed"
)

ModuleBasics = module.NewBasicManager(
    ...
    pricefeed.AppModuleBasic{},
)
Add pricefeed keeper
import (
    ...
    pricefeedkeeper "github.com/bandprotocol/oracle-consumer/x/pricefeed/keeper"
)

type BandApp struct {
    ...
    scopedpricefeedKeeper capabilitykeeper.ScopedKeeper
    pricefeedKeeper pricefeedkeeper.Keeper
}
Add pricefeed store key
keys := sdk.NewKVStoreKeys(
    ...
    pricefeedtypes.StoreKey,
)
Add pricefeed keeper in the app
scopedpricefeedKeeper := app.CapabilityKeeper.ScopeToModule(pricefeedtypes.ModuleName)
app.scopedpricefeedKeeper = scopedpricefeedKeeper
app.pricefeedKeeper = *pricefeedkeeper.NewKeeper(
    appCodec,
    keys[pricefeedtypes.StoreKey],
    keys[pricefeedtypes.MemStoreKey],
    app.GetSubspace(pricefeedtypes.ModuleName),
    app.IBCKeeper.ChannelKeeper,
    &app.IBCKeeper.PortKeeper,
    scopedpricefeedKeeper,
)
Create pricefeed module
pricefeedModule := pricefeedmodule.NewAppModule(appCodec, app.pricefeedKeeper, app.AccountKeeper, app.BankKeeper)
pricefeedIBCModule := pricefeedmodule.NewIBCModule(app.pricefeedKeeper)
Add pricefeed in governance Handler router
govRouter.
    AddRoute(...).
    AddRoute(pricefeedtypes.RouterKey, pricefeedmodule.NewUpdateSymbolRequestProposalHandler(app.pricefeedKeeper))
Add pricefeed in the module manager
app.mm = module.NewManager(
	...,
	pricefeedModule,
)
Set pricefeed order in begin block, end block and init genesis
app.mm.SetOrderBeginBlockers(
    ...,
    pricefeedtypes.ModuleName,
)

app.mm.SetOrderEndBlockers(
    ...,
    pricefeedtypes.ModuleName,
)

app.mm.SetOrderInitGenesis(
    pricefeedtypes.ModuleName,
)
Define the order of the pricefeed for deterministic simulations
app.sm = module.NewSimulationManager(
    ...
    pricefeedModule,
)
Add pricefeed subspace in params Keeper
func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino, key, tkey storetypes.StoreKey) paramskeeper.Keeper {
	paramsKeeper := paramskeeper.NewKeeper(appCodec, legacyAmino, key, tkey)

	paramsKeeper.Subspace(...)
	paramsKeeper.Subspace(pricefeedmoduletypes.ModuleName)
	// this line is used by starport scaffolding # stargate/app/paramSubspace

	return paramsKeeper
}

You have completed importing the pricefeed module and can now execute the chain by running this command :tada:

ignite chain serve -v
Step 2: Setup a relayer

The second step is to set up a relayer to listen and relay IBC packets between a your chain and BandChain.

Here are the simple guides for setting up a relayer.

This tutorial will use the Hermes relayer by adhering to the following steps.

Step 2.1: Build hermes for BandChain

# Clone Hermes version 1.4.0
git clone https://github.com/informalsystems/hermes.git
cd hermes
git checkout v1.4.0


# Build Hermes
cargo build --release

Step 2.2: Create config_relayer.toml

# hermes/
touch config_relayer.toml

add the Hermes config in config_relayer.toml

# The global section has parameters that apply globally to the relayer operation.
[global]

# Specify the verbosity for the relayer logging output. Default: 'info'
# Valid options are 'error', 'warn', 'info', 'debug', 'trace'.
log_level = 'trace'


# Specify the mode to be used by the relayer. [Required]
[mode]

# Specify the client mode.
[mode.clients]

# Whether or not to enable the client workers. [Required]
enabled = true

# Whether or not to enable periodic refresh of clients. [Default: true]
# Note: Even if this is disabled, clients will be refreshed automatically if
#      there is activity on a connection or channel they are involved with.
refresh = true

# Whether or not to enable misbehaviour detection for clients. [Default: false]
misbehaviour = true

# Specify the connections mode.
[mode.connections]

# Whether or not to enable the connection workers for handshake completion. [Required]
enabled = true

# Specify the channels mode.
[mode.channels]

# Whether or not to enable the channel workers for handshake completion. [Required]
enabled = true

# Specify the packets mode.
[mode.packets]

# Whether or not to enable the packet workers. [Required]
enabled = true

# Parametrize the periodic packet clearing feature.
# Interval (in number of blocks) at which pending packets
# should be eagerly cleared. A value of '0' will disable
# periodic packet clearing. [Default: 100]
clear_interval = 100

# Whether or not to clear packets on start. [Default: false]
clear_on_start = true

# Toggle the transaction confirmation mechanism.
# The tx confirmation mechanism periodically queries the `/tx_search` RPC
# endpoint to check that previously-submitted transactions
# (to any chain in this config file) have delivered successfully.
# Experimental feature. Affects telemetry if set to false.
# [Default: true]
tx_confirmation = true

# The REST section defines parameters for Hermes' built-in RESTful API.
# https://hermes.informal.systems/rest.html
[rest]

# Whether or not to enable the REST service. Default: false
enabled = true

# Specify the IPv4/6 host over which the built-in HTTP server will serve the RESTful
# API requests. Default: 127.0.0.1
host = '127.0.0.1'

# Specify the port over which the built-in HTTP server will serve the restful API
# requests. Default: 3000
port = 3000


# The telemetry section defines parameters for Hermes' built-in telemetry capabilities.
# https://hermes.informal.systems/telemetry.html
[telemetry]

# Whether or not to enable the telemetry service. Default: false
enabled = true

# Specify the IPv4/6 host over which the built-in HTTP server will serve the metrics
# gathered by the telemetry service. Default: 127.0.0.1
host = '127.0.0.1'

# Specify the port over which the built-in HTTP server will serve the metrics gathered
# by the telemetry service. Default: 3001
port = 3001

[[chains]]
id = 'example'
rpc_addr = 'http://localhost:26657'
grpc_addr = 'http://localhost:9090'
websocket_addr = 'ws://localhost:26657/websocket'
rpc_timeout = '10s'
account_prefix = 'cosmos'
key_name = 'requester'
store_prefix = 'ibc'
default_gas = 5000000
max_gas = 15000000
gas_price = { price = 0, denom = 'ustake' }
gas_multiplier = 1.1
max_msg_num = 20
max_tx_size = 209715
clock_drift = '20s'
max_block_time = '10s'
trusting_period = '10days'
trust_threshold = { numerator = '1', denominator = '3' }
address_type = { derivation = 'cosmos' }
ignore_port_channel = []
# [chains.packet_filter]
# policy = 'allow'
# list = [
#    ['wasm.*', '*'],
# ]

[[chains]]
id = 'band-laozi-testnet6'
rpc_addr = 'https://rpc.laozi-testnet6.bandchain.org:443'
grpc_addr = 'https://laozi-testnet6.bandchain.org:443'
websocket_addr = 'wss://rpc.laozi-testnet6.bandchain.org:443/websocket'
rpc_timeout = '10s'
account_prefix = 'band'
key_name = 'testkey'
store_prefix = 'ibc'
default_gas = 100000
max_gas = 10000000
gas_price = { price = 0.0025, denom = 'uband' }
gas_multiplier = 1.1
max_msg_num = 30
max_tx_size = 2097152
clock_drift = '5s'
max_block_time = '10s'
trusting_period = '14days'
trust_threshold = { numerator = '1', denominator = '3' }
address_type = { derivation = 'cosmos' }
ignore_port_channel = []
# [chains.packet_filter]
# policy = 'allow'
# list = [
#    ['oracle', '*'],
# ]

Step 2.3: Add the relayer key

create a mnemonic file on the example chain and BandChain

Note: Upon logging for the first time that you run example chain, you will be given either Alice's or Bob's mnemonic, which can be used as the consumer mnemonic. It's important to note that you need to have funds in your key in order to send transactions on each chain.

# hermes/
touch mem-example.txt
  • add your example chain mnemonic in mem-example.txt
# hermes/
touch mem-band.txt
  • add your BandChain mnemonic in mem-band.txt
add keys to Hermes by following the command
example chain key
target/release/hermes --config config_relayer.toml keys add --chain example --mnemonic-file "mem-example.txt"
and BandChian key
target/release/hermes --config config_relayer.toml keys add --chain band-laozi-testnet6 --mnemonic-file "mem-band.txt"  --hd-path "m/44'/494'/0'/0/0"

Create client connection

target/release/hermes --config config_relayer.toml create channel --a-chain band-laozi-testnet6 --b-chain example --a-port oracle --b-port pricefeed --order unordered --channel-version bandchain-1 --new-client-connection

Start Hermes relayer

target/release/hermes --config config_relayer.toml start
Step 3: Open update symbol request proposal and vote

The purpose of this proposal is to request price data from BandChain at block_interval specified in the proposal. If the proposal is approved, the pricefeed module will retrieve the data and store the response on the consumer chain.

create proposal.json

{
    "title": "Update Symbol requests",
    "description": "Update symbol that request price from BandChain",
    "symbol_requests": [
        {
            "symbol": "BTC",
            "oracle_script_id": "396",
            "block_interval": "40"
        },
        {
            "symbol": "ETH",
            "oracle_script_id": "396",
            "block_interval": "40"
        }
    ],
    "deposit": "10000000stake"
}

Submit proposal

exampled tx gov submit-legacy-proposal update-symbol-request proposal.json --from alice

Vote the proposal

exampled tx gov vote 1 yes --from alice
exampled tx gov vote 1 yes --from bob

Check proposal status

exampled query gov proposals
Query the latest price that got from BandChain

Once the proposal has been approved, the pricefeed module will query BTC and ETH from BandChain every 40 blocks on your chain, and you can view the latest price by executing this command.

exampled query pricefeed price BTC

Previous Chapter

Pricefeed overview