### Install Rust using rustup Bash Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/2-setup.mdx Downloads and executes the official rustup installer script using curl to install the Rust programming language. This is the recommended way to get Rust. ```bash curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh ``` -------------------------------- ### Install Docker Networking Helper on MacOS Bash Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/2-setup.mdx Installs the 'docker-mac-net-connect' helper tool using Homebrew and starts its service. This provides an alternative or supplementary method for enabling host networking for Docker on macOS. ```bash brew install chipmk/tap/docker-mac-net-connect && sudo brew services start chipmk/tap/docker-mac-net-connect ``` -------------------------------- ### Install Cargo Component Tools Bash Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/2-setup.mdx Installs 'cargo-binstall' for binary installations, then uses it to install 'cargo-component', 'warg-cli', and 'wkg' with specific flags. Finally, it configures the default registry for 'wkg' to 'wa.dev'. These tools are essential for building WebAssembly components. ```bash cargo install cargo-binstall cargo binstall cargo-component warg-cli wkg --locked --no-confirm --force wkg config --default-registry wa.dev ``` -------------------------------- ### Install Foundryup Bash Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/2-setup.mdx Downloads and runs the official Foundry installer script using curl. Foundryup is the toolchain manager for the Foundry Solidity development suite. ```bash curl -L https://foundry.paradigm.xyz | bash ``` -------------------------------- ### Install JQ on Linux Bash Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/2-setup.mdx Installs the JQ command-line JSON processor using the APT package manager on Linux systems. Requires superuser privileges. ```bash sudo apt -y install jq ``` -------------------------------- ### Install Make on Linux Bash Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/2-setup.mdx Installs the GNU Make build automation tool using the APT package manager on Linux systems. Requires superuser privileges. ```bash sudo apt -y install make ``` -------------------------------- ### Install JQ on MacOS Bash Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/2-setup.mdx Installs the JQ command-line JSON processor on macOS using the Homebrew package manager. Requires Homebrew to be installed. ```bash brew install jq ``` -------------------------------- ### Start Local Environment Bash Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/README.md Copies the example environment file and starts the local development environment including an Ethereum node (anvil), the WAVS service, and deployed Eigenlayer contracts via docker-compose. ```Bash cp .env.example .env # Start the backend # # This must remain running in your terminal. Use another terminal to run other commands. # You can stop the services with `ctrl+c`. Some MacOS terminals require pressing it twice. make start-all ``` -------------------------------- ### Run Foundryup to Install Foundry Bash Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/2-setup.mdx Executes the 'foundryup' command to install the Foundry toolchain, which includes Anvil, Forge, Cast, and Chisel. This command should be run after installing Foundryup. ```bash foundryup ``` -------------------------------- ### Install Rust Toolchain and WASM Target Bash Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/2-setup.mdx Installs the latest stable Rust toolchain and adds the required 'wasm32-wasip2' target for building WebAssembly components. This step is necessary after a fresh installation of Rust. ```bash rustup toolchain install stable rustup target add wasm32-wasip2 ``` -------------------------------- ### Install Make on MacOS Bash Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/2-setup.mdx Installs the GNU Make build automation tool on macOS using the Homebrew package manager. Requires Homebrew to be installed. ```bash brew install make ``` -------------------------------- ### Install Docker and Compose on Linux Bash Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/2-setup.mdx Installs the Docker engine ('docker.io') and Docker Compose v2 ('docker-compose-v2') using the APT package manager on Linux systems. Requires superuser privileges. ```bash sudo apt -y install docker.io sudo apt-get install docker-compose-v2 ``` -------------------------------- ### Install Docker on MacOS Bash Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/2-setup.mdx Installs Docker Desktop on macOS using the Homebrew package manager with the '--cask' option. Requires Homebrew to be installed on the system. ```bash brew install --cask docker ``` -------------------------------- ### Setup Project Dependencies Bash Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/README.md Installs project dependencies defined in package.json (via npm) and initializes git submodules. ```Bash # Install packages (npm & submodules) make setup ``` -------------------------------- ### Installing Wavs Development Tools (Bash) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/5-build.mdx Installs essential Rust tools (`cargo-binstall`, `cargo-component`, `warg-cli`, `wkg`) required for building and interacting with Wavs components using `cargo binstall`. It also configures the default `wkg` registry to `wa.dev`. This step requires Rust and Cargo to be installed on the system. ```bash cargo install cargo-binstall cargo binstall cargo-component warg-cli wkg --locked --no-confirm --force # Configure default registry wkg config --default-registry wa.dev ``` -------------------------------- ### Building and Testing Contracts with Makefile and Forge (Bash) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/3-project.mdx Runs a sequence of commands to prepare, build, and test the smart contracts within the project. It first executes `make setup` for dependencies, then `forge build` to compile, and finally `forge test` to run the Foundry tests. ```bash make setup # Build the contracts forge build # Run the solidity tests. forge test ``` -------------------------------- ### Install Rust Toolchain Bash Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/README.md Installs the rustup toolchain manager, adds the stable toolchain, and includes the wasm32-wasip2 target required for building WASI components. ```Bash curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh rustup toolchain install stable rustup target add wasm32-wasip2 ``` -------------------------------- ### Copying Environment File (Bash) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/6-run-service.mdx Creates a local `.env` configuration file for the project by copying the provided example file. This file is used to define environment variables required for subsequent setup and execution steps. ```bash cp .env.example .env ``` -------------------------------- ### Initializing WAVS Foundry Project with Forge (Bash) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/3-project.mdx Initializes a new project directory using the `forge init` command, specifying the `Lay3rLabs/wavs-foundry-template` and checking out the `0.3` branch. This command requires Foundry (forge) to be installed and accessible in your terminal. ```bash forge init --template Lay3rLabs/wavs-foundry-template my-wavs --branch 0.3 ``` -------------------------------- ### Upgrade Rust and Update WASM Target Bash Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/2-setup.mdx Removes old WASI targets if present, updates Rust to the latest stable version, and adds the required 'wasm32-wasip2' target. Use these commands to upgrade an existing Rust installation. ```bash rustup target remove wasm32-wasi || true rustup target remove wasm32-wasip1 || true rustup update stable rustup target add wasm32-wasip2 ``` -------------------------------- ### Starting Development Services (Bash/Makefile) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/6-run-service.mdx Executes a Makefile target to simultaneously start the local Anvil test blockchain, the WAVS runtime environment, and deploy the core EigenLayer contracts necessary for the service. This command should be kept running in a dedicated terminal. ```bash make start-all ``` -------------------------------- ### Create Foundry Project Bash Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/README.md Initializes a new Foundry project based on the specified WAVS template repository and branch name. Requires Foundry to be installed. ```Bash # If you don't have foundry: `curl -L https://foundry.paradigm.xyz | bash && $HOME/.foundry/bin/foundryup` forge init --template Lay3rLabs/wavs-foundry-template my-wavs --branch 0.3 ``` -------------------------------- ### Install Cargo Components & Config Bash Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/README.md Installs necessary Rust cargo subcommands and tools (`cargo-component`, `warg-cli`, `wkg`) for working with WebAssembly components and configures the default warg registry. ```Bash # Install required cargo components # https://github.com/bytecodealliance/cargo-component#installation cargo install cargo-binstall cargo binstall cargo-component warg-cli wkg --locked --no-confirm --force # Configure default registry wkg config --default-registry wa.dev ``` -------------------------------- ### Listing Template Directory Structure Bash Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/custom-components.mdx Displays the standard directory layout of the `wavs-foundry-template` project, highlighting the location of core components, contracts, scripts, and configuration files to guide developers through the project structure. ```Bash wavs-foundry-template/ ├── README.md ├── makefile # Commands, variables, and configs ├── components/ # WASI components │ └── eth-price-oracle/ │ ├── Cargo.toml # Component dependencies │ ├── lib.rs # Main Component logic │ ├── trigger.rs # Trigger handling │ └── bindings.rs # Bindings generated by `make build` ├── compiled/ # WASM files compiled by `make build` ├── src/ │ ├── contracts/ # Trigger and submission contracts │ └── interfaces/ # Solidity interfaces ├── script/ # Scripts used in makefile commands ├── cli.toml # CLI configuration ├── wavs.toml # WAVS service configuration ├── docs/ # Documentation └── .env # Private environment variables ``` -------------------------------- ### Example WAVS Component Implementation Rust Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/custom-components.mdx Presents a Rust example for implementing a WAVS WASI component. It demonstrates decoding an Ethereum log trigger using `decode_event_log_data!`, performing placeholder business logic, and encoding a structured result using `abi_encode()` for potential onchain submission. Requires `wavs-wasi-chain` and `alloy_sol_types` dependencies. ```Rust #[allow(warnings)] mod bindings; use alloy_sol_types::{sol, SolValue}; use bindings::{export, wavs::worker::layer_types::{TriggerData, TriggerDataEthContractEvent}, Guest, TriggerAction}; use wavs_wasi_chain::decode_event_log_data; // Solidity types for the incoming trigger event using the `sol!` macro sol! { event MyEvent(uint64 indexed triggerId, bytes data); struct MyResult { uint64 triggerId; bool success; } } // Define the component struct Component; export!(Component with_types_in bindings); impl Guest for Component { fn run(action: TriggerAction) -> Result>, String> { match action.data { TriggerData::EthContractEvent(TriggerDataEthContractEvent { log, .. }) => { // 1. Decode the event let event: MyEvent = decode_event_log_data!(log) .map_err(|e| format!("Failed to decode event: {}", e))?; // 2. Process data (your business logic goes here) let result = MyResult { triggerId, success: true }; // 3. Return encoded result Ok(Some(result.abi_encode())) } _ => Err("Unsupported trigger type".to_string()) } } } ``` -------------------------------- ### Main WAVS Oracle Component Logic (Rust) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/4-component.mdx This Rust code from `lib.rs` implements the main logic for the WAVS price oracle. It defines the `Component` struct and the `Guest::run` method, which processes trigger events, decodes input data to get an asset ID, fetches price data using the `get_price_feed` function, and formats the output. The `get_price_feed` function performs an HTTP GET request to CoinMarketCap, handling request construction, headers, and JSON parsing. ```Rust // !focus(1:13) mod trigger; use trigger::{decode_trigger_event, encode_trigger_output, Destination}; use wavs_wasi_chain::http::{fetch_json, http_request_get}; pub mod bindings; use crate::bindings::{export, Guest, TriggerAction}; use serde::{Deserialize, Serialize}; use wstd::{http::HeaderValue, runtime::block_on}; struct Component; export!(Component with_types_in bindings); impl Guest for Component { fn run(action: TriggerAction) -> std::result::Result>, String> { let (trigger_id, req, dest) = decode_trigger_event(action.data).map_err(|e| e.to_string())?; // Convert bytes to string and parse first char as u64 let input = std::str::from_utf8(&req).map_err(|e| e.to_string())?; println!("input id: {}", input); let id = input.chars().next().ok_or("Empty input")?; let id = id.to_digit(16).ok_or("Invalid hex digit")? as u64; let res = block_on(async move { let resp_data = get_price_feed(id).await?; println!("resp_data: {:?}", resp_data); serde_json::to_vec(&resp_data).map_err(|e| e.to_string()) })?; let output = match dest { Destination::Ethereum => Some(encode_trigger_output(trigger_id, &res)), Destination::CliOutput => Some(res), }; Ok(output) } } async fn get_price_feed(id: u64) -> Result { let url = format!( "https://api.coinmarketcap.com/data-api/v3/cryptocurrency/detail?id={}&range=1h", id ); let current_time = std::time::SystemTime::now().elapsed().unwrap().as_secs(); let mut req = http_request_get(&url).map_err(|e| e.to_string())?; req.headers_mut().insert("Accept", HeaderValue::from_static("application/json")); req.headers_mut().insert("Content-Type", HeaderValue::from_static("application/json")); req.headers_mut() .insert("User-Agent", HeaderValue::from_static("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36")); req.headers_mut().insert( "Cookie", HeaderValue::from_str(&format!("myrandom_cookie={}", current_time)).unwrap(), ); let json: Root = fetch_json(req).await.map_err(|e| e.to_string())?; Ok(PriceFeedData { symbol: json.data.symbol, price: json.data.statistics.price, timestamp: json.status.timestamp, }) } #[derive(Debug, Serialize, Deserialize)] pub struct PriceFeedData { symbol: String, timestamp: String, price: f64, } /// ----- /// /// Generated from /// ----- /// #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct Root { pub data: Data, pub status: Status, } #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct Data { pub id: f64, pub name: String, pub symbol: String, pub statistics: Statistics, pub description: String, pub category: String, pub slug: String, } #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct Statistics { pub price: f64, #[serde(rename = "totalSupply")] pub total_supply: f64, } #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct CoinBitesVideo { pub id: String, pub category: String, #[serde(rename = "videoUrl")] pub video_url: String, pub title: String, pub description: String, #[serde(rename = "previewImage")] pub preview_image: String, } #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct Status { pub timestamp: String, pub error_code: String, pub error_message: String, pub elapsed: String, pub credit_count: f64, } ``` -------------------------------- ### Copying .env Example File (Bash) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/custom-components.mdx Provides a standard bash command to create a local .env file by copying an existing .env.example file. The .env file is typically used for storing private environment variables during local development. ```bash # copy the example file cp .env.example .env ``` -------------------------------- ### Opening Project in VS Code (Bash) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/3-project.mdx Opens the current directory (the project root) in Visual Studio Code. This command assumes that VS Code is installed and configured to be launched from the terminal using the `code` command. ```bash code . ``` -------------------------------- ### Configuring Rust Component (Cargo.toml) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/custom-components.mdx Example `Cargo.toml` showing how to configure a Rust WASI component for the WAVS framework. It covers package metadata inheritance, defining dependencies, setting build type (`cdylib`), optimizing the release profile, and specifying WAVS component metadata. ```TOML # Package metadata - inherits most values from workspace configuration [package] name = "eth-price-oracle" # Name of the component edition.workspace = true # Rust edition (inherited from workspace) version.workspace = true # Version (inherited from workspace) authors.workspace = true # Authors (inherited from workspace) rust-version.workspace = true # Minimum Rust version (inherited from workspace) repository.workspace = true # Repository URL (inherited from workspace) # Component dependencies [dependencies] # Core dependencies wit-bindgen-rt = {workspace = true} # Required for WASI bindings and Guest trait wavs-wasi-chain = { workspace = true } # Required for core WAVS functionality # Helpful dependencies serde = { workspace = true } # For serialization (if working with JSON) serde_json = { workspace = true } # For JSON handling alloy-sol-macro = { workspace = true } # For Ethereum contract interactions wstd = { workspace = true } # For WASI standard library features alloy-sol-types = { workspace = true } # For Ethereum ABI handling anyhow = { workspace = true } # For enhanced error handling # Library configuration [lib] crate-type = ["cdylib"] # Specifies this is a dynamic library crate # Release build optimization settings [profile.release] codegen-units = 1 # Single codegen unit for better optimization opt-level = "s" # Optimize for size debug = false # Disable debug information strip = true # Strip symbols from binary lto = true # Enable link-time optimization # WAVS component metadata [package.metadata.component] package = "component:eth-price-oracle" # Component package name target = "wavs:worker/layer-trigger-world@0.3.0" # Target WAVS world and version ``` -------------------------------- ### Making HTTP GET Request in WAVS Component (Rust) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/custom-components.mdx Illustrates how to perform an asynchronous HTTP GET request within a synchronous WAVS component environment using `block_on` from `wstd`. Shows request creation, header addition, and fetching/parsing a JSON response. ```rust use wstd::runtime::block_on; // Required for running async code // Async function for the HTTP request async fn make_request() -> Result { // Create the request let url = "https://api.example.com/endpoint"; let mut req = http_request_get(&url).map_err(|e| e.to_string())?; // Add headers req.headers_mut().insert( "Accept", HeaderValue::from_static("application/json") ); // Make the request and parse JSON response let json: YourResponseType = fetch_json(req) .await .map_err(|e| e.to_string())?; Ok(json) } // Main component logic that uses block_on fn process_data() -> Result { // Use block_on to run the async function block_on(async move { make_request().await })? } ``` -------------------------------- ### Getting Chain Configuration in WAVS Component (Rust) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/custom-components.mdx Demonstrates using host bindings (`get_eth_chain_config`, `get_cosmos_chain_config`) to retrieve blockchain configuration details defined in the global wavs.toml file from within a Rust component. ```rust // Get the chain config for an Ethereum chain let chain_config = host::get_eth_chain_config(&chain_name)?; // Get the chain config for a Cosmos chain let chain_config = host::get_cosmos_chain_config(&chain_name)?; ``` -------------------------------- ### Fetching and Structuring Price Data (Rust) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/4-component.mdx This asynchronous function fetches price data for a given cryptocurrency ID from the CoinMarketCap API. It constructs the API URL, adds necessary headers including a dynamic cookie for caching bypass, makes an HTTP GET request, parses the JSON response, and structures the relevant price, symbol, and timestamp data into a `PriceFeedData` struct. ```Rust async fn get_price_feed(id: u64) -> Result { let url = format!( "https://api.coinmarketcap.com/data-api/v3/cryptocurrency/detail?id={}&range=1h", id ); let current_time = std::time::SystemTime::now().elapsed().unwrap().as_secs(); let mut req = http_request_get(&url).map_err(|e| e.to_string())?; req.headers_mut().insert("Accept", HeaderValue::from_static("application/json")); req.headers_mut().insert("Content-Type", HeaderValue::from_static("application/json")); req.headers_mut() .insert("User-Agent", HeaderValue::from_static("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36")); req.headers_mut().insert( "Cookie", HeaderValue::from_str(&format!("myrandom_cookie={}", current_time)).unwrap(), ); let json: Root = fetch_json(req).await.map_err(|e| e.to_string())?; Ok(PriceFeedData { symbol: json.data.symbol, price: json.data.statistics.price, timestamp: json.status.timestamp, }) } #[derive(Debug, Serialize, Deserialize)] pub struct PriceFeedData { symbol: String, timestamp: String, price: f64, } ``` -------------------------------- ### Viewing Makefile Help (Bash) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/3-project.mdx Executes the `help` target defined in the project's Makefile. This command typically provides a list of available `make` commands and information on how to use them, offering insight into project automation scripts. ```bash make help ``` -------------------------------- ### Deploying Service to WAVS (Bash/Makefile) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/6-run-service.mdx Executes a Makefile target that packages the service's WASI component and configuration information and deploys it to the running WAVS runtime. This registers the service with WAVS, specifying the on-chain event that will trigger its execution. ```bash make deploy-service ``` -------------------------------- ### Building All Wavs Project Assets (Make/Bash) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/5-build.mdx Executes the `build` target in the project's Makefile. This is a comprehensive build command that compiles both the Solidity contracts (if present) and the WASM components defined in the project. It's used to build the entire project's assets with a single command. ```bash make build ``` -------------------------------- ### Implementing the Run Function with Logging (Rust) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/4-component.mdx This is the main entry point (`run`) for the WASI component, implementing the `Guest` trait. It decodes the trigger action data, parses the input ID (expected as a single hex digit), calls the asynchronous `get_price_feed` function to fetch data, and serializes the result to bytes. It includes `println!` statements for debugging the input ID and the fetched response data during local development. ```Rust impl Guest for Component { fn run(action: TriggerAction) -> std::result::Result>, String> { let (trigger_id, req, dest) = decode_trigger_event(action.data).map_err(|e| e.to_string())?; // Convert bytes to string and parse first char as u64 let input = std::str::from_utf8(&req).map_err(|e| e.to_string())?; println!("input id: {}", input); let id = input.chars().next().ok_or("Empty input")?; let id = id.to_digit(16).ok_or("Invalid hex digit")? as u64; let res = block_on(async move { let resp_data = get_price_feed(id).await?; println!("resp_data: {:?}", resp_data); serde_json::to_vec(&resp_data).map_err(|e| e.to_string()) })?; ``` -------------------------------- ### Building Wavs WASI Components (Make/Bash) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/5-build.mdx Executes the `wasi-build` target defined in the project's Makefile. This command compiles all components located in the `/components` directory into WASM format, auto-generates necessary bindings, and outputs the results into the `compiled` directory. Requires a Makefile configured for Wavs component builds. ```bash make wasi-build ``` -------------------------------- ### Showing Service Execution Result (Bash/Makefile) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/6-run-service.mdx Executes a Makefile target that first obtains the latest trigger ID and then runs a Foundry script (`ShowResult.s.sol`) to query the blockchain and display the final result submitted by the WAVS service (e.g., the processed Bitcoin price) in the terminal. ```bash make show-result ``` -------------------------------- ### Build Solidity Contracts Bash Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/README.md Compiles the Solidity smart contracts located in the project using the Foundry build command. ```Bash # Build the contracts forge build ``` -------------------------------- ### Navigating into Project Directory (Bash) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/3-project.mdx Changes the current working directory in the terminal to the newly created project folder, `my-wavs`. This is a standard command required to execute commands specific to the project from its root directory. ```bash cd my-wavs ``` -------------------------------- ### Build WASI Components Bash Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/README.md Compiles the Rust code into WebAssembly WASI components using the project's makefile command. ```Bash make wasi-build # or `make build` to include solidity compilation. ``` -------------------------------- ### Configuring Ethereum Chains in wavs.toml (TOML) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/custom-components.mdx Shows how to define configurations for different Ethereum networks (e.g., local, mainnet) in the root wavs.toml file. This centralizes chain details like IDs and RPC endpoints. ```toml [chains.eth.local] chain_id = "31337" ws_endpoint = "ws://localhost:8545" http_endpoint = "http://localhost:8545" [chains.eth.mainnet] chain_id = "1" ws_endpoint = "wss://mainnet.infura.io/ws/v3/YOUR_INFURA_ID" http_endpoint = "https://mainnet.infura.io/v3/YOUR_INFURA_ID" ``` -------------------------------- ### Deploying Service Contracts (Bash/Foundry) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/6-run-service.mdx Retrieves the EigenLayer Service Manager address using a Makefile target and then uses Foundry's `forge script` to deploy the project's custom Trigger and Submission Solidity contracts to the local Anvil chain. The script executes a function that requires the service manager address. ```bash export SERVICE_MANAGER_ADDR=`make get-eigen-service-manager-from-deploy` forge script ./script/Deploy.s.sol ${SERVICE_MANAGER_ADDR} --sig "run(string)" --rpc-url http://localhost:8545 --broadcast ``` -------------------------------- ### Creating Ethereum RPC Provider in WAVS Component (Rust) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/custom-components.mdx Shows how to retrieve a specific Ethereum chain configuration using host bindings and then instantiate an 'alloy' RootProvider using the HTTP endpoint from the config via the `wavs-wasi-chain::ethereum::new_eth_provider` function. ```rust use crate::bindings::host::{get_eth_chain_config, get_cosmos_chain_config}; // Import host functions use wavs_wasi_chain::ethereum::new_eth_provider; use alloy_provider::{Provider, RootProvider}; use alloy_network::Ethereum; use anyhow::Context; // For context() error handling // Get the chain config for a specific chain defined in wavs.toml let chain_config = get_eth_chain_config("eth.local") // Use the key from wavs.toml (e.g., "eth.local" or "eth.mainnet") .map_err(|e| format!("Failed to get chain config: {}", e))?; // Create an Alloy provider instance using the HTTP endpoint let provider: RootProvider = new_eth_provider::( chain_config.http_endpoint .context("http_endpoint is missing in chain config")? // Ensure endpoint exists )?; ``` -------------------------------- ### Testing Wavs WASI Component Locally (Make/Bash) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/5-build.mdx Executes the `wasi-exec` target in the project's Makefile with the `COIN_MARKET_CAP_ID` environment variable set. This command simulates a trigger event and runs the WASM component locally in a test environment, allowing for easy debugging via print statements and viewing the component's output before deployment. Requires a Makefile with a `wasi-exec` target and a component that utilizes the `COIN_MARKET_CAP_ID` input. ```bash COIN_MARKET_CAP_ID=1 make wasi-exec ``` -------------------------------- ### Execute WASI Component Locally Bash Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/README.md Runs the compiled WASI component directly on the local machine with a specified input (COIN_MARKET_CAP_ID=1), allowing testing of the core business logic without deploying to WAVS. ```Bash COIN_MARKET_CAP_ID=1 make wasi-exec ``` -------------------------------- ### Run Solidity Tests Bash Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/README.md Executes the unit tests written for the Solidity contracts using the Foundry test command. ```Bash # Run the solidity tests forge test ``` -------------------------------- ### Triggering WAVS Service Execution (Bash/Foundry) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/6-run-service.mdx Sets environment variables for the data payload (CoinMarketCap ID for Bitcoin) and the deployed Trigger contract address. It then runs a Foundry script to call the Trigger contract, causing it to emit an event that signals WAVS operators to execute the off-chain oracle component. ```bash export COIN_MARKET_CAP_ID=1 export SERVICE_TRIGGER_ADDR=`make get-trigger-from-deploy` forge script ./script/Trigger.s.sol ${SERVICE_TRIGGER_ADDR} ${COIN_MARKET_CAP_ID} --sig "run(string,string)" --rpc-url http://localhost:8545 --broadcast -v 4 ``` -------------------------------- ### Deploy WAVS Service Bash Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/README.md Deploys the compiled WASI component and configures the WAVS service to watch for events from the deployed trigger contract. ```Bash TRIGGER_EVENT="NewTrigger(bytes)" make deploy-service ``` -------------------------------- ### Deploy WAVS Contracts Bash Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/README.md Deploys the service's trigger and submission contracts to the local network using a Foundry script, requiring the Service Manager address. ```Bash export SERVICE_MANAGER_ADDR=`make get-eigen-service-manager-from-deploy` forge script ./script/Deploy.s.sol ${SERVICE_MANAGER_ADDR} --sig "run(string)" --rpc-url http://localhost:8545 --broadcast ``` -------------------------------- ### Adding Ethereum Blockchain Dependencies (TOML) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/custom-components.mdx Lists the necessary dependencies in a Rust component's Cargo.toml for interacting with Ethereum. Includes WAVS-specific crates and several 'alloy' crates for RPC communication, ABI handling, and primitive types. ```toml [dependencies] # Core WAVS blockchain functionality wit-bindgen-rt = {workspace = true} # Required for WASI bindings and Guest trait wavs-wasi-chain = { workspace = true } # HTTP utilities # Alloy crates for Ethereum interaction alloy-sol-types = { workspace = true } # ABI handling & type generation alloy-sol-macro = { workspace = true } # sol! macro for interfaces alloy-primitives = { workspace = true } # Core primitive types (Address, U256, etc.) alloy-network = "0.11.1" # Network trait and Ethereum network type alloy-provider = { version = "0.11.1", default-features = false, features = ["rpc-api"] } # RPC provider alloy-rpc-types = "0.11.1" # RPC type definitions (TransactionRequest, etc.) # Other useful crates anyhow = { workspace = true } # Error handling serde = { workspace = true } # Serialization/deserialization serde_json = { workspace = true } # JSON handling ``` -------------------------------- ### Importing UI Components - TypeScript Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/design.mdx This commented-out snippet imports `Callout` and `DocsPage` components from the `fumadocs-ui` library. These components are typically used for rendering documentation pages and are likely related to the documentation generation process itself. ```TypeScript import { Callout } from 'fumadocs-ui/components/callout';\nimport { DocsPage } from 'fumadocs-ui/page'; ``` -------------------------------- ### Handling Component Response Output (Rust) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/4-component.mdx This snippet shows how the output from the WASI component's `run` function is handled based on the specified destination (`dest`). If the destination is Ethereum, the output is encoded using `encode_trigger_output`; otherwise, if the destination is CliOutput, the raw result is used. The function then returns the optional output wrapped in a `Result::Ok`. ```Rust let output = match dest { Destination::Ethereum => Some(encode_trigger_output(trigger_id, &res)), Destination::CliOutput => Some(res), }; Ok(output) } } ``` -------------------------------- ### Using Sol! Macro (Rust) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/custom-components.mdx Demonstrates the use of the `sol!` macro from `alloy-sol-macro`. It shows generating Rust types by referencing an external Solidity file (`../../src/interfaces/ITypes.sol`) or by defining Solidity types directly inline within the macro invocation. ```Rust mod solidity { use alloy_sol_macro::sol; // Generate types from Solidity file sol!("../../src/interfaces/ITypes.sol"); // Or define types inline sol! { struct TriggerInfo { uint64 triggerId; bytes data; } event NewTrigger(TriggerInfo _triggerInfo); } } ``` -------------------------------- ### Fetching Price Data in WAVS Component (Rust) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/4-component.mdx This Rust snippet from the `Component::run` method in `lib.rs` demonstrates fetching price data. It calls the asynchronous `get_price_feed` function with the derived asset ID within a `block_on` block, retrieves the `PriceFeedData` response, serializes it into JSON bytes using `serde_json`, and assigns the result to the `res` variable for subsequent output encoding. Error handling is included for the async operation and serialization. ```Rust // Convert bytes to string and parse first char as u64 let input = std::str::from_utf8(&req).map_err(|e| e.to_string())?; println!("input id: {}", input); let id = input.chars().next().ok_or("Empty input")?; let id = id.to_digit(16).ok_or("Invalid hex digit")? as u64; // !focus(1:4) let res = block_on(async move { let resp_data = get_price_feed(id).await?; println!("resp_data: {:?}", resp_data); serde_json::to_vec(&resp_data).map_err(|e| e.to_string()) })?; let output = match dest { Destination::Ethereum => Some(encode_trigger_output(trigger_id, &res)), Destination::CliOutput => Some(res), }; Ok(output) } } ``` -------------------------------- ### Upgrade Rust Toolchain Bash Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/README.md Updates the stable Rust toolchain and ensures the correct wasm32-wasip2 target is present, optionally removing older WASI targets. ```Bash # Remove old targets if present rustup target remove wasm32-wasi || true rustup target remove wasm32-wasip1 || true # Update and add required target rustup update stable rustup target add wasm32-wasip2 ``` -------------------------------- ### Dependencies for Sol! Macro (Cargo.toml) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/custom-components.mdx Specifies the necessary dependencies (`alloy-sol-macro` and `alloy-sol-types`) that need to be included in the `Cargo.toml` file to enable the use of the `sol!` macro for generating Rust types from Solidity definitions and handling ABI encoding/decoding. ```TOML [dependencies] alloy-sol-macro = { workspace = true } # For Solidity type generation alloy-sol-types = { workspace = true } # For ABI handling ``` -------------------------------- ### Adding HTTP Request Dependencies (TOML) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/custom-components.mdx Lists the required dependencies in a Rust component's Cargo.toml file for making HTTP requests. Includes `wavs-wasi-chain` for HTTP utilities and `wstd` for the `block_on` function to handle async operations. ```toml [dependencies] wavs-wasi-chain = { workspace = true } # HTTP utilities wstd = { workspace = true } # Runtime utilities (includes block_on) serde = { workspace = true } # Serialization serde_json = { workspace = true } # JSON handling ``` -------------------------------- ### Customizing Service Trigger Event (Bash/Makefile) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/6-run-service.mdx Overrides the default `TRIGGER_EVENT` environment variable before executing the `make deploy-service` command. This allows specifying a different event signature than the default, configuring WAVS to listen for a custom trigger event from the smart contract. ```bash TRIGGER_EVENT="NewTrigger(bytes)" make deploy-service ``` -------------------------------- ### Debug Logging to Stdout (Rust) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/custom-components.mdx Uses the standard Rust `println!` macro for simple debugging output. This output is visible when running the component locally using the `wasi-exec` command. ```Rust println!("Debug message: {:?}", data); ``` -------------------------------- ### Trigger WAVS Service Bash Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/README.md Calls the deployed trigger contract with a specific input (COIN_MARKET_CAP_ID=1), emitting an event that the WAVS service is watching to initiate a service execution. ```Bash export COIN_MARKET_CAP_ID=1 export SERVICE_TRIGGER_ADDR=`make get-trigger-from-deploy` forge script ./script/Trigger.s.sol ${SERVICE_TRIGGER_ADDR} ${COIN_MARKET_CAP_ID} --sig "run(string,string)" --rpc-url http://localhost:8545 --broadcast -v 4 ``` -------------------------------- ### Adding Prediction Market Oracle Trigger with Payment - Solidity Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/tutorial/7-prediction.mdx Shows the beginning of the `addTrigger` function in the `PredictionMarketOracleController` contract. This function is called to initiate the oracle process for a prediction market resolution and requires the caller to send exactly 0.1 ETH as payment. ```Solidity function addTrigger( TriggerInputData calldata triggerData ) external payable returns (ITypes.TriggerId triggerId) { require(msg.value == 0.1 ether, "Payment must be exactly 0.1 ETH"); ``` -------------------------------- ### Querying ERC721 NFT Balance with Alloy/WASI Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/custom-components.mdx This Rust snippet defines the necessary ERC721 interface subset and provides an asynchronous function to query the `balanceOf` method of an NFT contract for a specific owner address. It utilizes `wavs-wasi-chain` for provider creation and `alloy` for interacting with the Ethereum network, including encoding the call and decoding the result. It also shows how to run the async function from synchronous code using `wstd::runtime::block_on`. ```Rust use crate::bindings::host::get_eth_chain_config; use alloy_network::{Ethereum, Network}; use alloy_primitives::{Address, Bytes, TxKind, U256}; use alloy_provider::{Provider, RootProvider}; use alloy_rpc_types::{TransactionInput, eth::TransactionRequest}; // Note: use eth::TransactionRequest use alloy_sol_types::{sol, SolCall}; // Removed unused SolType, SolValue use wavs_wasi_chain::ethereum::new_eth_provider; use anyhow::Context; use wstd::runtime::block_on; // Required to run async code // Define the ERC721 interface subset needed sol! { interface IERC721 { function balanceOf(address owner) external view returns (uint256); } } // Function to query NFT ownership (must be async) pub async fn query_nft_ownership(owner_address: Address, nft_contract: Address) -> Result { // 1. Get chain configuration (using "eth.local" as an example) let chain_config = get_eth_chain_config("eth.local") .map_err(|e| format!("Failed to get eth.local chain config: {}", e))?; // 2. Create Ethereum provider let provider: RootProvider = new_eth_provider::( chain_config.http_endpoint .context("http_endpoint missing for eth.local")? ).map_err(|e| format!("Failed to create provider: {}", e))?; // Handle provider creation error // 3. Prepare the contract call using the generated interface let balance_call = IERC721::balanceOfCall { owner: owner_address }; // 4. Construct the transaction request for a read-only call let tx = TransactionRequest { to: Some(TxKind::Call(nft_contract)), // Specify the contract to call input: TransactionInput { input: Some(balance_call.abi_encode().into()), // ABI-encoded call data data: None // `data` is deprecated, use `input` }, // Other fields like nonce, gas, value are not needed for eth_call ..Default::default() }; // 5. Execute the read-only call using the provider // Note: provider.call() returns the raw bytes result let result_bytes = provider.call(&tx) .await .map_err(|e| format!("Provider call failed: {}", e))?; // 6. Decode the result (balanceOf returns uint256) // Ensure the result is exactly 32 bytes for U256::from_be_slice if result_bytes.len() != 32 { return Err(format!("Unexpected result length: {}", result_bytes.len())); } let balance = U256::from_be_slice(&result_bytes); // 7. Determine ownership based on balance Ok(balance > U256::ZERO) } // Example of how to call the async function from the main sync component logic fn main_logic(owner: Address, contract: Address) -> Result { let is_owner = block_on(async move { query_nft_ownership(owner, contract).await })?; // Use block_on to run the async function Ok(is_owner) } ``` -------------------------------- ### Configuring Private Variables in WAVS Makefile (Bash) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/custom-components.mdx Shows how to list the names of required private environment variables (`host_envs`) within the SERVICE_CONFIG in a Makefile. Only variables listed here and prefixed with 'WAVS_ENV_' will be available to the component. ```bash # makefile SERVICE_CONFIG ?= '{"fuel_limit":100000000,"max_gas":5000000,"host_envs":["WAVS_ENV_MY_API_KEY"],"kv":[],"workflow_id":"default","component_id":"default"}' ``` -------------------------------- ### Production Logging with Host Function (Rust) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/custom-components.mdx Uses the `host::log()` function provided by the WAVS environment for production logging. This method includes context like ServiceID and WorkflowID and uses the tracing mechanism, supporting different `LogLevel`s. ```Rust host::log(LogLevel::Info, "Production logging message"); ``` -------------------------------- ### Sol! Macro in Template Trigger File (Rust) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/custom-components.mdx Shows how the `sol!` macro is specifically used in the template's `trigger.rs` file. It points the macro to the `../../src/interfaces/ITypes.sol` file to automatically generate Rust types based on the Solidity definitions found there, making them available for use in the component. ```Rust mod solidity { use alloy_sol_macro::sol; pub use ITypes::*; // The objects here will be generated automatically into Rust types. // If you update the .sol file, you must re-run `cargo build` to see the changes. sol!("../../src/interfaces/ITypes.sol"); } ``` -------------------------------- ### Handling Trigger Input Data (Rust) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/custom-components.mdx Implements the `Guest` trait for a WAVS component, demonstrating how to receive and process `TriggerAction` data. It shows handling both `EthContractEvent` (decoding via `decode_event_log_data!`) and `Raw` data, processing it, and returning the result as bytes. ```Rust // 1. Define your Solidity types using the `sol!` macro sol! { event MyEvent(uint64 indexed triggerId, bytes data); struct MyResult { uint64 triggerId; bytes processedData; } } // 2. Handle on-chain event trigger and raw trigger types impl Guest for Component { fn run(action: TriggerAction) -> Result>, String> { match action.data { // On-chain event handling TriggerData::EthContractEvent(TriggerDataEthContractEvent { log, .. }) => { // Decode the event let event: MyEvent = decode_event_log_data!(log)?; // Process the data let result = MyResult { triggerId: event.triggerId, processedData: process_data(&event.data)?, }; // Encode for submission Ok(Some(result.abi_encode())) } // Manual trigger handling for testing TriggerData::Raw(data) => { // Process raw data directly let result = process_data(&data)?; Ok(Some(result)) } _ => Err("Unsupported trigger type".to_string()) } } } ``` -------------------------------- ### Configuring Public Variables in WAVS Makefile (Bash) Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/docs/custom-components.mdx Shows how to define public key-value pairs ('kv') within the SERVICE_CONFIG variable in a Makefile. These variables are used for non-sensitive information that is set during service deployment. ```bash # makefile SERVICE_CONFIG ?= '{"fuel_limit":100000000,"max_gas":5000000,"host_envs":[],"kv":[["max_retries","3"],["timeout_seconds","30"],["api_endpoint","https://api.example.com"]],"workflow_id":"default","component_id":"default"}' ``` -------------------------------- ### Show Service Result Bash Source: https://github.com/lay3rlabs/wavs-foundry-template/blob/0.3/README.md Queries the deployed submission contract to retrieve and display the result of the latest service execution using a Foundry script. ```Bash # Get the latest TriggerId and show the result via `script/ShowResult.s.sol` make show-result ```