Skip to content

Chainlink预言机

Chainlink Moonbeam Banner

概览

开发者现可以使用Chainlink去中心化的预言机网络在Moonbeam网络上获取数据。Price Feeds(喂价)包含由预言机运营商在智能合约持续更新的实时价格数据,便于其他智能合约的获取及使用。本教程将介绍可用的喂价以及如何在Moonriver上获取最新价格数据。

基本请求模型

在开始获取数据本身之前,您必须先了解“基本请求模型”的基础信息。从Chainlink预言机请求和接收数据的一般流程如下所示:

  1. 客户合约创建并向Chainlink预言机发出数据请求

  2. 请求通过LINK Token合约的transferAndCall函数发出。LINK Token是一个符合ERC-677的Token,即允许转移Token并将请求传输给预言机合约

  3. 转移Token后,LINK合约将调用预言机合约的onTokenTransfer函数

  4. 预言机合约由预言机节点运营者管理,负责处理通过LINK Token发出的链上请求。预言机合约收到请求后,将会向节点发出一个事件,并由该节点执行操作

  5. 预言机节点完成请求后,节点使用预言机合约的fulfillOracleRequest函数,通过在原始请求中定义的回调函数将结果返回给客户合约

Basic Request Diagram

当通过客户合约创建数据请求后,需要传入以下参数以确保交易能够进行并返回正确信息:

  • Oracle address —— 预言机节点部署合约的地址
  • Job ID —— 要执行的任务。一个预言机节点有一组Job ID,其中每个Job对应一个用户可以请求的任务,例如:获取喂价
  • Payment —— 预言机为完成请求而收到的LINK Token款项

获取数据

您可以通过以下几种方式开始从Moonbeam上的预言机获取数据:

  • 您可以使用依赖于Moonbeam运行的预言机节点的预部署的客户端合约、LINK Token合约、以及预言机合约
  • 您可以创建自己的自定义合约,取代与预部署的客户端合约一起使用的预部署的客户端合约
  • 您可以创建自己的自定义客户端合约,并运行自己的预言机节点

Moonbeam运行的预部署合约和预言机节点支持一组有限的job ID,可用于获取各种资产对的价格数据。如果您需要额外数据,请参考使用您自己的预言机节点以创建自定义合约段落,了解如何开始。

请注意,客户端合约必须拥有LINK Token余额用于支付请求。对于预部署的设置,LINK值已经被设为0。如果您部署自己的设置,您也可以在您的ChainlinkClient.sol 合约设置LINK值为0,并且您可以选择部署您自己的LINK Token合约或者使用预部署的合约。

使用预部署的合约

如果您想要跳过部署所有合约、设置您自己预言机节点、创建job ID等等障碍,您可以使用一个已经部署在Moonbase Alpha上的自定义客户端合约。该合约向一个已经部署的预言机合约发出所有请求,无需支付LINK Token。这些请求由Moonbeam团队运行的预言机节点完成。

部署在Moonbase Alpha的客户端合约如下:

pragma solidity ^0.6.6;

import "https://github.com/smartcontractkit/chainlink/blob/develop/contracts/src/v0.6/ChainlinkClient.sol";

/**
 * @title Client based in ChainlinkClient
 * @notice End users can deploy this contract to request the Prices from an Oracle
 */
