跳到主要内容

运行预言机提供方

商户可以选择以参考汇率为基准、按一定浮动比例为挂单定价,而不是写死一个固定价格——但这需要有人来发布汇率。预言机提供方正是做这件事:签署一个汇率并发布到网络,协议再取所有提供方的中位数,而不是信任其中任何一家。

里程碑 5 / 6 · 运行基础设施

开始之前

  • 一个你信任且有权再分发的汇率数据源——协议本身不提供数据源
  • 可访问某个 OpenFiat 节点的 JSON-RPC 端点,可以是你自己的节点,也可以是他人的
  • 一份持久化的 wallet.json 作为提供方身份——信誉绑定在密钥上,每次重启都换新密钥会让信誉从零开始
  • 以预言机提供方身份质押 OPEN,才有资格分享协议收入

操作步骤

  1. 01

    创建提供方身份

    提供方由一对 Ed25519 密钥标识,采用 Solana CLI 的 wallet.json 格式——与 solana-keygen 生成的文件相同,也与节点自身使用的格式一致。生成一次后请妥善保存:你发布的每条记录都用它签名,你的信誉也绑定在它上面。

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

    在服务注册表中注册

    注册(OFS-1500)是其他参与者发现你的方式:它公布你提供哪种服务、覆盖哪些货币对、服务哪个地区。无需任何人批准——你发布注册信息,网络会通过 gossip 传播它。

    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

    发布经签名的汇率

    每条记录都带有明确的过期时间,因此一旦提供方停止更新,它就不再影响价格,而不会留下一个陈旧的汇率。你可以按数据源的更新频率发布——并让过期时间贴近这个间隔,而不要设得过长。

    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

    确认你的汇率已被计入

    协议会取所有提供方的中位数,因此任何单一提供方都无法独自左右价格。读取中位数是最快的确认方式,可以验证你的记录已被接受、已传播,并且仍在有效期内。

    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

    保持持续发布

    把发布程序作为服务运行,使其随机器一起重启;并且要针对你自己的数据源故障告警,而不是针对节点——一个自信地发布错误汇率的提供方,比什么都不发布的提供方更糟糕,因为中位数只能防御少数错误输入。