分散式價格預言機
當消費者要求預言機服務時,預言機可能會因為各種各樣的原因無法及時回應,造成單點故障。因此,ChainLink 採用了分散式價格預言機的設計來為用戶提供服務。例如,一個提供BTC 美元價格的服務,聚合了31 個價格預言機為使用者提供服務。
此聚合器的合約原始碼可以在Etherscan 上查看:https://etherscan.io/address/0xae74faa92cb67a95ebcab07358bc222e33a34da7#readContract
其中,透過呼叫合約中的transmitters 方法即可查看該聚合器所包含的所有鏈下預言機。
每一個鏈下預言機可以透過呼叫transmit 方法來提供價格數據,以回應聚合器中使用者的請求。這些鏈下預言機是一些EOA 帳戶,他們不僅為BTC/USD 聚合器提供價格數據,還可能為其他聚合器提供價格數據,例如ETH/USD。
鏈上合約:
1. 首先,讀取目前合約狀態,並進行一連串的檢查:
2. 這些都通過後,可以進行一些準備了:
3. 接下來是使用ecrecover() 對每一個簽名資料進行驗簽,校驗hash 值是對_report 做的hash。同時也要檢查簽名者的角色是否為Signer,且要檢查簽名的重複性。
4. 最後,檢查觀察值是否依照順序排列好。再從排好順序的觀察值中選取中位數median,並確保median 不超過上下兩個閾值。一切都沒問題後,在s_transmissions 中記錄下本次預言機的answer。此外,也要對answer 進行校驗:
這裡經過一連串(中間有Proxy 合約)的call 最終呼叫了 UniswapAnchoredView 合約(Compound 使用的價格預言機) 的 validate 方法:
關鍵是比較了兩邊預言機給的價格的偏差是否在一個範圍內:
Feed Registry
前面的使用方式雖然已經很簡單,但如果需要不同token 的價格就得對每個token 執行 setPriceFeed,治理成本其實有點高,對某些場景來說就不太靈活。這時候,就可以考慮使用 Feed Registry 的方式來存取。
Feed Registry 可以簡單理解為 PriceFeeds 的聚合器,已經聚合了多個priceFeed,有了它,用戶就無需自己去設置priceFeed 了,可直接透過Feed Registry 讀取價格數據,如下圖:
餵價機制
首先,Price Feed 的價格是透過多個層級的資料聚合得到的。其實有三個資料聚合層:資料來源聚合、節點運營商聚合、預言機網路聚合。
最原始的價格資料主要來自幣安、火幣、Coinbase 等中心化交易平台,以及 Uniswap、Sushi 等去中心化交易平台。存在一些專門做數據聚合的服務商(例如 amberdata、CoinGecko),會從這些交易平台收集原始的價格數據,並對這些數據來源進行加工整合,例如根據交易量、流動性和時差等進行加權計算。
這就是第一個層面的聚合,對資料來源的聚合。擁有可靠的價格資料來源的關鍵是要有全面的市場覆蓋,才能確保一個價格點能代表所有交易環境的精確聚合,而不是單一交易所或少數交易所的價格,以防止資料被人為操縱和出現價格偏差。
第二層則是 Chainlink Node Operators 所做的聚合。每個Chainlink Node Operator 主要負責運行用於在區塊鏈上獲取和廣播外部市場數據的Chainlink 核心軟體。 Node Operators 會從多個獨立的數據聚合服務商取得價格數據,並取得它們之間的中位數,剔除異常值和API 停機時間。
最後一層則是整個預言機網路的聚合,其聚合的方式有多種,但最常見的聚合方式是當回應節點數量達到預設值時對資料取中位數。例如總共有31 個節點,預設值為21 ,也就是收到了21 個節點的回應後,就取這些節點的價格資料的中位數作為最終的價格。不過,並非每一輪的價格結果都會更新到鏈上,只有滿足兩個觸發參數之一的時候才會更新:偏差閾值(Deviation Threshold)和心跳閾值(Heartbeat Threshold)。而且,不同PriceFeed 的這兩個參數的值可能會不一樣。
總而言之,Chainlink 價格預言機接入方便,且安全性還是比較高的,但因為其價格更新機制存在偏差閾值,導致價格更新比較慢,短則幾分鐘或幾十分鐘更新一次,長則可能達24 小時才更新一次,因此,一般只適用於對價格更新不太敏感的應用。這也是Chainlink 價格預言機的局限性,並無法適用所有場景的應用。