contract Client is ChainlinkClient {
  // Stores the answer from the Chainlink oracle
  uint256 public currentPrice;
  address public owner;

  // Deploy with the address of the LINK token
  constructor(address _link) public {
    // Set the address for the LINK token for the network
    setChainlinkToken(_link);
    owner = msg.sender;
  }

  // Creates Chainlink Request
  function requestPrice(address _oracle, string memory _jobId, uint256 _payment) 
    public
    onlyOwner
  {
    // newRequest takes a JobID, a callback address, and callback function as input
    Chainlink.Request memory req = buildChainlinkRequest(stringToBytes32(_jobId), address(this), this.fulfill.selector);
    // Sends the request with the amount of payment specified to the oracle
    sendChainlinkRequestTo(_oracle, req, _payment);
  }

  // Callback function called by the Oracle when it has resolved the request
  function fulfill(bytes32 _requestId, uint256 _price)
    public
    recordChainlinkFulfillment(_requestId)
  {
    currentPrice = _price;
  }

  // Allows the owner to cancel an unfulfilled request
  function cancelRequest(
    bytes32 _requestId,
    uint256 _payment,
    bytes4 _callbackFunctionId,
    uint256 _expiration
  )
    public
  {
    cancelChainlinkRequest(_requestId, _payment, _callbackFunctionId, _expiration);
  }

  // Allows the owner to withdraw the LINK tokens in the contract to the address calling this function
  function withdrawLink()
    public
    onlyOwner
  {
    LinkTokenInterface link = LinkTokenInterface(chainlinkTokenAddress());
    require(link.transfer(msg.sender, link.balanceOf(address(this))), "Unable to transfer");
  }

  // Decodes an input string in a bytes32 word
  function stringToBytes32(string memory _source)
    private
    pure
    returns (bytes32 result) 
  {
    bytes memory emptyStringTest = bytes(_source);
    if (emptyStringTest.length == 0) {
      return 0x0;
    }

    assembly { // solhint-disable-line no-inline-assembly
      result := mload(add(_source, 32))
    }

    return result;
  }

  // Reverts if the sender is not the owner of the contract
  modifier onlyOwner() {
    require(msg.sender == owner);
    _;
  }
}

合约的核心函数如下:

  • constructor —— 当合约部署时运行。设置LINK Token地址和合约的所有者
  • requestPrice —— 需要预言机合约地址、job ID,以及支付Token(LINK)给请求的完成者。构建使用来自ChainlinkClient.sol 导入的sendChainlinkRequestTo 函数来发送新的请求
  • fulfill —— 预言机节点用于通过存储在合约中的查询信息来完成请求的回调

请注意,客户端合约必须拥有LINK Token余额,以能够支付请求。然而,在本例中,LINK值被设置为0。

客户端合约被部署于0x8ea35EdC1709ea0Ea2C86241C7D1C84Fd0dDeB11。您可以通过接口合约,试着与客户端合约交互:

pragma solidity ^0.6.6;

/**
 * @title Simple Interface to interact with Universal Client Contract
 * @notice Client Address 0x8ea35EdC1709ea0Ea2C86241C7D1C84Fd0dDeB11
 */
interface ChainlinkInterface {

  /**
   * @notice Creates a Chainlink request with the job specification ID,
   * @notice and sends it to the Oracle.
   * @notice _oracle The address of the Oracle contract fixed top
   * @notice _payment For this example the PAYMENT is set to zero
   * @param _jobId The job spec ID that we want to call in string format
   */
    function requestPrice(string calldata _jobId) external;

    function currentPrice() external view returns (uint);

}

这提供了两个函数:

  • requestPrice() —— 仅需要你想要查询的数据的job ID。此函数启动之前解释的事件链
  • currentPrice()是一个查看函数,返回存储在合约中的最新价格

目前,预言机节点已经有如下报价对的不同价格数据的一组Job ID:

基础报价对 Job ID参考
AAVE to USD 541b8f7db7374d78b38285ef1b8bfacc
ALGO to USD cdb48696e2314133a1dc8ea27922dd24
BAND to USD 6b0983e0cb6d4aca908b615302a9d672
BTC to USD 82ceee2897824a0e8b014ed4ed2ab31e
DOT to USD 6f6371a780324b90aaf195a0d39c723c
ETH to USD 60160cdd0e10489681967e9d7ef4c927
KSM to USD 30a1686f657249f4b6ab01e384b2beaa
LINK to USD aad8dbdb0c1840ab905728d85117b681
SUSHI to USD b4b07d0fc218455caaff2223a05ec208
UNI to USD 22b567acabdb419abe8136a2bab6ade8

