前言
在上一篇博客的基础上,本文将详细介绍Chainlink获取链下数据的方法。
本文需要在Remix上部署三个智能合约,分别是LinkToken.sol、Operator.sol和ATestnetConsumer.sol。而LinkToken合约的部署流程已在上文详细给出,这里我写一下跳转链接:【Chainlink】基于私链运行一个Chainlink预言机节点 | Keike1 (keikei99.github.io) 。
区块链上的用户作为数据获取方,需要从链下的数据提供方获取数据,而区块链本身不具备获取现实世界数据的能力,因此需要使用Chainlink预言机作为连接链上和链下之间的桥梁。下面我们将模拟用户获取数据的这个过程。
部署Operator.sol合约
Operator.sol
1 2 3 pragma solidity ^0.7 .6 ; import "@chainlink/contracts/src/v0.7/Operator.sol" ;
部署完要记得把合约地址记录下来
1 Operator: 0xD82f08722b8e24DA3ab608AB11E230dB0E0a5829
随后调用setAuthorizedSenders方法,填自己的账户地址,注意要以数组形式传递。如
1 ["0x0688AbDa371A869DcBb56d2E946A6898660ECDef"]
可以用isAuthorizedSender
来判断是否设置成功
在Chainlink Operator中创建新的Job
写入以下内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 name = "test" schemaVersion = 1 type = "directrequest" contractAddress = "YOUR_OPERATOR_CONTRACT_ADDRESS" maxTaskDuration = "0s" minIncomingConfirmations = 0 observationSource = """ decode_log [type="ethabidecodelog" abi="OracleRequest(bytes32 indexed specId, address requester, bytes32 requestId, uint256 payment, address callbackAddr, bytes4 callbackFunctionId, uint256 cancelExpiration, uint256 dataVersion, bytes data)" data="$(jobRun.logData)" topics="$(jobRun.logTopics)"] decode_cbor [type="cborparse" data="$(decode_log.data)"] fetch [type="http" method=GET url="$(decode_cbor.get)" allowUnrestrictedNetworkAccess="true"] parse [type="jsonparse" path="$(decode_cbor.path)" data="$(fetch)"] multiply [type="multiply" input="$(parse)" times="$(decode_cbor.times)"] encode_data [type="ethabiencode" abi="(bytes32 requestId, uint256 value)" data="{ \\"requestId\\": $(decode_log.requestId), \\"value\\": $(multiply) }"] encode_tx [type="ethabiencode" abi="fulfillOracleRequest2(bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes calldata data)" data="{\\"requestId\\": $(decode_log.requestId), \\"payment\\": $(decode_log.payment), \\"callbackAddress\\": $(decode_log.callbackAddr), \\"callbackFunctionId\\": $(decode_log.callbackFunctionId), \\"expiration\\": $(decode_log.cancelExpiration), \\"data\\": $(encode_data)}" ] submit_tx [type="ethtx" to="YOUR_OPERATOR_CONTRACT_ADDRESS" data="$(encode_tx)"] decode_log -> decode_cbor -> fetch -> parse -> multiply -> encode_data -> encode_tx -> submit_tx """
注意将以上的两个 YOUR_OPERATOR_CONTRACT_ADDRESS
改为你的Operator合约地址。
随后复制系统给出的externalJobId
备用。
1 JobId: e22cd821-ecb1-405f-9525-d273acc8d66e
部署ATestnetConsumer合约
ATestnetConsumer.sol
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 pragma solidity ^0.8 .7 ; import "@chainlink/contracts/src/v0.8/ChainlinkClient.sol" ;import "@chainlink/contracts/src/v0.8/ConfirmedOwner.sol" ;contract ATestnetConsumer is ChainlinkClient , ConfirmedOwner { using Chainlink for Chainlink .Request ; uint256 private constant ORACLE_PAYMENT = (1 * LINK_DIVISIBILITY ) / 10 ; uint256 public currentPrice; event RequestEthereumPriceFulfilled ( bytes32 indexed requestId, uint256 indexed price ); constructor ( ) ConfirmedOwner (msg.sender ) { setChainlinkToken (0x779877A7B0D9E8603169DdbD7836e478b4624789 ); } function requestEthereumPrice ( address _oracle, string memory _jobId ) public onlyOwner { Chainlink .Request memory req = buildChainlinkRequest ( stringToBytes32 (_jobId), address (this ), this .fulfillEthereumPrice .selector ); req.add ( "get" , "https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD" ); req.add ("path" , "USD" ); req.addInt ("times" , 100 ); sendChainlinkRequestTo (_oracle, req, ORACLE_PAYMENT ); } function fulfillEthereumPrice ( bytes32 _requestId, uint256 _price ) public recordChainlinkFulfillment (_requestId ) { emit RequestEthereumPriceFulfilled (_requestId, _price); currentPrice = _price; } function getChainlinkToken ( ) public view returns (address) { return chainlinkTokenAddress (); } function withdrawLink ( ) public onlyOwner { LinkTokenInterface link = LinkTokenInterface (chainlinkTokenAddress ()); require ( link.transfer (msg.sender , link.balanceOf (address (this ))), "Unable to transfer" ); } function cancelRequest ( bytes32 _requestId, uint256 _payment, bytes4 _callbackFunctionId, uint256 _expiration ) public onlyOwner { cancelChainlinkRequest ( _requestId, _payment, _callbackFunctionId, _expiration ); } function stringToBytes32 ( string memory source ) private pure returns (bytes32 result) { bytes memory tempEmptyStringTest = bytes (source); if (tempEmptyStringTest.length == 0 ) { return 0x0 ; } assembly { result := mload (add (source, 32 )) } } }
这里注意将第29行的setChainlinkToken的参数改为之前部署的LinkToken合约地址
私有链中可以有多种代币,这里我们使用Metamask去管理我们的账户余额以方便我们在Remix部署合约。
打开Metamask,点击Assets,点击Import tokens
然后填入你的LinkToken合约地址和它的代币符号LINK就行。
利用LinkToken合约向ATestnetConsumer合约转账
这里我转入了1个LINK,根据实际需要转就可以了。转账以后,去原来部署的ATestnetConsumer合约中调用requestEthereumPrice
方法。
_oracle填入Operator合约地址,_jobId填入刚刚在控制台得到的JobId。
JobId要去掉中间的横线!!!
JobId要去掉中间的横线!!!
JobId要去掉中间的横线!!!
最后点击currentPrice,ETH/USD的汇率就出现了。
至此,我们实现了在区块链中获取链下的价格数据。在实际的开发中,可以将这种单一的数据拓展为特定的业务数据,以实现区块链与预言机在特定领域的应用。
基本请求模型–单预言机工作模式 - 知乎 (zhihu.com)