This repository contains a Rust implementation of the Band StandardReference
Soroban smart contract. The contract
provides functionality for querying prices (reference data) of supported symbols from the BandChain. It allows relayers
to update and retrieve reference data for the supported symbols
There are 2 key components in this repository:
src/contract.rs
: contains coreStandardReference
contract functionalityexamples/mock_consumer_contract/src/lib.rs
: contains an example implementation of a contract which uses the prices from theStandardReference
contract.
The StandardReferenceTrait
trait defines the contract interface, which includes the following functions:
init
: Initializes the contract with the given admin address and adds the admin address to the relayers list.upgrade
: Upgrades the contract to a new wasm code at the provided wasm hash.version
: Returns the version of the contract.current_admin
: Returns the current admin address.transfer_admin
: Transfers the admin role to the new admin address and revokes relayer status from the old admin.is_relayer
: Checks if the given address is a relayer.add_relayers
: Adds the given addresses to the relayers list.remove_relayers
: Removes the given addresses from the relayers list.relay
: Relays symbol rates to the contract. The caller must be a relayer.force_relay
: Forces relay of symbol rates to the contract. The caller must be a relayer.delist
: Delists symbols from the contract. The caller must be a relayer.get_ref_data
: Retrieves reference data for the specified symbols.get_reference_data
: Retrieves reference data for the specified symbol pairs.
The StandardReference
struct implements the StandardReferenceTrait
trait, providing the actual implementation for
the contract functions.
The code includes a set of unit tests in the tests
module. These tests cover various scenarios and verify the
correctness of the contract implementation. Here are some key test cases:
test_reinit
: Verifies that initializing the contract multiple times results in a panic.test_transfer_admin
: Tests the transfer of admin role to a new admin address.test_add_relayers
: Tests adding relayers to the contract.test_remove_relayers
: Tests removing relayers from the contract.test_relay
: Tests the relay of symbol rates by relayers and verifies the updated reference data.test_unauthorized_relay
: Ensures that unauthorized relayers cannot update the reference data.test_force_relay
: Tests the forced relay of symbol rates by relayers and verifies the updated reference data.test_delist
: Tests the delisting of symbols from the contract and verifies the removal of reference data.
There are three types of players: admin, relayer and consumer.
- The admin has the authority to assign and revoke admin and relayer roles from any addresses.
- The relayer is responsible for relaying the prices or data from BandChain to
StandardReference
contract via therelay
andforce_relay
functions. The admin and relayer roles will be maintained by Band Protocol. - The consumer retrieves the prices for supported symbols via the
get_ref_data
andget_reference_data
functions. Please refer toMockConsumer
contract for guidance.
The RefData
struct is used to store reference data for a specific symbol pair. It includes information such as the
rate, resolve time, and request ID for a specific symbol.
pub struct RefData {
pub rate: u64,
pub resolve_time: u64,
pub request_id: u64,
}
The RefData
struct has the following fields:
rate
: Represents the rate of the symbol against USD.resolve_time
: Represents the Unix time when the reference data was resolved.request_id
: Represents the ID of the request associated with the reference data.
This function is intended for obtaining reference data rate with Quote asset as USD.
fn get_ref_data(env: Env, symbols: Vec<Symbol>) ->
Result<Vec<RefData>, StandardReferenceError>
symbols
: A vector of symbols for which reference data is requested.
The function returns a Result
containing the reference data as a vector of RefData
objects if successful. If there
is an error during the execution of the function, an Err
variant of StandardReferenceError
is returned.
The ReferenceData
struct is similar RefData
but the quote asset can be specified. It includes information such as the
rate of the pair, and the last update times of the base and quote assets.
pub struct ReferenceData {
pub rate: u128,
pub last_updated_base: u64,
pub last_updated_quote: u64,
}
The ReferenceData
struct has the following fields:
rate
: Represents the rate of the symbol pair. For example, the rate of BTC/USD.last_updated_base
: Represents the Unix time when the base asset (e.g., BTC) was last updated.last_updated_quote
: Represents the Unix time when the quote asset (e.g., USD) was last updated.
This function is used for obtaining the reference data rate by specifying both Base and Quote assets.
fn get_reference_data(
env: Env,
symbol_pairs: Vec<(Symbol, Symbol)>,
) -> Result<Vec<ReferenceData>, StandardReferenceError>
symbol_pairs
: A vector of symbol pairs (Base and Quote assets) for which reference data is requested.
The function returns a Result
containing the reference data as a vector of ReferenceData
objects if successful. If
there is an error during the execution of the function, an Err
variant of StandardReferenceError
is returned.
The mock consumer contract provides an example of how to interact with a StandardReference
contract. It provides
functionality to set the address of the standard reference contract and retrieve the price of symbol pairs.
The mock consumer contract provides the following functionality:
set_std_reference_address
: Sets the address of theStandardReference
contract in the contract storage.get_price_of
: Retrieves the price of the specified symbol pair from theStandardReference
contract. It uses the stored address of theStandardReference
contract to create a client and fetch the reference data.
For example, if we want to query the price of BTC/USD, the demo contract below shows how this can be done.
#![no_std]
use soroban_sdk::{contractimpl, contracttype, Address, Env, Symbol, Vec};
mod std_reference {
soroban_sdk::contractimport!(file = "../../dist/std_reference.wasm");
}
pub struct Demo;
#[contractimpl]
impl Demo {
pub fn set_std_reference_address(env: Env, std_reference_address: Address) {
env.storage()
.set(&DataKey::StdReferenceAddress, &std_reference_address);
}
pub fn demo(env: Env) -> u128 {
let addr = env
.storage()
.get_unchecked(&DataKey::StdReferenceAddress)
.unwrap();
let client = std_reference::Client::new(&env, &addr);
client
.get_reference_data(&Vec::from_array(&env, [("BTC", "USD")]))
.get_unchecked(0)
.unwrap()
.rate
}
}
After the StandardReference
contract address is set, the BTC/USD price can be queried via the demo()
function. An
example of result from demo()
would be: 30065900000000000000000
, which is equivalent to 30,065.90 USD. Note that the
output rate is multiplied by 1e18
.
The followings are prerequisites required to compile the contract:
- A Rust toolchain
- An editor that supports Rust
- Soroban CLI
Please refer to Soroban Documentation for installation instructions.
To build a Soroban contract for deployment or local testing, use the following command:
make all
A .wasm
file will be outputted in the target directory. The .wasm
file is the built contract. For example:
target/wasm32-unknown-unknown/std_reference/std_reference.wasm
To deploy, use the deploy script at scripts/deploy.sh
. For example:
scripts/deploy.sh -s <source> -n <network>
Where <source>
is the source account and can either be a secret key, seed phrase or identity account and <network>
is the network to deploy to where the current network options are futurenet
and localnet
.