在此例中,您可以继续使用在Remix中使用带有BTC to USD的job ID的接口合约。在创建文档和编译合约之后,您可以执行以下步骤:

  1. 进入Deploy and Run Transactions标签

  2. 确保您已经设置EnvironmentInjected Web3,并且您的MetaMask已经连接至Moonbase Alpha

  3. 输入客户端合约地址,0x8ea35EdC1709ea0Ea2C86241C7D1C84Fd0dDeB11,并点击At Address。这将创建一个您可以交互的客户端合约的实例

  4. Deployed Contracts部分,使用requestPrice()函数以查询对应job ID的数据

  5. 确认交易。您需要等待至之前解释的所有请求流程发生

  6. 然后您可以使用查看函数currentPrice()查看价格

Chainlink Basic Request on Moonbase Alpha

如果您想要添加其他特定的报价对,请直接通过Discord联系Moonbeam团队。

创建自定义客户端合约

如果您想要使用Moonbeam运行的预言机节点来运行自己的自定义客户端合约,请看以下信息:

合约种类 地址
预言机合约 0x1d693d883BeAeE16edD0D7588D6a9f7E1967E798
LINK Token 0xa36085F69e2889c224210F603D836748e7dC0088

如果您决定用这个方式,请注意预言机节点仅支持上一段落中列出的job ID。您只能够访问已支持报价对的价格数据。如果您想要更多功能,或者想要使用其他API,请参考使用您自己的预言机节点以创建自定义合约段落。

使用ChainlinkClient构建您自己的客户端合约前,首先您需要导入合约:

import "https://github.com/smartcontractkit/chainlink/evm-contracts/src/v0.8/ChainlinkClient.sol";

您可以查阅Chainlink documentation on ChainlinkClient API Reference以获得更多资讯。

请注意LINK Token支付设置为0。

使用您自己的预言机节点以创建自定义合约

开始您自己的设置,包括您自己的客户端合约、预言机合约和预言机节点前,需要开始运行预言机节点。您可以根据在Moonbeam上运行Chainlink预言机节点教程启动您自己的预言机节点。您也可以了解如何设置您预言机合约以及创建Job。

如果您创建一个可与任何API一起使用的Job,接着您可以创建一个客户端合约,设置API终端URL以执行GET请求。

请注意,客户端合约必须有用LINK Token余额,才可以支付请求。因此,您需要在ChainlinkClient.sol合约中设置LINK值为0。另外需要确保您预言机节点有一个0MINIMUM_CONTRACT_PAYMENT。您可以通过查看您节点的配置部分验证是否设置为0。

下方客户端合约是关于如何使用在您客户端合约中的任何API的举例:

pragma solidity ^0.8.7;

import "@chainlink/contracts/src/v0.8/ChainlinkClient.sol";

contract Client is ChainlinkClient {
    using Chainlink for Chainlink.Request;

    address private oracle;
    bytes32 private jobId;
    uint256 private fee;
    uint256 public volume;

    /**
    This example uses the LINK token address on Moonbase Alpha.
    Make sure to update the oracle and jobId.
    */
    constructor() {
        setChainlinkToken(address(0xa36085F69e2889c224210F603D836748e7dC0088));
        oracle = {INSERT-YOUR-ORACLE-NODE-ADDRESS};
        jobId = "{INSERT-YOUR-JOB-ID}";
        fee = 0;
    }

    /**
     * Create a Chainlink request to retrieve API response, find the target
     * data, then multiply by 1000000000000000000 (to remove decimal places from data).
     */
    function requestVolumeData() public returns (bytes32 requestId) {
        Chainlink.Request memory request = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);

        // Set the URL to perform the GET request on
        request.add("get", "https://min-api.cryptocompare.com/data/pricemultifull?fsyms=ETH&tsyms=USD");

        // Set the path to find the desired data in the API response, where the response format is:
        // {"RAW":
        //   {"ETH":
        //    {"USD":
        //     {
        //      "VOLUME24HOUR": xxx.xxx,
        //     }
        //    }
        //   }
        //  }
        request.add("path", "RAW.ETH.USD.VOLUME24HOUR");

        // Multiply the result by 1000000000000000000 to remove decimals
        int timesAmount = 10**18;
        request.addInt("times", timesAmount);

        // Sends the request
        return sendChainlinkRequestTo(oracle, request, fee);
    }

    /**
     * Receive the response in the form of uint256
     */ 
    function fulfill(bytes32 _requestId, uint256 _volume) public recordChainlinkFulfillment(_requestId)
    {
        volume = _volume;
    }
}

