Skip to main content

Basic Oracle Function

Switchboard Functions allow you to verifiably execute your own Rust code off-chain and submit transactions either on a pre-defined cron schedule or on-demand. Switchboard Functions gives smart contracts greater flexibility as they can delegate execution to an off-chain computational layer.

In this example we will build a custom oracle that pushes the latest price of BTC, ETH, and SOL to your program every 5 seconds.

Integration Checklist:

  • Define Switchboard interface in your program
  • Write a custom function in Rust
  • Deploy function to Docker container
  • Create Switchboard function account

Define Switchboard interface in your programโ€‹

Your on-chain program needs to have an instruction which verifies the function.enclaveSigner has signed the transaction.

use switchboard_solana::{FunctionAccountData};

#[derive(Accounts)]
pub struct SaveDataInstruction<'info> {
// ... your required accounts to modify your program's state

// We use this to derive and verify the functions enclave state
#[account(
constraint =
function.load()?.validate(
&enclave_signer.to_account_info()
)?
)]
pub function: AccountLoader<'info, FunctionAccountData>,
pub enclave_signer: Signer<'info>,
}

The validate_enclave method will verify:

  1. The provided enclave account corresponds to the provided function account
  2. The provided signer matches the function's enclave signer and has signed the transaction
  3. The provided enclave accounts mr_enclave value is present in the function accounts mr_enclaves array.

Write a custom function in Rustโ€‹

Now we need to write our custom function which will invoke our program and save the trading prices to our program state.

Clone the solana-functions-template repository:

gh repo create myrepo --template switchboard-xyz/solana-functions-template
# or just git clone
git clone https://github.com/switchboard-xyz/solana-functions-template.git

You should see a file in src/main.rs similar to:

pub use switchboard_solana::prelude::*;

#[tokio::main(worker_threads = 12)]
async fn main() {
// First, initialize the runner instance with a freshly generated Gramine keypair
let runner = FunctionRunner::new_from_cluster(Cluster::Devnet, None).unwrap();

// Then, write your own Rust logic and build a Vec of instructions.
// Should be under 700 bytes after serialization
let ixs: Vec<solana_program::instruction::Instruction> = vec![];

// Finally, emit the signed quote and partially signed transaction to the functionRunner oracle
// The functionRunner oracle will use the last outputted word to stdout as the serialized result. This is what gets executed on-chain.
runner.emit(ixs).await.unwrap();
}

To write your custom Switchboard function just append instructions to the ixs array. The FunctionRunner will automatically prepend the function_verify instruction with the quote generated from the enclave. The quote verifier oracle running the function will verify this instruction before sending your transaction.

See examples/functions/01_basic_oracle/sgx-function/src/main.rs in Switchboard's Solana SDK for the full example code.

Deploy function to Docker containerโ€‹

The template repository includes a Makefile to streamline publishing your container to the docker repository along with outputting your MrEnclave measurement. This measurement corresponds to the code fingerprint of the outputted Rust binary. You should store this value in the function account we create in the following step. This will ensure that the only code that is allowed to add data to your smart contract must be generated from a binary with this signature. You can add multiple MrEnclave values to your function account to allow backwards compatibility and make upgrades easier.

Edit the Makefile and add your docker registry.

# Variables
## Cargo.toml name of the compiled binary
CARGO_NAME=switchboard-function
## Docker registry image name (Ex: switchboardlabs/basic-oracle-function)
DOCKER_IMAGE_NAME=switchboard-function

Use one of the following commands to compile your function:

CommandDescription
makeBuild the container locally and output the MrEnclave measurement
make publishPublish the container to the provided docker repository under the latest tag

Create Switchboard function accountโ€‹

Finally we just need to create our function account on-chain. We will define a cron schedule of */5 * * * * * which will invoke our function every 5 seconds. We will need to pre-fund our function escrow account in order to reward verifier oracles for running our function.

Visit the Switchboard app: app.switchboard.xyz

In the top right, sign in to your Solana wallet for the selected cluster.

Switchboard App Sign-in Solana

Then click build and start configuring your function

Switchboard App Function Config Modal