运行预言机提供方
商户可以选择以参考汇率为基准、按一定浮动比例为挂单定价,而不是写死一个固定价格——但这需要有人来发布汇率。预言机提供方正是做这件事:签署一个汇率并发布到网络,协议再取所有提供方的中位数,而不是信任其中任何一家。
里程碑 5 / 6 · 运行基础设施
开始之前
- 一个你信任且有权再分发的汇率数据源——协议本身不提供数据源
- 可访问某个 OpenFiat 节点的 JSON-RPC 端点,可以是你自己的节点,也可以是他人的
- 一份持久化的 wallet.json 作为提供方身份——信誉绑定在密钥上,每次重启都换新密钥会让信誉从零开始
- 以预言机提供方身份质押 OPEN,才有资格分享协议收入
操作步骤
- 01
创建提供方身份
提供方由一对 Ed25519 密钥标识,采用 Solana CLI 的 wallet.json 格式——与 solana-keygen 生成的文件相同,也与节点自身使用的格式一致。生成一次后请妥善保存:你发布的每条记录都用它签名,你的信誉也绑定在它上面。
shellsolana-keygen new --outfile /etc/openfiat/oracle-wallet.json chmod 600 /etc/openfiat/oracle-wallet.json - 02
在服务注册表中注册
注册(OFS-1500)是其他参与者发现你的方式:它公布你提供哪种服务、覆盖哪些货币对、服务哪个地区。无需任何人批准——你发布注册信息,网络会通过 gossip 传播它。
Rustuse 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?;TypeScriptimport { 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); - 03
发布经签名的汇率
每条记录都带有明确的过期时间,因此一旦提供方停止更新,它就不再影响价格,而不会留下一个陈旧的汇率。你可以按数据源的更新频率发布——并让过期时间贴近这个间隔,而不要设得过长。
Rustuse 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?;TypeScriptimport { 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); - 04
确认你的汇率已被计入
协议会取所有提供方的中位数,因此任何单一提供方都无法独自左右价格。读取中位数是最快的确认方式,可以验证你的记录已被接受、已传播,并且仍在有效期内。
shellcurl -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"}}' - 05
保持持续发布
把发布程序作为服务运行,使其随机器一起重启;并且要针对你自己的数据源故障告警,而不是针对节点——一个自信地发布错误汇率的提供方,比什么都不发布的提供方更糟糕,因为中位数只能防御少数错误输入。