使用Chainlink預言機,十分鐘開發一個DeFi專案

買賣虛擬貨幣
Chainlink價格參考資料合約是可以在智慧合約網路中值得依賴的真實價格資料的鏈上參考點。這些合約由多個 Chainlink 節點定時更新,提供高精度,高頻率,可定製化的 DeFi 價格參考資料,可以方便的為 DeFi 專案的開發提供開箱即用的穩定基礎設施。本文我們會教你如何使用這些合約。除此之外,Chainlink 還提供了透過獲取鏈下資料的方式,從使用者指定的 API 獲取價格資料。我們下面就介紹一下這兩種方式。直接從 API 獲取價格首先我們先簡單回顧一下,一般情況下我們如何使用 Chainlink 來獲取真實世界中的價格資料。我們知道,價格是透過交易來產生的,所以最直接的方式是透過交易所提供的介面來獲取某個交易所的某個加密貨幣的價格。但是這只是來自於一個交易所的資料,可能會有個體性的誤差。有一些加密貨幣行情網站,他們會彙總多個交易所的資料,或者根據自己的指標來計算資料,得到一個偏離度比較小也就是更真實的資料。所以我們就採用從行情網站的介面獲得資料,然後透過提交交易,將價格資料送到智慧合約中。我們選擇的行情網站的 cryptocompare,它提供了一些非常好用的 API 來提供各類交易市場上的資訊。我們就以它文件上給出的一個 API 來作為例子:https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=USD,JPY,EUR訪問這個介面,會返回一個 JSON 物件,提供當前時間,BTC 分別相對於美元、日元、歐元的價格。
{    "USD": 9460.99,    "JPY": 1018295.17,    "EUR": 8640.8}好的,下面我們就來編寫合約來獲取 BTC 的美元價格。
1 建立 truffle 專案mkdir MyChainlinkedContractcd MyChainlinkedContracttruffle init如果您還沒有安裝 truffle suite, 可以透過 npm install truffle -g 來安裝。2 安裝 Chainlink 開發庫
npm install @chainlink/contracts --save3 建立使用者合約您可以用您喜歡的編輯器工具,比如 VS Code, 開啟專案目錄。目錄結構如下:.├── contracts│   └── Migrations.sol
├── migrations│   └── 1_initial_migration.js├── test└── truffle-config.j我們在 contracts 目錄中新建一個合約檔案 MyContract.sol,在檔案中新建一個合約,繼承自 ChainlinkClient 合約,並設定建構函式,引數分別是:1._link 所使用網路環境下的 LINK token 地址
2._oracle 所使用的 Oracle 合約地址。如果您不知道選擇什麼哪個 Oracle,可以前往 Chainlink 市場 market.link 上選擇。3._specId 即 jobId,用於完成規範命令序列的任務 ID,同樣可在 Chainlink 市場 market.link 上對應的 Oracle 下選擇。pragma solidity ^0.6.0;import "@chainlink/contracts/src/v0.6/ChainlinkClient.sol";// MyContract 透過繼承 Chainlinked 合約獲得了建立Chainlink請求的功能contract MyContract is ChainlinkClient {
  constructor(address _link, address _oracle, bytes32 _specId) public {    setChainlinkToken(_link);    setChainlinkOracle(_oracle);    specId = _specId;  }    bytes32 internal specId;
}接下來我們就可以編寫建立 Chainlink 請求的程式碼  function requestEthereumPrice(string memory _currency, uint256 _payment) public {    requestEthereumPriceByCallback(_currency, _payment, address(this));  }  function requestEthereumPriceByCallback(string memory _currency, uint256 _payment, address _callback) public {
    Chainlink.Request memory req = buildChainlinkRequest(specId, _callback, this.fulfill.selector);    req.add("get", "https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=USD,EUR,JPY");    string[] memory path = new string[](1);    path[0] = _currency;    req.addStringArray("path", path);    sendChainlinkRequest(req, _payment);
  }其中的主要語句就是透過 buildChainlinkRequest 建立了一個 Chainlink 請求,該請求會發起一次 LINK 的轉賬到 Oracle 地址,並附帶請求資料。請求資料可以透過 add 的方法新增到請求中。請求資料可以包括:請求地址、解析路徑、倍數等。另外我們還需要定義一個回撥函式來接收 Oracle 獲取到的結果,這個函式需要作為引數在構建 Chainlink 請求時傳入到函式 buildChainlinkRequest 中: event RequestFulfilled(    bytes32 indexed requestId,  // User-defined ID    bytes32 indexed price
  );    function fulfill(bytes32 _requestId, bytes32 _price)    public    recordChainlinkFulfillment(_requestId)  {    emit RequestFulfilled(_requestId, _price);
    currentPrice = _price;  }這樣其實一個最簡單的 Chainlink 消費者合約就建立完成了,下面是一段完整的程式碼,當然你也可以在此之上新增一些其他函式,比如提取 LINK、取消請求等等。pragma solidity ^0.6.0;import "@chainlink/contracts/src/v0.6/ChainlinkClient.sol";contract MyContract is ChainlinkClient {
  constructor(address _link, address _oracle, bytes32 _specId) public {    setChainlinkToken(_link);    setChainlinkOracle(_oracle);    specId = _specId;  }  bytes32 internal specId;
  bytes32 public currentPrice;  event RequestFulfilled(    bytes32 indexed requestId,  // User-defined ID    bytes32 indexed price  );  function requestEthereumPrice(string memory _currency, uint256 _payment) public {
    requestEthereumPriceByCallback(_currency, _payment, address(this));  }  function requestEthereumPriceByCallback(string memory _currency, uint256 _payment, address _callback) public {    Chainlink.Request memory req = buildChainlinkRequest(specId, _callback, this.fulfill.selector);    req.add("get", "https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD,EUR,JPY");    string[] memory path = new string[](1);
    path[0] = _currency;    req.addStringArray("path", path);    sendChainlinkRequest(req, _payment);  }  function fulfill(bytes32 _requestId, bytes32 _price)    public
    recordChainlinkFulfillment(_requestId)  {    emit RequestFulfilled(_requestId, _price);    currentPrice = _price;  }}
