switchboard

Par elophanto · elophanto

SDK complet du protocole Switchboard Oracle pour Solana — la solution oracle sans permission pour les flux de prix, les données à la demande, l'aléatoire VRF et le streaming en temps réel via Surge. Couvre le SDK TypeScript, l'intégration Rust, les Oracle Quotes et tous les outils Switchboard.

npx skills add https://github.com/elophanto/elophanto --skill switchboard

Guide Complet d'Intégration du Protocole Switchboard Oracle

Le guide définitif pour intégrer Switchboard - le protocole oracle le plus rapide, le plus personnalisable et le seul sans permission sur Solana.

Qu'est-ce que Switchboard ?

Switchboard est un protocole oracle sans permission permettant aux développeurs de mettre des données personnalisées on-chain avec des performances de pointe :

  • Price Feeds - Tarification d'actifs en temps réel avec efficacité pull-based
  • Oracle Quotes - Latence sub-seconde sans stockage on-chain (réduction de coût de 90%)
  • Surge - Streaming WebSocket avec latence sub-100ms
  • VRF Randomness - Fonctions aléatoires vérifiables cryptographiquement sécurisées
  • Prediction Markets - Données de prévision basées sur le marché

Statistiques Clés

  • Sécurise plus de 1 milliard de dollars de volume on-chain
  • Utilisé par Kamino, Jito, MarginFi, Drift Protocol
  • Latence 2-5ms avec tarification Surge
  • Réduction de coût de 90% par rapport aux oracles traditionnels

Principes Fondamentaux

Principe Description
Rapidité 2-5ms avec Surge, 400ms standard - leader de l'industrie pour DeFi
Efficacité des Coûts Les feeds pull-based éliminent les frais de streaming constant
Sans Permission Déployer des feeds instantanément sans approbations
Sécurité TEE (Trusted Execution Environments) prévient la manipulation de données

Approches d'Intégration

1. Oracle Quotes (Recommandé)

Flux de données direct oracle-to-program sans stockage on-chain :

  • Latence sub-seconde
  • Réduction de coût de 90%
  • Pas de verrous en écriture (lectures parallèles)
  • Conception stateless

2. Feeds Traditionnels

Mises à jour de feed pull-based classiques :

  • Maintenance du compte feed
  • Opérations de cranking
  • Adapté aux cas d'usage simples

3. Surge (Temps Réel)

Streaming WebSocket pour applications haute fréquence :

  • Latence sub-100ms
  • Connexions persistantes
  • Idéal pour les interfaces de trading

IDs de Programme

Programme Mainnet Devnet
Oracle Program SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f Aio4gaXjXzJNVLtzwtNVmSqGKpANtXhybbkhtAC94ji2
Quote Program orac1eFjzWL5R3RbbdMV68K9H6TaCVVcL6LjvQQWAbz -

Files d'Attente Par Défaut

Réseau Adresse de la File d'Attente
Mainnet A43DyUGA7s8eXPxqEjJY6EBu1KKbNgfxF8h17VAHn13w
Devnet EYiAmGSdsQTuCw413V5BzaruWuCCSDgTPtBGvLkXHbe7

Démarrage Rapide

Installation

# SDK TypeScript
npm install @switchboard-xyz/on-demand @switchboard-xyz/common

# Rust (Cargo.toml)
# switchboard-on-demand = "0.8.0"

Configuration de Base

import { web3, AnchorProvider, Program } from "@coral-xyz/anchor";
import {
  PullFeed,
  CrossbarClient,
  ON_DEMAND_MAINNET_PID,
  ON_DEMAND_DEVNET_PID
} from "@switchboard-xyz/on-demand";

// Configuration de la connexion et du fournisseur
const connection = new web3.Connection("https://api.mainnet-beta.solana.com");
const wallet = useWallet(); // ou Keypair
const provider = new AnchorProvider(connection, wallet);

// Charger le programme Switchboard
const sbProgram = await Program.at(ON_DEMAND_MAINNET_PID, provider);

// Initialiser le client Crossbar pour la communication oracle
const crossbar = new CrossbarClient("https://crossbar.switchboard.xyz");

Price Feeds

Récupérer et Mettre à Jour un Feed

import { PullFeed, asV0Tx } from "@switchboard-xyz/on-demand";

// Créer une référence de compte feed
const feedPubkey = new web3.PublicKey("YOUR_FEED_PUBKEY");
const feedAccount = new PullFeed(sbProgram, feedPubkey);

// Récupérer l'instruction de mise à jour avec les signatures oracle
const { pullIx, responses, numSuccess, luts } = await feedAccount.fetchUpdateIx({
  crossbarClient: crossbar,
  chain: "solana",
  network: "mainnet", // ou "devnet"
});

