Chainlink预言机¶
概览¶
开发人员现在可以使用Chainlink的去中心化预言机网络 从基于Moonbeam的网络中获取数据。它有两种主要的架构:喂价(Price Feeds) 和基本请求模型。喂价包含由预言机运营商在智能合约中不断更新的实时价格数据,以便其他智能合约可以获取和使用它。基本请求模型描述了一种链上架构,用于从单个预言机源请求数据。 本指南将介绍如何使用这两种架构获取最新的价格数据。
此处提供的信息仅供参考,由第三方提供。 Moonbeam文档网站(https://docs.moonbeam.network/)上列出和描述的任何项目与Moonbeam立场无关。
喂价¶
在介绍获取数据本身之前,您需要先了解喂价的基本情况。
在标准配置下,每次喂价是由去中心化预言机网络进行数据更新。每个预言机节点向Aggregator合约发布价格数据,而后获得奖励。Aggregator合约从预言机网络定期接收最新数据更新,并将数据聚合并存储在链上,便于使用者轻松获取。但在每一轮聚合中,只有预言机节点收到超过最低数量门槛的响应才会更新数据。
终端用户可以通过Aggregator接口或通过代理合约的Consumer接口使用只读操作检索喂价。
获取价格数据¶
Moonbeam网咯均有Data Feed合约,以简化请求喂价的流程。在Moonbase Alpha的现有配置下,Moonbeam团队只运营一个预言机节点,通过单一API来源获取数据。智能合约每隔12小时检查和更新一次价格数据。因此,Moonbeam Alpha的喂价数据仅用于测试目的,不具有权威性。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个函数可获取价格:decimal
、description
、version
、getRoundData
和latestRoundData
。
目前Moonbeam、Moonriver和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 |
GLMR to USD | 0x537879A0beA294c1ce04161Ae827919e92C23e92 |
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交互教程。
创建文件和编译合约后,您将需要执行以下步骤:
-
进入Deploy and Run Transactions标签
-
将ENVIRONMENT设置为Injected Web3
-
如果您的MetaMask已连接,它将出现在ACCOUNT选择列表中。否则,MetaMask将提示您连接至您的账户。
-
在CONTRACT下拉列表中选择
AggregatorV3Interface
合约 -
在At Address字段输入
BTC to USD
对应的Data Feed地址,点击At Address按钮0x8c4425e141979c66423A83bE2ee59135864487Eb
0x1B5C6cF9Df1CBF30387C24CC7DB1787CCf65C797
0xa39d8684B8cd74821db73deEB4836Ea46E145300
这将创建一个可以进行交互的Aggregator接口实例,并出现在Remix的Deployed Contracts部分。执行以下操作步骤获取最新价格数据:
- 展开
AggregatorV3Interface
合约获取可用函数 - 点击
latestRoundData()
请求相应喂价数据,在本示例中为BTC to USD
请注意,您必须用decimals()
了解喂价的小数位数,才能获得实际价格。
如果您希望更多报价对出现在上述表格,请随时通过Discord server联系我们。
基本请求模型¶
在开始获取数据本身之前,您必须先了解“基本请求模型”的基础信息。从Chainlink预言机请求和接收数据的一般流程如下所示:
-
客户合约创建并向Chainlink预言机发出数据请求
-
请求通过LINK Token合约的
transferAndCall
函数发出。LINK Token是一个符合ERC-677的Token,即允许转移Token并将请求传输给预言机合约 -
转移Token后,LINK合约将调用预言机合约的
onTokenTransfer
函数 -
预言机合约由预言机节点运营者管理,负责处理通过LINK Token发出的链上请求。预言机合约收到请求后,将会向节点发出一个事件,并由该节点执行操作
-
预言机节点完成请求后,节点使用预言机合约的
fulfillOracleRequest
函数,通过在原始请求中定义的回调函数将结果返回给客户合约
当通过客户合约创建数据请求后,需要传入以下参数以确保交易能够进行并返回正确信息:
- 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 | 7ddc5b9e9eef410184666620dbc740a4 |
ALGO to USD | 9ac119b772ed4d2580965fa84ef26f56 |
AVAX to USD | 007ba4a3bf424ea79d2fad13fd763808 |
BAND to USD | 6405edbca42e4b92bd4de2601310de79 |
BNB to USD | f4d1d07ce01d4039a410f0592ab9df4c |
BTC to USD | 02d8ae1716924ea7af12dba7d08aa9a2 |
COMP to USD | 35c893eca2eb4a35b6ee8b786920df2d |
CRV to USD | f8b5326162214b22874d3df2394f4da2 |
CVX to USD | a0f8e66321a64383b4ab92b98c44a500 |
DAI to USD | e191bd95b8134508bb258fc8c4375956 |
DOT to USD | 58a5aba286a143b68f9fcf9c1f022fd5 |
ETH to USD | e539daf468bb465c9c795deb5ff7fe22 |
FRAX to USD | 2a6d5b0b0e4b4d38b63dc4ea7a4aa2b9 |
FTM to USD | 6bf63cdfd9d54512b53671375d489079 |
KSM to USD | db34748e6cad4eab895eae8cbfacc80b |
LINK to USD | caea548c900d49959c91d595ef3854b6 |
MKR to USD | 2b32e18e39404182a8357c1caa9c6f74 |
OP to USD | 99c4ff9ec4194208a06c082353cef852 |
stETH to USD | 7d8b047a8e5e4a88adf4d24896a833e5 |
SUSHI to USD | 36e18c5b30b24b988c007dc9343cad5e |
UNI to USD | 49f014fd0475434cbe32e8f14d0d5d6d |
USDC to USD | 76dcaca128a24ec4aa46a3f17d084d02 |
USDT to USD | 7407e378b901429d884eebee1c1b7ee6 |
YFI to USD | f0f56b5c976649cb9d681f18efce34d1 |
在此例中,您可以继续使用在Remix中使用带有BTC to USD
的job ID的接口合约。在创建文档和编译合约之后,您可以执行以下步骤:
-
进入Deploy and Run Transactions标签
-
确保您已经设置ENVIRONMENT为Injected Web3,并且您的MetaMask已经连接至Moonbase Alpha
-
输入客户端合约地址,
0x8ea35EdC1709ea0Ea2C86241C7D1C84Fd0dDeB11
,并点击At Address。这将创建一个您可以交互的客户端合约的实例 -
在Deployed Contracts部分,使用
requestPrice()
函数以查询对应job ID的数据 -
确认交易。您需要等待至之前解释的所有请求流程发生
-
然后您可以使用查看函数
currentPrice()
查看价格
如果您想要添加其他特定的报价对,请直接通过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。另外需要确保您预言机节点有一个0
的 MINIMUM_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的状态。
| Created: March 26, 2021