對於Web3 應用而言,獲取加密資產的數據是一個很常見的價格要求,許多協議都需要依賴於開發和及時更新的數據來運營DeFi 應用並保證其安全性。除此之外,智能合約者有的時候也需要獲取加密資產的歷史數據。
在這篇文章中,我們將展示如何從Chainlink 價格信息 中獲得歷史價格數據,並且在上鍊驗證獲得的結果,你可以在這裡查看代碼。
獲得歷史價格數據的需求
在過去的這些年中,我們見證了DeFi 爆炸式增長,DeFi 的共同需求是他們需要非常安全、準確和值得信任的數據。 Chainlink Price Feeds 已經成為DeFi 生態中最常見的的價格機,並且集成了一個百億美元的協議,比如 阿夫,合成器 和 交易員喬..
Price Feed 的常見使用場景是從對中獲取價格的數據。然而,有DeFi 的應用程序還有查看各種資產在當前時間點的最新情況的歷史記錄。例如一個金融產品會比較不同段的資產價格,例如加密資產保險使用歷史來計算出波動率,然後動態調整市場數據。
歷史價格數據可以通過很多市場數據的API 被獲取,然後通過Chainlink 任何API 的功能在鏈上獲取。然而這個解決方案有安全性的顧慮:就是數據源和傳輸數據的這個機器沒有可能是中心化的,並且驗證數據是否是準確的。就像實時價格數據的方法,歷史數據需要去中心化價格的多種數據源,同時也需要使用API 的數據源,否則不能使用。
當通過價格數據的時間,Price Feeds 可以保證價格數據是準確的,同時提供價格數據的歷史時間點,提供給中心化的價格數據問題,同時提供多個中心化的價格數據。數據來源是整個市場,而不是跨境。
當然,還有一些其他解決方案來獲取Chainlink Price Feed 的數據,比如 Reputation.link 的API 或者是 Graph 的一個subGraph儘管這些有效的解決方案,但依賴於單一API或者鏈下數據都是正確的這個條件。
在這個解決方案中,我們將展示使用Chainlink 語言來進行必要的鏈下計算,然後從Chainlink Price Feeds 中獲得歷史數據。上獲得歷史價格數據。
Price Feed 簡介
從用戶合約的視角來看,Chainlink Price Feeds智能合約大體上可以分為兩種類型:代理合同和聚合合約。
代表合約代表合約(就像這個ETH,同時對用戶需要獲取一個合約價格的最新合約。在一輪中獲取價格數據。
代理合約現在所有的聚合合約,同時也可以聚合合約。看到 高文ETH/USD 這個代理合同中,第二個聚合合同正在被使用(階段ID = 2)。
這些聚合合約在的時候有點不同,但是它們的這些數據是在哪個版本上實現的(FluxAggregator,legacy Aggregator 等等),都存儲了聚合,同時也有一個函數讓家鄉機器可以提交價格數據。
解決方案概述
Chainlink Price Feeds 的價格數據是鏈上最新的,同時開發者`getLatestRoundData` 函數來獲取比較簡單的一個交易的價格數據。然而,從Price Feeds 中獲取數據卻歷史並不復雜,過程執行複雜。
Chainlink Price Feeds 存儲聚合以後的數據,有一個獨特的回合ID。當價格對每個變量的特性超過的目標物值,或者超過心跳時間如果開發者知道歷史價格數據的回合ID,那麼就可以很容易通過 獲取歷史價格 然而,roundID決定和價格,區塊,或者任何其他可以使用的變量都沒有直接的時間聯繫。
另外注意的是,Chainlink Price Feed 有很多版本要聚合的契約,比如 通量聚合器 和使用鏈下報告 鏈下聚合器 一個交易是可能在以前的聚合價使用然後對合約有可能在以前的聚合價使用的Fluxator,換成Ag OCR 的Offchain Aggregator 來更新的輪次ID 時間。因此,不同的不同的聚合版本。
所以在給與的合同中的總ID比聚合多個。是需要自增1而的聚合合約在每次部署的時候輪ID重新計算都從1開始。下面的方法,可以由聚合的輪ID計算出代理合約的輪ID,初始階段是通過你可以通過調用代理的`phaseAggregators`這個getter方法來獲取階段。
external proxy round ID = uint80(uint256(phase) << 64 | originalRoundId);
最後,每一個輪次都有價格。有一些輪次(可能是可能沒有或在測試價格數據,主要是因為當時連接數據或環境問題。
因為這些複雜的數據在上得到了一個可以且準確的數據,比如你做了一個循環數據,需要遍歷一遍,或者上一個循環驗證和在鏈的映射,但是這些操作都非常貴。
除了給智能合約提供鏈上數據時間,Chainlink 去化一個計算機網絡的通用框架來做鏈下協議,這樣用戶就不用存儲大量的鏈上數據,也用對未知數量的一輪可以通過外部循環上的合約(鏈外適配器)運行一個外部循環上的合約(鏈外適配器)來對一輪ID進行循環調用。示範機把ID傳到返回上,然後用戶合約可以馬上使用這個數據,並通過歷史數據API 驗證鏈上數據來驗證數據比較的結果,驗證方式是通過圓形ID 獲取價格數據,然後返回的價格數據。
這個解決方案所基於的概念是:雞肉機會處理區塊鏈本身所不能處理的數據歷史,或者這種數據鍊或除因為容量限制和效率不能處理的問題。除此之外,通過方式獲得價格,還有很多優勢:
- 不需要在鏈上存儲的數據,或者對鏈上數據進行大量的循環檢查。
- 使用鏈上比較的函數獲取歷史,以及從鏈上取得的數據,杜絕下禽數據機提供錯誤數據的風險。
- 像這樣的數據是沒有任何狀態的,不會像Chainlink 的數據商運營存儲一樣,並且提供一個通用的來獲取歷史數據。
- 它不提供其他的解決方案,或者直接與外部系統鏈接上鍊接API。
怎樣使用Chainlink 獲得加密資產歷史價格數據
創建一個歷史價格數據請求
初始化一個歷史價格數據請求,用戶合同需要給一個之前的雞肉機提交一個API 請求,這個雞肉在自己的任務中需要運營一個自定義的價格數據外部異常。 。在發送參數中,用戶需要發出請求有效的代理合約地址和返回價格(Unix時間)以。
function getHistoricalPrice(address _proxyAddress, uint _unixTime) public returns (bytes32 requestId) { Chainlink.Request memory request = buildChainlinkRequest(jobId, address(this), this.singleResponseFulfill.selector); // Set the URL to perform the GET request on request.add("proxyAddress", addressToString(_proxyAddress)); request.add("unixDateTime", uint2str(_unixTime)); //set the timestamp being searched, we will use it for verification after searchTimestamp = _unixTime; //reset any previous values answerRound = 0; previousRound = 0; nextRound = 0; nextPrice = 0; nextPriceTimestamp = 0; previousPrice = 0; previousPriceTimestamp = 0; priceAnswer = 0; priceTimestamp = 0; // Sends the request return sendChainlinkRequestTo(oracle, request, fee); }
歷史價格數據
這個鏈接之後,它輸入信息給歷史請求。輪盤查驗中的信息是要來的,我們要在最接近位置的時間點更新,需要通過這個輪盤查詢的數據來查詢價格小。
它隨後接收到外部會合同的地址和進行搜索的後續代理的計算:
- 對地址和地址進行驗證
- 確定哪一個蘊藏要蘊藏的聚合的圓
- 在上一步找到的合約中,獲取存儲在聚合聚合中的輪ID列表(使用列表 eth_getlogs)
- 在返回的回合ID列表中,進行二分查找,找到這個包含時間的圓形ID。 O(logN),的時間,搜索的複雜度是 上)
- 如果是空的或者是利用這個循環進行二分法的),就會發現在二分查找中的空值,然後圓掉,再在新的中新的這些分把查找等原因。
- 當被查找的圓形ID 被找到之後,前面的某個ID 返回會使用這個聚合合同的圓形ID,以及它和後面的圓形ID,算出這三個圓形ID 在合同中的圓形ID,然後在一個圓形ID 中roundAnswer`,`earlierRoundAnswer` 和`laterRoundAnswer` 這三個值。
- 處理歷史價格數據的請求的價值有機獲取上一步的三個,然後在通過 多變量響應 的功能鏈上返回給鏈上的用戶契約。
{ "jobRunID": "534ea675a9524e8e834585b00368b178", "data": { "roundAnswer": "36893488147419111519", "earlierRoundAnswer": "36893488147419111518", "laterRoundAnswer": "36893488147419111520" }, "result": null, "statusCode": 200 }
在鏈上驗證結果
使用中心化數據源或者雞安全機來獲得價格數據,贈送智能合約帶來潛在的潛在的。然而,在這個例子中,我們對於雞的價格回合ID,可以利用數據庫的歷史風險返回數據函數在這種方式中,數據還是從鏈上獲取,鏈輪ID是由外部的計算機計算出來的。 。
我們可以通過下面的方式來驗證圓形數據,然後使用這個數據來獲取最終的歷史數據:
首先,我們需要驗證三個回合(answerRound, previousRound, nextRound)中包含的有效返回回合數據
//verify the responses //first get back the responses for each round ( uint80 id, int price, uint startedAt, uint timeStamp, uint80 answeredInRound ) = priceFeed.getRoundData(_answerRound); require(timeStamp > 0, "Round not complete"); priceAnswer = price; priceTimestamp = timeStamp; ( id, price, startedAt, timeStamp, answeredInRound ) = priceFeed.getRoundData(_previousRound); require(timeStamp > 0, "Round not complete"); previousPrice = price; previousPriceTimestamp = timeStamp; ( id, price, startedAt, timeStamp, answeredInRound ) = priceFeed.getRoundData(_previousRound); require(timeStamp > 0, "Round not complete"); nextPrice = price; nextPriceTimestamp = timeStamp;
數據以及它們包含的這些數據。
- 保證輪次的順序是正確的
- 保證三個圓中包含的時間順序是對的
- 如果這些回合中有任何的間隔(例如previousRound = 1625097600和answerRound = 1625097605),請確保在這兩個ID之間有效的回合ID。並且沒有在這個例子中,previousRound = 1625097600 answerRound = 1625097605,這個這些合同需要保證1625097601、1625097602、1625097603 和1625097604 回合不會返回有效數據
//first, make sure order of rounds is correct require(previousPriceTimestamp < timeStamp, "Previous price timetamp must be < answer timestamp"); require(timeStamp < nextPriceTimestamp, "Answer timetamp must be < next round timestamp"); //next, make sure prev round is before timestamp that was searched, and next round is after require(previousPriceTimestamp < searchTimestamp, "Previous price timetamp must be < search timestamp"); require(searchTimestamp < nextPriceTimestamp, "Search timetamp must be < next round timestamp"); require(priceTimestamp <= searchTimestamp, "Answer timetamp must be less than or equal to searchTimestamp timestamp"); //check if gaps in round numbers, and if so, ensure there's no valid data in between if (answerRound - previousRound > 1) { for (uint80 i= previousRound; i<answerRound; i++) { (uint80 id, int price, uint startedAt, uint timeStamp, uint80 answeredInRound ) = priceFeed.getRoundData(i); require(timeStamp == 0, "Missing Round Data"); } } if (nextRound - answerRound > 1) { for (uint80 i= answerRound; i<nextRound; i++) { (uint80 id, int price, uint startedAt, uint timeStamp, uint80 answeredInRound ) = priceFeed.getRoundData(i); require(timeStamp == 0, "Missing Round Data"); } }
如果歷史都可以通過,那麼返回(answerRound)的價格數據,就可以在這個時間點上驗證一下歷史測試的價格數據。
總結
Chainlink Price Feeds 是一種讓Solidity 合同獲得者獲得優質價格數據的智能方法。除此之外,Chainlink 的雞肉機框架可以實現鏈下計算,允許開發者通過民間化的模式,獲得安全可驗證的歷史價格數據。
您可以關注鏈家禽機資料和私信加入者社區,有大量關於智能合約開發的學習以及關於區塊鏈的話題!