// Construire et envoyer la transaction
const tx = await asV0Tx({
  connection,
  ixs: [pullIx],
  signers: [payer],
  computeUnitPrice: 200_000,
  computeUnitLimitMultiple: 1.3,
  lookupTables: luts,
});

const signature = await connection.sendTransaction(tx);
console.log("Feed updated:", signature);

Lire la Valeur d'un Feed

// Obtenir la valeur actuelle du feed
const feedData = await feedAccount.loadData();
const value = feedData.value.toNumber();
const lastUpdated = feedData.lastUpdatedSlot;

console.log(`Price: ${value}, Last Updated: ${lastUpdated}`);

Oracle Quotes (Recommandé)

Oracle Quotes fournissent la façon la plus efficace de consommer les données oracle :

import { OracleQuote } from "@switchboard-xyz/on-demand";

// Hashes de feed (chaînes hex de 64 caractères)
const feedHashes = [
  "0x...", // SOL/USD
  "0x...", // BTC/USD
];

// Dériver le compte quote canonique
const queueKey = new web3.PublicKey("A43DyUGA7s8eXPxqEjJY6EBu1KKbNgfxF8h17VAHn13w");
const quotePubkey = OracleQuote.getCanonicalPubkey(queueKey, feedHashes);

// Récupérer l'instruction quote
const sigVerifyIx = await queue.fetchQuoteIx(crossbar, feedHashes, {
  numSignatures: 1,
  variableOverrides: {},
});

Intégration Rust (Oracle Quotes)

use anchor_lang::prelude::*;
use switchboard_on_demand::{default_queue, SwitchboardQuoteExt, SwitchboardQuote};

#[program]
pub mod my_program {
    use super::*;

    pub fn read_oracle_data(ctx: Context<ReadOracleData>) -> Result<()> {
        let feeds = &ctx.accounts.quote_account.feeds;
        let current_slot = ctx.accounts.sysvars.clock.slot;
        let quote_slot = ctx.accounts.quote_account.slot;

        // Vérifier l'obsolescence
        let staleness = current_slot.saturating_sub(quote_slot);
        require!(staleness < 100, ErrorCode::StaleFeed);

        for feed in feeds.iter() {
            msg!("Feed {}: Value = {}", feed.hex_id(), feed.value());
        }

        Ok(())
    }
}

#[derive(Accounts)]
pub struct ReadOracleData<'info> {
    #[account(address = quote_account.canonical_key(&default_queue()))]
    pub quote_account: Box<Account<'info, SwitchboardQuote>>,
    pub sysvars: Sysvars<'info>,
}

#[derive(Accounts)]
pub struct Sysvars<'info> {
    pub clock: Sysvar<'info, Clock>,
}

Surge (Streaming Temps Réel)

Pour les applications nécessitant des mises à jour de prix en temps réel :

import { SwitchboardSurge } from "@switchboard-xyz/on-demand";

// Initialiser le client Surge
const surge = new SwitchboardSurge({
  apiKey: "YOUR_API_KEY", // Optionnel
  gatewayUrl: "wss://surge.switchboard.xyz",
  autoReconnect: true,
  maxReconnectAttempts: 5,
  reconnectDelay: 1000,
});

// S'abonner aux feeds
surge.subscribe(["SOL/USD", "BTC/USD"]);

// Gérer les événements
surge.on("connected", () => {
  console.log("Connected to Surge");
});

surge.on("data", (data) => {
  console.log(`${data.symbol}: ${data.price}`);
});

surge.on("error", (error) => {
  console.error("Surge error:", error);
});

surge.on("disconnected", () => {
  console.log("Disconnected from Surge");
});

VRF Randomness

Aléa on-chain sécurisé cryptographiquement :

Client TypeScript

import { RandomnessService } from "@switchboard-xyz/on-demand";

// Demander l'aléa
const randomnessAccount = await RandomnessService.create(sbProgram, {
  queue: queuePubkey,
  callback: {
    programId: myProgramId,
    accounts: [...],
    ixData: Buffer.from([...]),
  },
});

// Révéler l'aléa (après exécution oracle)
const randomValue = await randomnessAccount.reveal();
console.log("Random value:", randomValue);

Intégration Rust

use switchboard_on_demand::RandomnessAccountData;

pub fn consume_randomness(ctx: Context<ConsumeRandomness>) -> Result<()> {
    let randomness_data = RandomnessAccountData::parse(
        ctx.accounts.randomness_account.to_account_info()
    )?;

    // Utiliser la valeur aléatoire
    let random_value = randomness_data.get_value(&ctx.accounts.clock)?;

    // Exemple : tirage au sort
    let is_heads = random_value[0] % 2 == 0;

    Ok(())
}

Créer des Feeds Personnalisés