4 將使用者合約部署到 Ropsten 測試網路上我們配置好 truffle 的 config 檔案中的 Ropsten network 欄位和相關的 provider,新增一個 migration 檔案,透過下面的命令,將我們的使用者合約部署到 Ropsten 測試網路上。truffle migrate --network ropsten5 向合約地址轉入LINK由於發起 Chainlink 請求需要向 Oracle 支付 LINK 作為費用,所以我們的使用者合約需要擁有 LINK 才能成功獲取資料。所以我們需要向剛剛部署好的 MyContract 合約轉入一部分 LINK。每次請求的費用可以根據所選擇的 Oracle 要求的費用來支付。6 編寫測試指令碼
接下來我們就可以呼叫合約來獲取我們想要的資料了。我們可以使用 truffle 給我們提供的控制檯,也可以自己編寫指令碼檔案來測試。指令碼檔案的編寫也非常簡單,我們寫一個請求的檔案和一個讀取的檔案request.jsconst MyContract = artifacts.require('MyContract')module.exports = async callback => {  const mc = await MyContract.deployed()  const tx = await mc.requestEthereumPrice('USD', '1000000000000000000')
  callback(tx.tx)}read.jsconst MyContract = artifacts.require('MyContract')module.exports = async callback => {
  const mc = await MyContract.deployed()  const data = await mc.currentPrice.call()  callback(console.log(parseInt(data)))}透過以下命令來呼叫請求指令碼:npx truffle exec scripts/request.js  --network ropsten
