Skip to content

Latest commit

 

History

History
258 lines (180 loc) · 9.74 KB

README.md

File metadata and controls

258 lines (180 loc) · 9.74 KB

Switchboard Logo

Switchboard On-Demand on EVM

A collection of libraries and examples for interacting with Switchboard on EVM chains.

NOTE: Switchboard On-Demand on EVM is currently an unaudited alpha. Use at your own risk.

Documentation and examples for using Switchboard On-Demand on Ethereum Virtual Machine (EVM) Networks. With Switchboard On-Demand, users can customize and create low-latency data feeds from any source.

Current Deployments

Table of Contents

Overview

Switchboard On-Demand is a decentralized oracle service that allows users to create custom data feeds on the Ethereum Virtual Machine (EVM) networks. Users can create a feed that specifies the data sources, aggregation method, and other parameters. Once the feed is created, users can query Switchboard oracles to resolve it, and verify the data on-chain. This is an example of a pull-based oracle service, where users can request custom data on-demand with low-latency and low gas cost.

Getting Started

To get started with Switchboard On-Demand, you will need to install the Switchboard CLI and set up a Switchboard account. You can then create a Switchboard On-Demand job and query the oracle to get the data.

There's a Solidity-SDK that you can use to interact with the oracle contract on-chain and leverage customized oracle data within your smart contracts. For querying oracle updates off-chain for on-chain submission, you can use the Switchboard On-Demand Typescript-SDK.

Prerequisites

To use Switchboard On-Demand, you will need to have a basic understanding of Ethereum and smart contracts. For more on Switchboard's Architecture, see the docs (EVM docs will be consolidated with main docs upon completion of audit).

Installation

You can install the Switchboard On-Demand Solidity SDK by running:

npm install @switchboard-xyz/on-demand-solidity

And you can install the cross-chain Typescript SDK by running:

npm install @switchboard-xyz/on-demand

Forge (Optional)

If you're using Forge, add following to your remappings.txt file: @switchboard-xyz/on-demand-solidity/=node_modules/@switchboard-xyz/on-demand-solidity

Solidity

The code below shows the flow for leveraging Switchboard feeds in Solidity.

//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import {ISwitchboard} from "@switchboard-xyz/on-demand-solidity/ISwitchboard.sol";
import {Structs} from "@switchboard-xyz/on-demand-solidity/structs/Structs.sol";

contract Example {
    ISwitchboard switchboard;

    // Every Switchboard Feed has a unique feed ID derived from the OracleJob definition and Switchboard Queue ID.
    bytes32 feedId;

    // If the transaction fee is not paid, the update will fail.
    error InsufficientFee(uint256 expected, uint256 received);

    // If the feed result is invalid, this error will be emitted.
    error InvalidResult(int128 result);

    // If the Switchboard update succeeds, this event will be emitted with the latest price.
    event FeedData(int128 price);

    /**
     * @param _switchboard The address of the Switchboard contract
     * @param _feedId The feed ID for the feed you want to query
     */
    constructor(address _switchboard, bytes32 _feedId) {
        // Initialize the target _switchboard
        // Get the existing Switchboard contract address on your preferred network from the Switchboard Docs
        switchboard = ISwitchboard(_switchboard);
        feedId = _feedId;
    }

    /**
     * getFeedData is a function that uses an encoded Switchboard update
     * If the update is successful, it will read the latest price from the feed
     * See below for fetching encoded updates (e.g., using the Switchboard Typescript SDK)
     * @param updates Encoded feed updates to update the contract with the latest result
     */
    function getFeedData(bytes[] calldata updates) public payable {
        // Get the fee for updating the feeds. If the transaction fee is not paid, the update will fail.
        uint256 fee = switchboard.getFee(updates);
        if (msg.value < fee) {
            revert InsufficientFee(fee, msg.value);
        }

        // Submit the updates to the Switchboard contract
        switchboard.updateFeeds{value: fee}(updates);

        // Read the current value from a Switchboard feed.
        // This will fail if the feed doesn't have fresh updates ready (e.g. if the feed update failed)
        Structs.Update memory latestUpdate = switchboard.latestUpdate(feedId);

        // Get the latest feed result
        // This is encoded as decimal * 10^18 to avoid floating point issues
        // Some feeds require negative numbers, so results are int128's, but this example uses positive numbers
        int128 result = latestUpdate.result;

        // In this example, we revert if the result is negative
        if (result < 0) {
            revert InvalidResult(result);
        }

        // Emit the latest result from the feed
        emit FeedData(latestUpdate.result);
    }
}

This contract:

  1. Sets the Switchboard contract address and feed ID in the constructor
  2. Defines a function getFeedData that:
    • Checks if the transaction fee is paid, using switchboard.getFee(bytes[] calldata updates)
    • Submits the updates to the Switchboard contract using switchboard.updateFeeds(bytes[] calldata updates)
    • Reads the latest value from the feed using switchboard.getLatestValue(bytes32 feedId)
    • Emits the latest result from the feed

Getting the Encoded Updates

To get the encoded updates for the feed, you can use the Switchboard Typescript SDK. Here's an example of how to get the encoded updates:

/* Example Using Crossbar (equivalent to index.ts) */
import * as ethers from "ethers";
import * as fs from "fs";
import { CrossbarClient } from "@switchboard-xyz/on-demand";

// Parse the response as JSON
const secret = fs.readFileSync(".secret", "utf-8");

// Create a provider
const provider = new ethers.JsonRpcProvider(
  "https://sepolia-rollup.arbitrum.io/rpc"
);

// Create a signer
const signerWithProvider = new ethers.Wallet(secret, provider);

// Target contract address
const exampleAddress = "0x4ED8171dB9eC85ee785e34AFBeFcAB539dbE2790";

// for tokens (this is the Human-Readable ABI format)
const abi = [
  "function getFeedData(bytes[] calldata updates) public payable",
  "function aggregatorId() public view returns (bytes32)",
];

const crossbar = new CrossbarClient(`https://crossbar.switchboard.xyz`);

// The Contract object
const exampleContract = new ethers.Contract(
  exampleAddress,
  abi,
  signerWithProvider
);

// Get the encoded updates
const { encoded } = await crossbar.fetchEVMResults({
  chainId: 421614,
  aggregatorIds: [await exampleContract.aggregatorId()],
});

// Update the contract + do some business logic
const tx = await exampleContract.getFeedData(encoded);

console.log(tx);

// Log the transaction hash
console.log("Transaction completed!");

Running the Example

Prerequisites

  1. Install Forge
  2. Install Bun for running the example typescript script
  3. Pick an Aggregator ID, the Address from a Switchboard feed, and set it to the ENV variable AGGREGATOR_ID (e.g., export AGGREGATOR_ID=0x...). You can create a Feed or find one in the Switchboard Explorer
  4. Setup an Arbitrum Sepolia Testnet wallet with funding

Running the Example

To run the example, you will need to:

  1. Clone the repository and navigate to Example:
git clone https://github.com/switchboard-xyz/evm-on-demand
cd evm-on-demand/example
  1. Install the dependencies:
bun install
  1. Run Forge install:
forge install
  1. Set the test wallet's private key to ENV variable PRIVATE_KEY (e.g., export PRIVATE_KEY=0x...)

  2. Run the script to deploy the example contract:

forge script script/Deploy.s.sol:DeployScript --rpc-url https://sepolia-rollup.arbitrum.io/rpc --broadcast -vv
  1. Set the Contract Address from the deployment to ENV variable EXAMPLE_ADDRESS (e.g., export EXAMPLE_ADDRESS=0x...)

  2. Run the script to update the feed:

bun run index.ts