注意事项

上述举例使用预部署的LINK Token合约地址。您也可以部署自己的LINK Token合约并使用。

一旦您已经在Remix上部署合约,您可以开始请求容量数据。在发出请求之后,您可以通过您节点的Job查看Job的状态。

喂价

在介绍获取数据本身之前,您需要先了解喂价的基本情况。

在标准配置下,每次喂价是由去中心化预言机网络进行数据更新。每个预言机节点向Aggregator合约发布价格数据,而后获得奖励。Aggregator合约从预言机网络定期接收最新数据更新,并将数据聚合并存储在链上,便于使用者轻松获取。但在每一轮聚合中,只有预言机节点收到超过最低数量门槛的响应才会更新数据。

终端用户可以通过Aggregator接口或通过代理合约的Consumer接口使用只读操作检索喂价。

Price Feed Diagram

获取价格数据

Moonbeam网咯均有Data Feed合约,以简化请求喂价的流程。在Moonbase Alpha的现有配置下,Moonbeam团队只运营一个预言机节点,通过单一API来源获取数据。价格数据每分钟查看一次,每小时通过智能合约更新一次,另外在价格变动超过1%时也会进行更新。Moonbeam和Moonriver的Data Feed合约由多个Chainlink节点定期更新。

数据储存在一系列智能合约中(每个喂价储存在一个智能合约中),可以通过Aggregator接口获取:

pragma solidity ^0.8.0;

interface AggregatorV3Interface {
    /**
     * Returns the decimals to offset on the getLatestPrice call
     */
    function decimals() external view returns (uint8);

    /**
     * Returns the description of the underlying price feed aggregator
     */
    function description() external view returns (string memory);

    /**
     * Returns the version number representing the type of aggregator the proxy points to
     */
    function version() external view returns (uint256);

    /**
     * Returns price data about a specific round
     */
    function getRoundData(uint80 _roundId) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);

    /**
     * Returns price data from the latest round
     */
    function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}

如上述接口所示,有5个函数可获取价格:decimaldescriptionversiongetRoundDatalatestRoundData

目前MoonbeamMoonriver和Moonbase Alpha提供以下报价对的数据喂价合约:

