Chainlink Any API 代码解析
智能合约对链下数据的兼容会大大增加开发复杂度,Chainlink 通过 AnyAPI 使开发者的智能合约可以通过去中心化预言机网络(Decentralized Oracle Network:DON)从外部获取数据。这样在使用 Chainlink AnyAPI 的时候,开发人员可以投入最少的开发资源,获得最大的灵活度,因此可以更加专注在智能合约的功能性上,而非怎么样去获取数据上。
虽然 Chainlink Data Feed 可以给链上智能合约提供由 DON 聚合以后的通证价格,但是在很多场景下,尤其是非 DeFi 应用中,dApp 除了价格以外还需要多种多样的数据来实现自己的业务逻辑。比如在保险领域,智能合约需要天气数据来计算参保方的赔付金额,在合成资产协议中,外部股票市场的数据是必不可少的,除此以外,随着 web3 的场景越来越丰富,会越来越多地依赖于链下数据,比如说链下的交通运输,房地产,身份信息等等多种多样的数据。
如果你的智能合约需要依赖于这些数据,Chainlink AnyAPI 都可以作为一个工具让你从指定的外部数据源获取到特定数据。接下来,就让我们看看 Chainlink AnyAPI 的工作原理是什么。
使用 Chainlink AnyAPI 服务
发送 Chainlink 请求
在从 Chainlink 预言机节点获得数据之前,我们首先需要创建一个用户合约,然后在用户合约中给 Chainlink 预言机节点发送一个请求。下面的代码将展示如何通过用户合约给预言机节点发送请求:
function requestVolumeData() public returns (bytes32 requestId) {
Chainlink.Request memory req = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);
req.add('get', 'https://min-api.cryptocompare.com/data/pricemultifull?fsyms=ETH&tsyms=USD');
req.add('path', 'RAW,ETH,USD,VOLUME24HOUR');
int256 timesAmount = 10**18;
req.addInt('times', timesAmount);
return sendChainlinkRequest(req, fee);
}
Chainlink AnyAPI 获取的数据的方式一般是通过预言机节点给外部数据源发送 RESTful 请求,所以节点在发送请求之前需要知道要请求的数据的 API 和参数。为了给 Any API 提供必要的数据,在智能合约的 `ChainlinkRequest` 中,我们需要通过函数 `buildChainlinkRequest` 加入这些相关信息。`buildChainlinkRequest` 这个函数定义在 `ChainlinkClient.sol`,代码展示如下:
function buildChainlinkRequest( bytes32 specId, address callbackAddr, bytes4 callbackFunctionSignature) internal pure returns (Chainlink.Request memory) { Chainlink.Request memory req; return req.initialize(specId, callbackAddr, callbackFunctionSignature);}
在 `buildChainlinkRequest` 函数中,所有与请求相关的信息都会加入到 Request 这个结构体中,并且调用函数 `initialize` 来完成初始化。
Struct Request定义在 Chainlink.sol 文件中,代码如下:
struct Request { bytes32 id; address callbackAddress; bytes4 callbackFunctionId; uint256 nonce; BufferChainlink.buffer buf;}
函数 `initialize` 也定义在 Chainlink.sol 文件中,代码如下:
function initialize( Request memory self, bytes32 jobId, address callbackAddr, bytes4 callbackFunc) internal pure returns (Chainlink.Request memory) { BufferChainlink.init(self.buf, defaultBufferSize); self.id = jobId; self.callbackAddress = callbackAddr; self.callbackFunctionId = callbackFunc; return self;}
在函数 `buildChainlinkRequest` 中,会接受 3 个参数:
-
jobId: 预言机节点需要执行的 job 的 ID。
-
callbackAddr:用户合约地址,这个参数是预言机节点将会将返回数据的合约地址。
-
callbackFunc:这个是预言机节点需要回调的函数签名。
在这三个参数设置好以后,还需要加入 URL 和数据路径,因为我们需要告诉预言机节点通过哪个 API 获取数据,并且在获取数据以后,如何在返回的数据中找到我们所需要的有效数据。
req.add('get', 'https://min-api.cryptocompare.com/data/pricemultifull?fsyms=ETH&tsyms=USD');
req.add('path', 'RAW,ETH,USD,VOLUME24HOUR');
这里的 `path` 参数很重要,因为预言机通常会通过用户提供的 URL 获得庞大而杂乱的数据,下面的 JSON 数据就是一个例子。
{"RAW": {"ETH": {"USD": {"TYPE":"5", "MARKET":"CCCAGG", "FROMSYMBOL":"ETH", "TOSYMBOL":"USD", "FLAGS":"2049", "PRICE":1083.43, "LASTUPDATE":1655472805, "MEDIAN":1083.49, "LASTVOLUME":0.01152796, "LASTVOLUMETO":12.488815466, "LASTTRADEID":"298687546", "VOLUMEDAY":279647.314121829, "VOLUMEDAYTO":304661021.9293905, "VOLUME24HOUR":617393.32461219, ....... ....... "TOTALTOPTIERVOLUME24HTO":"$ 5.47B", "IMAGEURL":"/media/37746238/eth.png"} } } }
通过在 request 中的 path 数据,预言机节点才可以获取到我们所想要的数据。所以在 URL 和路径被设置好以后,这个 `chainlinkRequest` 就完成并且可以被发送了。
return sendChainlinkRequest(req, fee);
这里的调用顺序是:
-
终端用户会在用户合约中调用函数 `requestVolumeData`,然后用户合约会调用 ChainlinkClient.sol 中的函数 `sendChainlinkRequest`。
-
然后 `sendChainlinkRequest` 会调用函数 `sendChainlinkRequestTo`,这个函数会接受的参数是预言机的地址,函数签名和其他相关信息,然后 encode 所有的信息,转化为 bytes 数据。
-
接下来,`_rawRequest` 会调用 Link 通证合约的中的 `transferAndCall` 函数,`transferAndCall` 是 ERC-677 标准中所定义的函数。`transferAndCall`会把要执行的代码(上一步中的 encode 数据)发送给预言机合约,然后要求该合约执行 代码逻辑。
-
最后,预言机合约 `OperatorInterface.sol`中的函数 `operatorRequest` 会被上一步中的 `transferAndCall`调用,然后该函数会将函数签名,requestId 等信息写到 event 中,以便链下预言机发现。
让我们看一个具体的例子,Chainlink 官方在测试网 Kovan 中所部署了一个预言机合约。这个合约的代码可以在这里看到:
https://kovan.etherscan.io/address/0x74EcC8Bdeb76F2C6760eD2dc8A46ca5e581fA656#code
function oracleRequest( address sender, uint256 payment, bytes32 specId, address callbackAddress, bytes4 callbackFunctionId, uint256 nonce, uint256 dataVersion, bytes calldata data ) external override validateFromLINK { (bytes32 requestId, uint256 expiration) = _verifyAndProcessOracleRequest( sender, payment, callbackAddress, callbackFunctionId, nonce, dataVersion ); emit OracleRequest(specId, sender, requestId, payment, sender, callbackFunctionId, expiration, dataVersion, data); }
在代码中,可以很容易看到,这个函数就是把所有的信息写到了 event log 中,然后等待链下的预言机节点检测。
返回请求数据
返回数据的函数 `fulfillOracleRequest2` 也被定义在 `OperatorInterface.sol` 文件中。
function fulfillOracleRequest2(bytes32 requestId,uint256 payment,address callbackAddress,bytes4 callbackFunctionId,uint256 expiration,bytes calldata data) external returns (bool);
让我们看看刚才的合约中,这个函数中的逻辑是什么样的:
function fulfillOracleRequest2(
bytes32 requestId,
uint256 payment,
address callbackAddress,
bytes4 callbackFunctionId,
uint256 expiration,
bytes calldata data
)
external
override
validateAuthorizedSender
validateRequestId(requestId)
validateCallbackAddress(callbackAddress)
validateMultiWordResponseId(requestId, data)
returns (bool)
{
_verifyOracleRequestAndProcessPayment(requestId, payment, callbackAddress, callbackFunctionId, expiration, 2);
emit OracleResponse(requestId);
require(gasleft() >= MINIMUM_CONSUMER_GAS_LIMIT, "Must provide consumer enough gas");
(bool success, ) = callbackAddress.call(abi.encodePacked(callbackFunctionId, data));
return success;
}
通过上面的代码,我们看到这个函数调用了 `callbackAddress` 地址合约中的一个函数,这个地址就是之前传给预言机合约的用户合约地址。用户合约中 `fullfill` 函数被调用了:
function fulfill(bytes32 _requestId, uint256 _volume) public recordChainlinkFulfillment(_requestId) { emit RequestVolume(_requestId, _volume); volume = _volume; }
这个展示合约中的 fulfill 函数非常简单,就是将 requestId 和 volume 的数据写入 event log,然后将 volume 写入到本地变量 _volume 中。
运行 Chainlink 节点提供 Any API 服务
在上一章节,我们了解了如何在用户合约中使用 Chainlink AnyAPI服务,以获取到多种多样的数据。接下来,我们来看看如何去运行 Chainlink AnyAPI 的“后端”,来看看如何运行一个预言机节点来帮助链上合约获取它们所需要的各种数据。
新建 Chainlink 节点
新建节点有很多种方式,如果你是一个运维人员并且自身有足够的硬件资源,可以根据官方文档中的教程新建一个节点。如果你不想要自己运维节点,那么可以选择使用 naas.link(node as a service),只需要点几个按钮就可以新建一个节点,并且是免费的。另外,Chainlink 官方开发者关系团队也在不同的链上维护了一些 Chainlink 节点,可以在这里查看关于这些节点的 JobId,合约地址和其他相关信息:
https://docs.chain.link/docs/decentralized-oracles-ethereum-mainnet/
如果你需要一些特殊的数据,比如说天气数据,股票数据,体育比赛数据等等,可以在 Chainlink 提供的数据市场 market.link 中搜索。
如果你对于Chainlink节点所提供的数据有更个性化的要求,可以登陆 Chainlink 的 Discord,Chainlink 团队会帮你在社区中联系节点运营商,以满足你的需求。华语开发者也可以直接联系 Chainlink 中国团队,获得更快的响应。
在上一章节,我们了解了如何去写一个用户合约来使用 Chainlink AnyAPI ,从而获取到多种多样的数据。接下来,我们可以学习一下如何去运行 AnyAPI 的“后端”,来看看如何运行一个预言机节点来帮助链上合约获取它们所需要的各种数据。
预言机节点的 job 和 task pipeline (TOML)
TOML(Tom's Obviously Minimal Language)是一种配置文件的格式,因为 sematics 比较清晰,所以更容易阅读,被很多项目所使用。Chainlink 节点就是使用 TOML 来定义节点所提供的 API 服务所对应的 job 的详细信息。
在 Chainlink 节点中,每一个 jobId 都会代表一个在节点中运行的 job。比如说在 API 样例代码中,jobId 代表的 job 是获取 BTC 昨天的市场数据的。Chainlink 节点使用 TOML 来定义怎么样从 API 中获取数据,并且将这个数据进行标准化,使其可以被用户合约使用。
Chainlink 节点做的任何操作都会依赖于 job,现在支持以下 6 种 job:
-
Cron:根据一个时间表而非外部触发来执行一个 job。
-
Direct request job:根据一个用户所发出的请求的 receipt 来执行一个 job。预言机合约会在被 emit 的 log 中发现用户的请求。这个方式和以前 ethlog/runlog 执行 job 的方式类似。
-
Flux monitor job:根据不同的预言机节点所返回的数据来更新 data feed 中的数据。更新会在波动率足够大,或者是 heartbeat 超出时间限制的时候触发。
-
Keeper job:根据链上合约中的状态进行判断,判断成功以后会执行智能合约中的函数,可以非定期地调用合约中的函数。
-
Off-chain reporting job:Off-chain reporting(OCR)和 Flux monitor job 非常类似,OCR job 会根据多个 Chainlink 预言机节点聚合以后的数据更新 data feed。OCR 和 Flux monitor job 的区别是 OCR 使用了由密码学保证的链下协议,可以让单个节点在一个 round 中将所有其他节点中的数据提交上来。通过这个方式,可以节省大量的gas。
-
Webhook job: Webhook 可以由 HTTP 请求所触发,HTTP 请求可以由用户或者其他外部触发器所触发。
在 job 中,需要定义以下变量:
-
name: 在 Chainlink 节点 UI 中所现实的 job 的名字。
-
type: job 的类型,可以是上述 job 类型中的任何一个。
-
schemaVersion: 现在都需要设置为 1,这个设置是为了让 job 的格式向前兼容。
-
observationSource: 这个参数定义 job 具体要做的操作。
-
maxTaskDuration: 任何一个任务能够运行的最长事件默认值。如果一个任务达到了最长时间,这个任务就会报错。
-
externalJobID: 提供了一种可选方法,用户可以通过这个参数直接定义 job 的。
除了上述参数以外,你还需要定义 job 中的任务(task),让我们看看一个 job 的 TOML 文件的例子:
type = "directrequest"
schemaVersion = 1
evmChainID = 1
name = "example eth request event spec"
contractAddress = "0x613a38AC1659769640aaE063C651F48E0250454C"
observationSource = """
ds [type="http" method=GET url="http://example.com"]
ds_parse [type="jsonparse" path="USD"]
ds_multiply [type="multiply" times=100]
ds -> ds_parse -> ds_multiply
`ds`, `ds_parse`, `ds_multiply` 是 job 要执行的 3 个任务,执行顺序通过 `ds -> ds_parse -> ds_multiply` 这一行定义,语法非常简单,即先给 “http://exmpale.com” 发送 GET 请求,然后使用路径 “USD” 来找到用户在这个JSON 文件中需要的值。这个 JSON 文件如下:
{ usd: number}
最后,这个 job 会根据 `ds_multiply` 这个任务将结果乘以 100。
总结
除了可以通过 Chainlink data feed 获取通证价格以外,开发还可以通过 Chainlink AnyAPI 获得任何自己想要的数据。文章讲解了如何新建自己的 Chainlink 节点,并且在合约中使用 Chainlink 节点 AnyAPI 服务获得个性化数据。
您可以关注 Chainlink 预言机并且私信加入开发者社区,有大量关于智能合约的学习资料以及关于区块链的话题!
比推快讯
更多 >>- 1亿枚 USDT 从 Tether Treasury 转移到 Bitfinex
- Optimism已修复其测试网的两个关键漏洞
- Arkham:与 Wisdomtree WBIT相关的钱包持有8900枚BTC,价值约 5.79 亿美元
- 纳指止步4周连跌,创去年11月以来最大单周涨幅
- DAI 单日链上转账金额达到 860 亿美元,创下历史新高
- 以太坊 Rollup 协议 Zircuit 拟于夏季启动主网,总锁仓价值已达25亿美元
- 美参议员警告用于交易儿童性虐待材料的加密活动有所增加
- 加密银行 Custodia Bank 就其美联储成员资格败诉一案提出上诉
- Yuga Labs裁员,专注于建立“更小、更灵活的加密原生团队”
- NFTfi基金会:NFTFI空投申领门户将从 4 月 30 日开放至 6 月 10 日
- dYdX Chain累计交易量突破1200亿美元
- Phoenix Wallet将从美国应用商店下架,Jack Dorsey称“感觉完全没有必要”
- 零知识 (ZK) 协议 Hinkal 完成140 万美元战略融资,估值 7000 万美元
- 姆爷Eminem取代马特·达蒙成为 Crypto.com 最新明星代言人
- 九只现货比特币ETF今日净减持2,987枚BTC,净流出约1.9亿美元
- Yuga Labs 聘请媒体资深人士 Won Kim 担任品牌合作主管
- 一巨鲸从3月1日至4月15日买入近2.5万枚BTC,价值达 16.8 亿美元
- Sam Altman、黄仁勋等科技巨头高管加入美国联邦人工智能安全委员会
- PENDLE 持仓第二巨鲸疑似在持有近一年后卖出 120 万枚 PENDLE ,预计获利 645 万美元
- 福克斯记者:马萨诸塞州参议员候选人John Deaton将提交法庭之友陈述,以支持Coinbase的上诉动议
- Azuki将Azuki #723捐赠给Arbitrum DAO财库
- 链上借贷协议Venus Protocol已于以太坊主网部署DAI市场
- 花旗现预计美联储将于7月降息,2024年共降息100个基点
- 零信任DePIN网络Staex完成战略轮融资,Moonrock Capital参投
- 灰度向Coinbase Prime等地址转入约1811.1枚BTC
- 比特币波动指数昨日降至60.53,日跌幅8.07%
- 美国利率期货合约交易员预计美联储九月首次降息的可能性增加
- 稳定币公司OpenDelta完成250万美元Pre-Seed轮融资
- 美国3月核心PCE物价指数年率2.8%,预期2.7%
- Web3游戏L3E7完成1000万美元融资
- 芝加哥期权交易所拟将数字资产衍生品业务整合到其全球衍生品和结算业务中
- 某鲸鱼地址将3527枚ezETH PT和3300枚ETH存入Zircuit
- Quantix Capital投资NFA Labs 1000万美美元
- Uniswap基金会向Auditless拨款120万美元以创建Protocol Grants Program
- Arthur Hayes:美财长耶伦或将推出天量流动性注入计划,加速加密牛市回归
- RWA协议The Conlony完成新一轮融资,TechStars等参投
- 名义价值93亿美元的BTC与ETH期权即将到期
- Amaranth基金会创始人花费2470万美元购买7814枚ETH
- Vitalik:PoW也相当中心化,PoW只是转向PoS之前的临时阶段
- 中国证监会科技监管司司长、信息中心主任姚前接受审查调查
- 比特币长期持有者已从1月份开始抛售仓位
- PenPad宣布获得加密投资机构Animoca Brands投资
- 富达现货比特币ETF首次录得资金流出
- 华盛证券成为首批虚拟资产现货ETF承销商
比推专栏
更多 >>观点
项目
比推热门文章
- 【比推每日新闻精选】与 Wisdomtree WBIT相关的钱包持有8900枚BTC,价值约 5.79 亿美元;DAI 单日链上转账金额达到 860 亿美元,创下历史新高;芝加哥期权交易所拟将数字资产业务整合到其全球衍生品和结算业务中
- 1亿枚 USDT 从 Tether Treasury 转移到 Bitfinex
- Optimism已修复其测试网的两个关键漏洞
- Arkham:与 Wisdomtree WBIT相关的钱包持有8900枚BTC,价值约 5.79 亿美元
- 【比推每日市场动态】再测6.4万美元,BTC无聊走势或将持续3到6个月
- 纳指止步4周连跌,创去年11月以来最大单周涨幅
- DAI 单日链上转账金额达到 860 亿美元,创下历史新高
- 以太坊 Rollup 协议 Zircuit 拟于夏季启动主网,总锁仓价值已达25亿美元
- 美参议员警告用于交易儿童性虐待材料的加密活动有所增加
- 加密银行 Custodia Bank 就其美联储成员资格败诉一案提出上诉