Aller au contenu

Run an oracle provider

Merchants can price an advertisement at a margin against a reference rate instead of a fixed number — which only works if somebody publishes rates. Oracle providers do that: sign a rate, publish it to the network, and the protocol takes the median across every provider rather than trusting any single one.

Jalon 5 sur 6 · Run the infrastructure

Avant de commencer

  • A rate source you trust and are allowed to redistribute — the protocol does not supply one
  • Reachable access to an OpenFiat node's JSON-RPC endpoint, your own or someone else's
  • A persistent wallet.json for the provider's identity — reputation accrues to the key, so a fresh key each restart starts from zero
  • OPEN staked as an oracle provider, to be eligible for a share of protocol revenue

Étapes

  1. 01

    Create the provider's identity

    A provider is identified by an Ed25519 keypair in Solana CLI wallet.json format — the same file solana-keygen produces, and the same format the node itself uses. Generate it once and keep it: every record you publish is signed with it, and your reputation is attached to it.

    shell
    solana-keygen new --outfile /etc/openfiat/oracle-wallet.json
    chmod 600 /etc/openfiat/oracle-wallet.json
  2. 02

    Register in the service registry

    Registration (OFS-1500) is how other participants discover you: it advertises which service you provide, which currency pairs you cover, and which region you serve. Nobody approves it — you publish the registration and the network gossips it.

    Rust
    use openfiat_registry::Registration;
    use openfiat_sdk::{Client, ClientConfig};
    use openfiat_types::{MarketDataService, ServiceId, ServiceType, Timestamp};
    
    let client = Client::new(ClientConfig {
        endpoint: "http://localhost:7080".to_string(),
        ..ClientConfig::default()
    });
    
    let registration = Registration {
        service_id: ServiceId::new("my-oracle-1"),
        service_type: ServiceType::MarketData(MarketDataService::FxOracle),
        provider: provider_peer_id.clone(),
        provider_public_key: keypair.public_key(),
        endpoints: vec!["/ip4/203.0.113.10/udp/4001/quic-v1".to_string()],
        supported_ofs: vec![1500, 7000],
        region: Some("Kenya".to_string()),
        capabilities: vec!["USDC/KES".to_string()],
        pricing: None,
        timestamp: Timestamp::now(),
    };
    client.send_provider_register(registration, &keypair).await?;
    TypeScript
    import { Client, providers, toBytes, type Registration } from "@openfiat/sdk";
    
    const client = new Client({ endpoint: "http://localhost:7080", timeoutMs: 30_000 });
    
    const registration: Registration = {
      service_id: "my-oracle-1",
      service_type: { MarketData: "FxOracle" },
      provider: toBytes(peerId),
      provider_public_key: toBytes(keypair.publicKey),
      endpoints: ["/ip4/203.0.113.10/udp/4001/quic-v1"],
      supported_ofs: [1500, 7000],
      region: "Kenya",
      capabilities: ["USDC/KES"],
      pricing: null,
      timestamp: Date.now(),
    };
    await providers.sendProviderRegister(client, registration, keypair);
  3. 03

    Publish a signed rate

    Every record carries an explicit expiry, so a provider that goes quiet stops influencing prices rather than leaving a stale rate behind. Publish on whatever interval your source updates — and keep the expiry close to that interval rather than far beyond it.

    Rust
    use openfiat_oracles::events::OraclePublish;
    use openfiat_oracles::record::OracleData;
    use openfiat_oracles::OracleId;
    
    let now = Timestamp::now();
    let publish = OraclePublish {
        id: OracleId::new("usdc-kes"),
        provider: provider_peer_id.clone(),
        provider_public_key: keypair.public_key(),
        data: OracleData::ExchangeRate {
            base: "USDC".to_string(),
            quote: "KES".to_string(),
            rate: 129.52,
        },
        version: 1,
        timestamp: now,
        // One minute: publish at least this often, or the rate stops counting.
        expires_at: Timestamp::from_millis(now.as_millis() + 60_000),
    };
    client.send_oracle_publish(publish, &keypair).await?;
    TypeScript
    import { oracles, type OraclePublish } from "@openfiat/sdk";
    
    const now = Date.now();
    const publish: OraclePublish = {
      id: "usdc-kes",
      provider: toBytes(peerId),
      provider_public_key: toBytes(keypair.publicKey),
      data: { ExchangeRate: { base: "USDC", quote: "KES", rate: 129.52 } },
      version: 1,
      timestamp: now,
      expires_at: now + 60_000,
    };
    await oracles.sendOraclePublish(client, publish, keypair);
  4. 04

    Check your rate is counted

    The protocol takes the median across providers, so no single one moves the price on its own. Reading the median back is the quickest confirmation that your record was accepted, propagated, and is still inside its expiry window.

    shell
    curl -s -X POST http://localhost:7080/rpc -H 'content-type: application/json' \
      -d '{"jsonrpc":"2.0","id":1,"method":"getMedianExchangeRate","params":{"base":"USDC","quote":"KES"}}'
  5. 05

    Keep it publishing

    Run the publisher as a service so it restarts with the machine, and alert on your own source failing rather than on the node — a provider that publishes a confidently wrong rate is worse than one that publishes nothing, because the median only protects against a minority of bad inputs.