Utiliser l'Interface Feed Builder

  1. Visiter ondemand.switchboard.xyz
  2. Cliquer sur « Create Feed »
  3. Configurer les sources de données et l'agrégation
  4. Déployer sur mainnet/devnet
  5. Copier le hash du feed pour l'intégration

Utiliser le SDK TypeScript

import { FeedBuilder } from "@switchboard-xyz/on-demand";

const feedConfig = new FeedBuilder()
  .addJob({
    tasks: [
      {
        httpTask: {
          url: "https://api.example.com/price",
        },
      },
      {
        jsonParseTask: {
          path: "$.price",
        },
      },
    ],
  })
  .setMinResponses(3)
  .setMaxVariance(0.1);

const feedHash = await feedConfig.build();

Comparaison des Frameworks

Aspect Anchor (Basique) Pinocchio (Avancé)
Courbe d'Apprentissage Adapté aux débutants Avancé uniquement
Unités de Calcul ~2 000 CU ~190 CU
Modèle de Sécurité Validation complète Cranker de confiance
Cas d'Usage DeFi standard Oracles AMM, HFT

Bonnes Pratiques

1. Vérifications d'Obsolescence

Toujours vérifier la fraîcheur du feed :

let staleness = current_slot.saturating_sub(feed_slot);
require!(staleness < MAX_STALENESS_SLOTS, ErrorCode::StaleFeed);

2. Signatures Multiples

Demander plusieurs signatures oracle pour les opérations critiques :

const { pullIx } = await feedAccount.fetchUpdateIx({
  numSignatures: 3, // Augmenter pour une sécurité plus élevée
});

3. Gestion d'Erreurs

try {
  const { pullIx, numSuccess } = await feedAccount.fetchUpdateIx({...});

  if (numSuccess < minRequired) {
    throw new Error(`Insufficient oracle responses: ${numSuccess}`);
  }
} catch (error) {
  if (error.message.includes("timeout")) {
    // Réessayer avec d'autres oracles
  }
  throw error;
}

4. Budget de Calcul

Pour les opérations complexes, augmenter le budget de calcul :

import { ComputeBudgetProgram } from "@solana/web3.js";

const modifyComputeUnits = ComputeBudgetProgram.setComputeUnitLimit({
  units: 400_000,
});

const tx = new Transaction()
  .add(modifyComputeUnits)
  .add(pullIx)
  .add(yourInstruction);

Ressources

Liens Officiels

Dépôts GitHub

Dépôt Description
switchboard Dépôt SDK principal
sb-on-demand-examples Exemples d'intégration
solana-sdk SDK Rust
on-demand SDK TypeScript

Communauté

Structure des Compétences

switchboard/
├── SKILL.md                    # Ce fichier
├── resources/
│   ├── program-ids.md          # Toutes les adresses de programme et files d'attente
│   ├── sdk-reference.md        # Référence API SDK TypeScript
│   ├── rust-reference.md       # Référence SDK Rust
│   └── github-repos.md         # Liens des dépôts
├── examples/
│   ├── setup/
│   │   └── example.ts          # Configuration de base
│   ├── feeds/
│   │   ├── pull-feed.ts        # Mises à jour de pull feed
│   │   ├── oracle-quote.ts     # Intégration oracle quote
│   │   └── read-feed.ts        # Lire les valeurs de feed
│   ├── randomness/
│   │   └── vrf-example.ts      # Aléa VRF
│   └── surge/
│       └── streaming.ts        # Streaming temps réel
├── templates/
│   └── setup.ts                # Modèle de démarrage complet
└── docs/
    └── troubleshooting.md      # Problèmes courants

Vérifier

  • Un appel RPC/SDK réel a été émis (mainnet, devnet ou validateur local) et la charge utile de réponse est capturée dans la transcription, pas seulement paraphrasée
  • Chaque transaction a été simulée (simulateTransaction ou équivalent) avant toute étape de signature/envoi ; les journaux de simulation sont joints
  • Pour toute transaction signée/envoyée, la signature résultante est enregistrée et confirmée on-chain (statut retourné par getSignatureStatuses ou une URL d'explorateur)
  • Le slippage, les frais de priorité et les limites d'unités de calcul ont été définis explicitement avec des valeurs numériques concrètes, pas laissés par défaut de la bibliothèque
  • Les adresses de compte, les mints et les IDs de programme utilisés dans l'exécution correspondent aux adresses switchboard-oracle documentées pour le cluster cible (pas de mélange mainnet/devnet)
  • Le chemin d'échec a été exercé au moins une fois (solde insuffisant, oracle obsolète, blockhash expiré, etc.) et la gestion d'erreurs de l'agent a produit un message lisible par l'homme

Skills similaires