Layer LogoWAVS Docs
Build a service

4. Oracle component walkthrough

The core logic of the price oracle in this example is located in the /eth-price-oracle/src/lib.rs file. Scroll down to follow a walkthrough of the code for the oracle component.

trigger.rs

The trigger.rs file handles the decoding of incoming trigger data and preparing it for processing within the WAVS component. The encode_trigger_output function ensures that processed data is formatted correctly before being sent back. Update the code if you require different trigger types (e.g. Cosmos events) or if you are building a custom trigger.

Oracle component definitions

The lib.rs file contains the main component logic for the oracle. The first section of the code imports the required modules for requests, serialization, and bindings, defines the component struct, and exports the component for execution within the WAVS runtime.

Trigger data

The next section decodes the event sent from the trigger and extracts the trigger_id and data. The run function in lib.rs calls decode_trigger_event from trigger.rs to parse the trigger input, retrieve the trigger ID, and process the data by converting it into an asset ID, which will be used in the query.

Fetching price data

Then, get_price_feed(id).await is called to fetch the asset price from CoinMarketCap.

Price feed data

The asset ID passed from the trigger is used in the API request to fetch the data from CoinMarketCap.

The returned price data is then extracted from the response and converted into a structured format.

Handling the Response

The price data is ready to be passed to the submission contract (also known as the Service Handler), which will verify that the response was sent by an operator before submitting on-chain.

Logging for development

When developing your own WASI component, it's helpful to use logging methods in your code. Components write all their standard output and errors (stdout or stderr) to the host machine.

In this example, println!() is used in fn run() to debug input id and resp_data while processing price feed requests asynchronously. This will write to stdout and is visible in local development when you run the wasi-exec command in the next section of this guide.

For production, you can use a host::log() function which takes a LogLevel and writes its output via the tracing mechanism. Along with the string that the developer provides, it attaches additional context such as the ServiceID, WorkflowID, and component Digest.

Next steps

Continue to the next section to learn how to build and test your component.

trigger.rs
use crate::bindings::wavs::worker::layer_types::{TriggerData, TriggerDataEthContractEvent};
use alloy_sol_types::SolValue;
use anyhow::Result;
use wavs_wasi_chain::decode_event_log_data;
pub enum Destination {
Ethereum,
CliOutput,
}
pub fn decode_trigger_event(trigger_data: TriggerData) -> Result<(u64, Vec<u8>, Destination)> {
match trigger_data {
TriggerData::EthContractEvent(TriggerDataEthContractEvent { log, .. }) => {
let event: solidity::NewTrigger = decode_event_log_data!(log)?;
let trigger_info = solidity::TriggerInfo::abi_decode(&event._triggerInfo, false)?;
Ok((trigger_info.triggerId, trigger_info.data.to_vec(), Destination::Ethereum))
}
TriggerData::Raw(data) => Ok((0, data.clone(), Destination::CliOutput)),
_ => Err(anyhow::anyhow!("Unsupported trigger data type")),
}
}
pub fn encode_trigger_output(trigger_id: u64, output: impl AsRef<[u8]>) -> Vec<u8> {
solidity::DataWithId { triggerId: trigger_id, data: output.as_ref().to_vec().into() }
.abi_encode()
}
mod solidity {
use alloy_sol_macro::sol;
pub use ITypes::*;
sol!("../../src/interfaces/ITypes.sol");
}

Edit on GitHub