基础报价对 数据喂价合约
ATOM to USD 0x4F152D143c97B5e8d2293bc5B2380600f274a5dd
BNB to USD 0x0147f2Ad7F1e2Bc51F998CC128a8355d5AE8C32D
BTC to USD 0x8c4425e141979c66423A83bE2ee59135864487Eb
DOT to USD 0x1466b4bD0C4B6B8e1164991909961e0EE6a66d8c
ETH to USD 0x9ce2388a1696e22F870341C3FC1E89710C7569B5
FRAX to USD 0x05Ec3Fb5B7CB3bE9D7150FBA1Fb0749407e5Aa8a
GLMR to USD 0x4497B606be93e773bbA5eaCFCb2ac5E2214220Eb
LINK to USD 0xd61D7398B7734aBe7C4B143fE57dC666D2fe83aD
USDC to USD 0xA122591F60115D63421f66F752EF9f6e0bc73abC
基础报价对 数据喂价合约
1INCH to USD 0x1466b4bD0C4B6B8e1164991909961e0EE6a66d8c
AAVE to USD 0x37f35ef6735c594e6E803bC81577bAC759d8179C
ANKR to USD 0x94Ee35E8b9B1b4Cd3BDB720242d6d1796b43C2Ff
AVAX to USD 0x992F9B8Aa09B8e084acf4e3213d8b2da5D366D6a
AXS to USD 0x9322CeAd48BA0C76Fecc78e82499ce8a829Eab89
BNB to USD 0xD6B013A65C22C372F995864CcdAE202D0194f9bf
BTC to USD 0x1B5C6cF9Df1CBF30387C24CC7DB1787CCf65C797
BUSD to USD 0x596129F6ABCaB2E6E81D19284B78eA73C176D170
CAKE to USD 0xc44ecD8C11fd1F281A3d6044CA65e649484B228c
COMP to USD 0x29710821d57a1Fc46E2D9FdDE65Df2cF205bad2A
CRV to USD 0x03d44d68EdF41c540A90C6eB2BE27C4a75ee689f
DAI to USD 0x7ba0e3EbCe25DD3b5A0f36dd7aB34019B863b08D
DOT to USD 0x54B584eb643375C41c55ddD8Da4b90124b18d05c
ETH to USD 0xc3cF399566220dc5Ed6C8CFbf8247214Af103C72
EUR to USD 0xe6Ccbe1Cb33dF799a59E37a1382c7009dbaBE9ff
FRAX to USD 0xD080d4760318710e795B0a59f181f6C1512ffB15
FTM to USD 0x5e70fC5f38cB930F9BE8BEAEaF80CF927Af3B17E
FXS to USD 0xE5B624e1098C25C94279bA20A0CC68Fa9215e63b
KSM to USD 0x6e0513145FCE707Cd743528DB7C1cAB537DE9d1B
LINK to USD 0xdD27789b504fEd690F406A82F16B45a0901172C0
LUNA to USD 0x5F8E0c452EcA522a2208Fff7443515AaFF3cAaE6
MANA to USD 0x424807fA7B16f747CbD30963fAe25fB8Db0b97bF
MIM to USD 0xdD6296BD7515271F7E4b10C3A87A2f9863fECa97
MKR to USD 0xD8542f327FaD60b80D8C19025147E6b9d857bb99
MOVR to USD 0x3f8BFbDc1e79777511c00Ad8591cef888C2113C1
SAND to USD 0x5403385DF6eb607fc1fA6983eF5801A11eC7fD9a
SNX to USD 0x26E3F9273abC8a01228bE97a106E60FA38b98df2
SUSHI to USD 0x28A9E2747a10eE94D2d7359DEB60023D19FfdD96
THETA to USD 0xA0784167e040906b5580e3c4a53932B288f615ce
UNI to USD 0x05Ec3Fb5B7CB3bE9D7150FBA1Fb0749407e5Aa8a
USDC to USD 0x12870664a77Dd55bBdcDe32f91EB3244F511eF2e
USDT to USD 0xF80DAd54AF79257D41c30014160349896ca5370a
XRP to USD 0x3FD363679fb59596d45881bbfBe4bb864f3545A2
YFI to USD 0xE3324ea60FA272BBB4511dDBD4776feFE4674fa0
基础报价对 数据喂价合约
AAVE to USD 0x9DA76AE39AECA424801DF1b63e9E2750643F9e76
ALGO to USD 0x49E1021974902B4a279fC7D359b4890FC38F8261
AVAX to USD 0xA356990bCDed8Cc6865Be606D64E7381bfe00B72
BAND to USD 0x8997c0A8Dc9d13061FCf303711D1db4278e53a1B
BNB to USD 0x4ADfBA6138A5bdA31f980EF5D59Fc7f8440A5D92
BTC to USD 0xa39d8684B8cd74821db73deEB4836Ea46E145300
COMP to USD 0xaa18FFaF5E2f410250D318b57EAd2C68603A382c
CRV to USD 0x1a435D01CB50E6B1F8e429a32EE03BF164B43ece
CVX to USD 0x3A1Ac4DA79a3c813b2B335d1a631adEec360A59c
DAI to USD 0x5A5737F6C0683be63863131bfCAd793a9F828DDa
DOT to USD 0xA8B2138c8E765288D4f6fb7D3ebCe2507A006a9C
ETH to USD 0x0BAA6E884cfD628b33867F9E081B44a76276fA2D
FRAX to USD 0xB616464cB6135AF542de908FAa3FbE3a0b3048e4
FTM to USD 0xD23f056ceD090Bc9Cf6c75d2FB43c0ccC35AD542
KSM to USD 0x4E9A1ebc0bEebe3516a8A9a911D781c37414bb39
LINK to USD 0x5310f2d4B531BCEA8126e2aEE40BAd71B707f530
MKR to USD 0x2257c46D4ddF160bACBD00cd67cD6Fd87C48c581
OP to USD 0xC5837B50a86fD87e1D2FbE4FEbFe7C042a9CFBE8
stETH to USD 0x49CEee3f8e9e05f26A81f1122A78D058Ef3c05a9
SUSHI to USD 0xd265FAfc809108D9cf47b6970d750419ba9355B8
UNI to USD 0x326fAFb9E9B5188d7c2e95C0D782ECB19E5EfDF9
USDC to USD 0xCb1c08B86C6EaC617b1bEd5D1E9cD7Fd308FdA4d
USDT to USD 0x141Ec95dB9298cB4Bb20CbAC422A42A047a764b1
YFI to USD 0x8E812AA9f56BBAa5B936c8F08B0a260f3eF7cF46

