如何可靠地进行 API 调用?

4 api ethereum solidity smartcontracts

我有一个我正在努力制定的智能合约,它支付我的英雄联盟锦标赛的获胜者。但是我遇到了一个问题。我需要进行 API 调用以获得比赛的获胜者,我有一个简单的 URL。

"example-winner.com/winner"
Run Code Online (Sandbox Code Playgroud)

它返回带有获胜者地址的简单 JSON:

{"winner":"0xa7D0......."}
Run Code Online (Sandbox Code Playgroud)

但是,我不确定如何对外部函数进行 API 调用。我知道我需要使用某种预言机技术。

有什么想法吗?下面是我的代码:

pragma solidity ^0.4.24;
contract LeagueWinners{
    address public manager;
    address[] public players;
    uint256 MINIMUM = 1000000000000000;
    constructor() public{
        manager = msg.sender;
    }
    function enter() public payable{
        assert(msg.value > MINIMUM);
        players.push(msg.sender);
    }
    function getWinner() public{
        assert(msg.sender == manager);
        // TODO
        // Get the winner from the API call
        result = 0; // the result of the API call
        players[result].transfer(address(this).balance);
        // returns an adress object
        // all units of transfer are in wei
        players = new address[](0);
        // this empties the dynamic array
    }
}
Run Code Online (Sandbox Code Playgroud)

Pat*_*ins 6

您可以将 Chainlink 用作您的Oracle

正如许多人提到的,您需要一个 oracle 来获取 API 调用。需要注意的重要一点是,您的合约实际上是要求预言机为您调用 API,而不是调用 API 本身。这是因为区块链是确定性的。有关更多信息,请参阅此线程

要回答您的问题,您可以使用去中心化预言机服务Chainlink

你会添加一个函数:

  function getWinner() 
    public
    onlyOwner
  {
    Chainlink.Request memory req = buildChainlinkRequest(JOB, address(this), this.fulfill.selector);
    req.add("get", "example-winner.com/winner");
    req.add("path", "winner");
    sendChainlinkRequestTo(ORACLE, req, ORACLE_PAYMENT);
  }
Run Code Online (Sandbox Code Playgroud)

出于以下示例的目的,我们将假装您想要返回一个uint256而不是地址。您可以返回一个 bytes32,然后将其转换为地址,但为简单起见,假设 API 返回获胜者的索引。您必须找到可以发出http.get请求并返回uint256对象的节点和 jobId 。您可以从market.link找到节点和作业。每个测试网(Ropsten、Mainnet、Kovan 等)都有不同的节点地址,因此请确保选择正确的节点地址。

对于这个演示,我们将使用 LinkPool 的 ropsten 节点

address ORACLE=0x83F00b902cbf06E316C95F51cbEeD9D2572a349a;
bytes32 JOB= "c179a8180e034cf5a341488406c32827";
Run Code Online (Sandbox Code Playgroud)

理想情况下,您会选择多个节点来运行您的工作,使其无需信任和去中心化。您可以在此处阅读有关预协调器和聚合数据的更多信息。披露我是那个博客的作者

您的完整合同如下所示:

pragma solidity ^0.6.0;

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


contract GetData is ChainlinkClient {
    uint256 indexOfWinner;
    address public manager;
    address payable[] public players;
    uint256 MINIMUM = 1000000000000000;
  
  // The address of an oracle 
    address ORACLE=0x83F00b902cbf06E316C95F51cbEeD9D2572a349a;
    //bytes32 JOB= "93fedd3377a54d8dac6b4ceadd78ac34";
    bytes32 JOB= "c179a8180e034cf5a341488406c32827";
    uint256 ORACLE_PAYMENT = 1 * LINK;

  constructor() public {
    setPublicChainlinkToken();
    manager = msg.sender;
  }

function getWinnerAddress() 
    public
    onlyOwner
  {
    Chainlink.Request memory req = buildChainlinkRequest(JOB, address(this), this.fulfill.selector);
    req.add("get", "example-winner.com/winner");
    req.add("path", "winner");
    sendChainlinkRequestTo(ORACLE, req, ORACLE_PAYMENT);
  }

  // When the URL finishes, the response is routed to this function
  function fulfill(bytes32 _requestId, uint256 _index)
    public
    recordChainlinkFulfillment(_requestId)
  {
    indexOfWinner = _index;
    assert(msg.sender == manager);
    players[indexOfWinner].transfer(address(this).balance);
    players = new address payable[](0);
  }
  
  function enter() public payable{
        assert(msg.value > MINIMUM);
        players.push(msg.sender);
    } 
    
  modifier onlyOwner() {
    require(msg.sender == manager);
    _;
  }
    
    // Allows the owner to withdraw their LINK on this contract
  function withdrawLink() external onlyOwner() {
    LinkTokenInterface _link = LinkTokenInterface(chainlinkTokenAddress());
    require(_link.transfer(msg.sender, _link.balanceOf(address(this))), "Unable to transfer");
  }
  
  
}
Run Code Online (Sandbox Code Playgroud)

这将完成您需要的一切。


如果你不能调整API返回一个uint,你可以返回一个bytes32,然后将其转换为地址或字符串。

 function bytes32ToStr(bytes32 _bytes32) public pure returns (string memory) {
     bytes memory bytesArray = new bytes(32);
     for (uint256 i; i < 32; i++) {
         bytesArray[i] = _bytes32[i];
         }
     return string(bytesArray);
     }
Run Code Online (Sandbox Code Playgroud)