Chainlink 和 API3 在技术方案上并没有本质上的区别,但在实现的细节上有区别
Chainlink 已经集成好了一些第三方数据,供链上合约调用,主要是价格方面的数据。使用方式也很简单,在自己的 contract 里调用 chianlink 的 Aggregator
contract 即可。
// SPDX-License-Identifier: MIT
pragma solidity ^ 0.8 .7;
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
contract PriceConsumerV3 {
AggregatorV3Interface internal priceFeed;
/**
* Network: Rinkeby
* Aggregator: ETH/USD
* Address: 0x8A753747A1Fa494EC906cE90E9f37563A8AF630e
*/
constructor() {
priceFeed = AggregatorV3Interface(0x8A753747A1Fa494EC906cE90E9f37563A8AF630e);
}
/**
* Returns the latest price
*/
function getLatestPrice() public view returns(int) {
(
/*uint80 roundID*/
,
int price,
/*uint startedAt*/
,
/*uint timeStamp*/
,
/*uint80 answeredInRound*/
) = priceFeed.latestRoundData();
return price;
}
}
那 data feed 的数据从哪里来的呢?链下的去中心化 oracle 节点会用 OCR(off-chain reporting) 技术来 aggregate 数据
chainlink 用的 ocr 比较简单。选一个 leader 节点去接收其它节点上报的价格数据,组装数据, 发给其它节点去验证,没问题的话,leader 节点就组装最终的 report 发给其他节点。随机选一个节点,将 report 上报给 aggregator. aggregator
校验数据,没问题就取所有数据的 median
value
//SPDX-License-Identifier: MIT
pragma solidity ^ 0.8 .7;
import '@chainlink/contracts/src/v0.8/ChainlinkClient.sol';
import '@chainlink/contracts/src/v0.8/ConfirmedOwner.sol';
/**
* Request testnet LINK and ETH here: https://faucets.chain.link/
* Find information on LINK Token Contracts and get the latest ETH and LINK faucets here: https://docs.chain.link/docs/link-token-contracts/
*/
/**
* THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY.
* THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE.
* DO NOT USE THIS CODE IN PRODUCTION.
*/
contract MultiWordConsumer is ChainlinkClient, ConfirmedOwner {
using Chainlink
for Chainlink.Request;
bytes32 private jobId;
uint256 private fee;
// multiple params returned in a single oracle response
uint256 public btc;
uint256 public usd;
uint256 public eur;
event RequestMultipleFulfilled(bytes32 indexed requestId, uint256 btc, uint256 usd, uint256 eur);
/**
* @notice Initialize the link token and target oracle
* @dev The oracle address must be an Operator contract for multiword response
*
*
* Rinkeby Testnet details:
* Link Token: 0x01BE23585060835E02B77ef475b0Cc51aA1e0709
* Oracle: 0xf3FBB7f3391F62C8fe53f89B41dFC8159EE9653f (Chainlink DevRel)
* jobId: 53f9755920cd451a8fe46f5087468395
*
*/
constructor() ConfirmedOwner(msg.sender) {
setChainlinkToken(0x01BE23585060835E02B77ef475b0Cc51aA1e0709);
setChainlinkOracle(0xf3FBB7f3391F62C8fe53f89B41dFC8159EE9653f);
jobId = '53f9755920cd451a8fe46f5087468395';
fee = (1 * LINK_DIVISIBILITY) / 10; // 0,1 * 10**18 (Varies by network and job)
}
/**
* @notice Request mutiple parameters from the oracle in a single transaction
*/
function requestMultipleParameters() public {
Chainlink.Request memory req = buildChainlinkRequest(
jobId,
address(this),
this.fulfillMultipleParameters.selector
);
req.add('urlBTC', 'https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=BTC');
req.add('pathBTC', 'BTC');
req.add('urlUSD', 'https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD');
req.add('pathUSD', 'USD');
req.add('urlEUR', 'https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=EUR');
req.add('pathEUR', 'EUR');
sendChainlinkRequest(req, fee); // MWR API.
}
/**
* @notice Fulfillment function for multiple parameters in a single request
* @dev This is called by the oracle. recordChainlinkFulfillment must be used.
*/
function fulfillMultipleParameters(
bytes32 requestId,
uint256 btcResponse,
uint256 usdResponse,
uint256 eurResponse
) public recordChainlinkFulfillment(requestId) {
emit RequestMultipleFulfilled(requestId, btcResponse, usdResponse, eurResponse);
btc = btcResponse;
usd = usdResponse;
eur = eurResponse;
}
/**
* Allow withdraw of Link tokens from the contract
*/
function withdrawLink() public onlyOwner {
LinkTokenInterface link = LinkTokenInterface(chainlinkTokenAddress());
require(link.transfer(msg.sender, link.balanceOf(address(this))), 'Unable to transfer');
}
}
和 API3 的明显区别是,API3 将请求的内容放进了 airnode,而 chainlink 是放进合约里的,相比之下,chainlink 的做法要更为灵活。
existing job 是一些预定好请求内容的 job (注意代码中的 jobId), 可以直接用,contract 负责接收数据即可。
// SPDX-License-Identifier: MIT
pragma solidity ^ 0.8 .7;
import '@chainlink/contracts/src/v0.8/ChainlinkClient.sol';
import '@chainlink/contracts/src/v0.8/ConfirmedOwner.sol';
/**
* Request testnet LINK and ETH here: https://faucets.chain.link/
* Find information on LINK Token Contracts and get the latest ETH and LINK faucets here: https://docs.chain.link/docs/link-token-contracts/
*/
/**
* THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY.
* THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE.
* DO NOT USE THIS CODE IN PRODUCTION.
*/
contract GetGasPrice is ChainlinkClient, ConfirmedOwner {
using Chainlink
for Chainlink.Request;
uint256 public gasPriceFast;
uint256 public gasPriceAverage;
uint256 public gasPriceSafe;
bytes32 private jobId;
uint256 private fee;
event RequestGasPrice(
bytes32 indexed requestId,
uint256 gasPriceFast,
uint256 gasPriceAverage,
uint256 gasPriceSafe
);
/**
* @notice Initialize the link token and target oracle
*
* Rinkeby Testnet details:
* Link Token: 0x01BE23585060835E02B77ef475b0Cc51aA1e0709
* Oracle: 0xf3FBB7f3391F62C8fe53f89B41dFC8159EE9653f (Chainlink DevRel)
* jobId: 7223acbd01654282865b678924126013
*
*/
constructor() ConfirmedOwner(msg.sender) {
setChainlinkToken(0x01BE23585060835E02B77ef475b0Cc51aA1e0709);
setChainlinkOracle(0xf3FBB7f3391F62C8fe53f89B41dFC8159EE9653f);
jobId = '7223acbd01654282865b678924126013';
fee = (1 * LINK_DIVISIBILITY) / 10; // 0,1 * 10**18 (Varies by network and job)
}
/**
* Create a Chainlink request the gas price from Etherscan
*/
function requestGasPrice() public returns(bytes32 requestId) {
Chainlink.Request memory req = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);
// No need extra parameters for this job. Send the request
return sendChainlinkRequest(req, fee);
}
/**
* Receive the responses in the form of uint256
*/
function fulfill(
bytes32 _requestId,
uint256 _gasPriceFast,
uint256 _gasPriceAverage,
uint256 _gasPriceSafe
) public recordChainlinkFulfillment(_requestId) {
emit RequestGasPrice(_requestId, _gasPriceFast, _gasPriceAverage, _gasPriceSafe);
gasPriceFast = _gasPriceFast;
gasPriceAverage = _gasPriceAverage;
gasPriceSafe = _gasPriceSafe;
}
/**
* Allow withdraw of Link tokens from the contract
*/
function withdrawLink() public onlyOwner {
LinkTokenInterface link = LinkTokenInterface(chainlinkTokenAddress());
require(link.transfer(msg.sender, link.balanceOf(address(this))), 'Unable to transfer');
}
}