成功之後稍等一段時間,因為需要以太坊網路對交易的確認,然後再透過以下命令讀取 Oracle 獲取到的資料。npx truffle exec scripts/read.js  --network ropsten順利的話,控制檯上會打出當前 BTC 的價格 9352這樣,我們就以一個非常直接的方式,透過一個我們指定的 API,完成了一個在合約中獲取 BTC 價格資料的用例。價格參考資料 Reference Data下面我們就來講一下 Chainlink 價格參考資料合約應該怎麼來使用。
使用價格參考資料合約和上面的直接從鏈下獲取的方式是有很打不同的,我們先來了解一下 Chainlink 的價格參考資料合約。Chainlink 的價格參考資料是專門為 DeFi 專案設計的預言機,網址是 feeds.chain.link。Chainlink 的價格參考資料預言機網路極大提升了以太坊上 Dapp 資料的安全性和可靠性,而且大大加速了新 DeFi 產品成功上線的速度。Chainlink 在去中心化的預言機網路中提供價格參考資料,這是一個共享資源社羣,並受到使用者支援。使用者使用這些預言機網路的成本要低於自己傳輸資料的成本,而且由於預言機網路是去中心化的,安全水平也會大幅提升。更多的資訊可以參考這篇博文。簡單來說,Chainlink 價格參考資料提供的價格是在鏈上可以直接訪問的,不需要透過我們指定 API 來手動獲取。1 新建專案我們新建一個目錄就叫 RefereceData,然後用上面的的方式建立一個 truffle 專案:mkdir RefereceData
cd RefereceDatatruffle initnpm install @chainlink/contracts --save2 新建使用者合約檔案在 constracts 目錄下新建一個 ReferenceConsumer.sol 檔案,檔案中需要引入聚合介面合約 AggregatorInterface:import "@chainlink/contracts/src/v0.4/interfaces/AggregatorInterface.sol";
3 找到我們需要的參考資料合約地址Chainlink文件中給我們提供了非常多交易對的參考合約地址,不僅有主網的地址,還有 Ropsten、Rinkeby、Kovan 測試網的合約地址。我們就選擇 Ropsten 網路下的 BTC/USD 交易對的合約,地址為 0x882906a758207FeA9F21e0bb7d2f24E561bd09814 配置參考合約地址在合約中透過建構函式或者編寫 setter 方法,配置好我們想要的參考合約的地址建構函式方式:
AggregatorInterface internal ref;constructor(address _aggregator) public {  ref = AggregatorInterface(_aggregator);}setter 方式:AggregatorInterface internal ref;
function setReferenceContract(address _aggregator)  public  onlyOwner(){  ref = AggregatorInterface(_aggregator);}
5 使用參考合約獲取價格資料AggregatorInterface 介面給我們提供了 5 個方法供我們使用分別是:latestAnswer() 最新的聚合結果latestTimestamp() 最新一次聚合的時間戳latestRound() 最新一次聚合的輪次號getAnswer(uint256 roundId) 透過輪次號獲取歷史結果
getTimestamp(uint256 roundId) 透過輪次號獲取歷史時間戳返回的價格結果中,所有 USD 參考資料合約的結果值會乘以 100000000,所有 ETH 參考資料合約的結果值會乘以 1000000000000000000。6 完整示例我們分別就對這個幾個方法做個包裝就可以然後就可以獲取到 BTC/USD 的價格啦。下面的一段完整的程式碼,大家可以在這個基礎上,加入一些其他的業務邏輯,就可以建立一個 DeFi 專案啦。pragma solidity ^0.4.24;import "@chainlink/contracts/src/v0.4/interfaces/AggregatorInterface.sol";
contract ReferenceConsumer {  AggregatorInterface internal ref;  constructor(address _aggregator) public {    ref = AggregatorInterface(_aggregator);  }  function getLatestAnswer() public view returns (int256) {
    return ref.latestAnswer();  }  function getLatestTimestamp() public view returns (uint256) {    return ref.latestTimestamp();  }  function getLatestRound() public view returns (uint256) {
    return ref.getLatestRound();  }  function getPreviousAnswer(uint256 _back) public view returns (int256) {    uint256 latest = ref.latestRound();    require(_back <= latest, "Not enough history");    return ref.getAnswer(latest - _back);
  }  function getPreviousTimestamp(uint256 _back) public view returns (uint256) {    uint256 latest = ref.latestRound();    require(_back <= latest, "Not enough history");    return ref.getTimestamp(latest - _back);  }
}7 編寫測試指令碼呼叫合約在 Migrations 目錄下新建一個檔案 2_referenceconsumer_migration.js ,將我們上面查到的 BTC/USD 的參考合約地址在部署時傳入建構函式中:const ReferenceConsumer = artifacts.require("ReferenceConsumer");module.exports = function(deployer) {  deployer.deploy(ReferenceConsumer, "0x882906a758207FeA9F21e0bb7d2f24E561bd0981");
};然後部署到 Ropsten 網路上。在 scripts 目錄下新建一個測試檔案,比如叫 getdata.jsconst ReferenceConsumer = artifacts.require('ReferenceConsumer')module.exports = async callback => {  const rc = await ReferenceConsumer.deployed()
  const data = await rc.getLatestAnswer()  callback(console.log(parseInt(data)))}透過以下命令執行該測試指令碼:npx truffle exec scripts/getdata.js  --network ropsten一切順利的話就會在控制檯得到 920089000000 的結果,這就是當前 BTC 的價格,即 9200.89。
有了 Chainlink 的價格參考資料合約,DeFi 的開發變得非常簡單了,開發者只需要關注自己的金融方面的業務邏輯即可。https://feeds.chain.link網站上列出了非常多的使用 Chainlink 價格參考資料的 DeFi 專案,他們都是開源的,我們也可以去他們的 GitHub 頁面,去學習如何用 Chainlink 來設計一個 DeFi 專案。

免責聲明:

  1. 本文版權歸原作者所有,僅代表作者本人觀點,不代表鏈報觀點或立場。
  2. 如發現文章、圖片等侵權行爲,侵權責任將由作者本人承擔。
  3. 鏈報僅提供相關項目信息,不構成任何投資建議

推荐阅读

;