如何在 solidity 中调用 API?
How to make an API call in solidity?
我正在尝试制定一份智能合约,它会支付我的英雄联盟锦标赛的获胜者。但是我 运行 遇到了问题。我需要调用 API 来获得比赛的获胜者,我有一个简单的 URL。
"example-winner.com/winner"
它 returns 简单 JSON 获胜者的地址:
{"winner":"0xa7D0......."}
但是,我不确定如何对外部函数进行 API 调用。我知道我需要使用某种 oracle 技术。
有什么想法吗?下面是我的代码:
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
}
}
你没有正确解释你想做什么。您在使用 solidity 代码时遇到问题了吗?或者更确切地说与您的服务器?这是一个编辑过的版本。看看有没有帮助。
pragma solidity ^0.4.24;
contract LeagueWinners{
address public manager;
//address[] public players;
uint256 MINIMUM = 1000000000000000;
constructor() public{
manager = msg.sender;
}
struct Player {
address playerAddress;
uint score;
}
Player[] public players;
// i prefer passing arguments this way
function enter(uint value) public payable{
assert(msg.value > MINIMUM);
players.push(Player(msg.sender, value));
}
//call this to get the address of winner
function winningPlayer() public view
returns (address winner)
{
uint winningScore = 0;
for (uint p = 0; p < players.length; p++) {
if (players[p].score > winningScore) {
winningScore = players[p].score;
winner = players[p].playerAddress;
}
}
}
// call this to transfer fund
function getWinner() public{
require(msg.sender == manager, "Only a manager is allowed to perform this operation");
// TODO
address winner = winningPlayer();
// Get the winner from the API call
//uint result = 0; // the result of the API call
winner.transfer(address(this).balance);
// returns an adress object
// all units of transfer are in wei
delete players;
// this empties the dynamic array
}
}
至少我是这么理解你的问题的。
你不能。虚拟机在区块链本身之外没有任何 I/O。相反,您需要告诉您的智能合约谁是赢家,然后智能合约可以读取该变量的值。
这种设计模式也被称为"oracle"。 Google "Ethereum oracle" 了解更多信息。
基本上你的网络服务器可以调用你的智能合约。你的智能合约无法调用你的网络服务器。如果您需要您的智能合约来访问第 3 方服务,那么您的 Web 服务器将需要发出请求,然后通过调用智能合约中的函数将结果转发给 solidity。
您可以使用 Chainlink 作为您的 Oracle。
正如许多人提到的,您需要一个 oracle 来接听您的 API 电话。需要注意的重要一点是,您的合约实际上是在要求预言机为您发出 API 调用,而不是 API 调用本身。这是因为区块链是确定性的。有关详细信息,请参阅 this thread。
要回答您的问题,您可以使用去中心化预言机服务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);
}
出于以下示例的目的,我们将假装您想要 return uint256
而不是地址。您可以 return 一个 bytes32,然后将其转换为地址,但为简单起见,我们假设 API return 是获胜者的索引。您必须找到可以发出 http.get
请求和 return 一个 uint256
对象的节点和 jobId。您可以从 market.link 中找到节点和作业。每个测试网(Ropsten、Mainnet、Kovan 等)都有不同的节点地址,因此请确保选择正确的地址。
对于这个演示,我们将使用 LinkPool 的 ropsten 节点
address ORACLE=0x83F00b902cbf06E316C95F51cbEeD9D2572a349a;
bytes32 JOB= "c179a8180e034cf5a341488406c32827";
理想情况下,您会选择多个节点来 运行 您的工作,以使其无需信任且去中心化。您可以 read here 获取有关预协调器和聚合数据的更多信息。 披露我是该博客的作者
您的完整合同如下所示:
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");
}
}
这将满足您的所有需求。
如果你不能将API调整为return一个uint,你可以return一个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);
}
我正在尝试制定一份智能合约,它会支付我的英雄联盟锦标赛的获胜者。但是我 运行 遇到了问题。我需要调用 API 来获得比赛的获胜者,我有一个简单的 URL。
"example-winner.com/winner"
它 returns 简单 JSON 获胜者的地址:
{"winner":"0xa7D0......."}
但是,我不确定如何对外部函数进行 API 调用。我知道我需要使用某种 oracle 技术。
有什么想法吗?下面是我的代码:
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
}
}
你没有正确解释你想做什么。您在使用 solidity 代码时遇到问题了吗?或者更确切地说与您的服务器?这是一个编辑过的版本。看看有没有帮助。
pragma solidity ^0.4.24;
contract LeagueWinners{
address public manager;
//address[] public players;
uint256 MINIMUM = 1000000000000000;
constructor() public{
manager = msg.sender;
}
struct Player {
address playerAddress;
uint score;
}
Player[] public players;
// i prefer passing arguments this way
function enter(uint value) public payable{
assert(msg.value > MINIMUM);
players.push(Player(msg.sender, value));
}
//call this to get the address of winner
function winningPlayer() public view
returns (address winner)
{
uint winningScore = 0;
for (uint p = 0; p < players.length; p++) {
if (players[p].score > winningScore) {
winningScore = players[p].score;
winner = players[p].playerAddress;
}
}
}
// call this to transfer fund
function getWinner() public{
require(msg.sender == manager, "Only a manager is allowed to perform this operation");
// TODO
address winner = winningPlayer();
// Get the winner from the API call
//uint result = 0; // the result of the API call
winner.transfer(address(this).balance);
// returns an adress object
// all units of transfer are in wei
delete players;
// this empties the dynamic array
}
}
至少我是这么理解你的问题的。
你不能。虚拟机在区块链本身之外没有任何 I/O。相反,您需要告诉您的智能合约谁是赢家,然后智能合约可以读取该变量的值。
这种设计模式也被称为"oracle"。 Google "Ethereum oracle" 了解更多信息。
基本上你的网络服务器可以调用你的智能合约。你的智能合约无法调用你的网络服务器。如果您需要您的智能合约来访问第 3 方服务,那么您的 Web 服务器将需要发出请求,然后通过调用智能合约中的函数将结果转发给 solidity。
您可以使用 Chainlink 作为您的 Oracle。
正如许多人提到的,您需要一个 oracle 来接听您的 API 电话。需要注意的重要一点是,您的合约实际上是在要求预言机为您发出 API 调用,而不是 API 调用本身。这是因为区块链是确定性的。有关详细信息,请参阅 this thread。
要回答您的问题,您可以使用去中心化预言机服务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);
}
出于以下示例的目的,我们将假装您想要 return uint256
而不是地址。您可以 return 一个 bytes32,然后将其转换为地址,但为简单起见,我们假设 API return 是获胜者的索引。您必须找到可以发出 http.get
请求和 return 一个 uint256
对象的节点和 jobId。您可以从 market.link 中找到节点和作业。每个测试网(Ropsten、Mainnet、Kovan 等)都有不同的节点地址,因此请确保选择正确的地址。
对于这个演示,我们将使用 LinkPool 的 ropsten 节点
address ORACLE=0x83F00b902cbf06E316C95F51cbEeD9D2572a349a;
bytes32 JOB= "c179a8180e034cf5a341488406c32827";
理想情况下,您会选择多个节点来 运行 您的工作,以使其无需信任且去中心化。您可以 read here 获取有关预协调器和聚合数据的更多信息。 披露我是该博客的作者
您的完整合同如下所示:
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");
}
}
这将满足您的所有需求。
如果你不能将API调整为return一个uint,你可以return一个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);
}