举例而言,您可以通过Remix使用Aggregator接口获取BTC to USD喂价。如果您需要加载合约至Remix,请参考使用Remix文档。

您需要将您的MetaMask账户连接至Remix,请确保您已安装MetaMask并已连接至相应的网络。若您还未设置MetaMask,请参考使用MetaMask与Moonbeam交互教程。

创建文件和编译合约后,您将需要执行以下步骤:

  1. 进入Deploy and Run Transactions标签

  2. Environment设置为Injected Web3

  3. 如果您的MetaMask已连接,它将出现在Account选择列表中。否则,MetaMask将提示您连接至您的账户。

  4. Contract下拉列表中选择AggregatorV3Interface合约

  5. At Address字段输入BTC to USD对应的Data Feed地址,点击At Address按钮

    0x8c4425e141979c66423A83bE2ee59135864487Eb
    
    0x1B5C6cF9Df1CBF30387C24CC7DB1787CCf65C797
    
    0xa39d8684B8cd74821db73deEB4836Ea46E145300
    

Load the Chainlink Price Feed Aggregator Interface on Moonriver

这将创建一个可以进行交互的Aggregator接口实例,并出现在Remix的Deployed Contracts部分。执行以下操作步骤获取最新价格数据:

  1. 展开AggregatorV3Interface合约获取可用函数
  2. 点击latestRoundData()请求相应喂价数据,在本示例中为BTC to USD

Interact with the Chainlink Price Feed Aggregator Interface on Moonriver

请注意,您必须用decimals()了解喂价的小数位数,才能获得实际价格。

如果您希望更多报价对出现在上述表格,请随时通过Discord server联系我们。

本网站的所有信息由第三方提供,仅供参考之用。Moonbeam Foundation不保证网站信息的准确性、完整性或真实性。如使用或依赖本网站信息,需自行承担相关风险,Moonbeam Foundation不承担任何责任和义务。这些材料的所有陈述和/或意见由提供方个人或实体负责,与Moonbeam Foundation立场无关,概不构成任何投资建议。对于任何特定事项或情况,应寻求专业权威人士的建议。此处的信息可能会包含或链接至第三方提供的信息与/或第三方服务(包括任何第三方网站等)。这类链接网站不受Moonbeam Foundation控制。Moonbeam Foundation对此类链接网站的内容(包括此类链接网站上包含的任何信息或资料)概不负责也不认可。这些链接内容仅为方便访客而提供,Moonbeam Foundation对因您使用此信息或任何第三方网站或服务提供的信息而产生的所有责任概不负责。