How to make an API call in solidity? - api

I have a smart contract that I’m trying to make, it pays out the winners of my League of Legends tournament. However I’m running into an issue. I need to make an API call to get the winner of the match, I have a simple URL that I’ve make.
"example-winner.com/winner"
And it returns simple JSON with the address of the winner:
{"winner":"0xa7D0......."}
However, I’m not sure how to make the API call to the outside function. I know I need to use some sort of oracle technology.
Any thoughts? Below is my code:
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
}
}

You can use Chainlink as your Oracle.
As many have mentioned, you will need an oracle to get your API call. Something that is important to note, your contract is actually asking an oracle to make your API call for you, and not making the API call itself. This is because the blockchain is deterministic. For more information see this thread.
To answer your question, you can use the decentralized oracle service Chainlink.
You'd add a function:
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);
}
For the purpose of the following exmaple, we are going to pretend you want to return a uint256 instead of an address. You can return a bytes32 and then convert it to an address, but for simplicity let's say the API returns the index of the winner. You'll have to find a node and jobId that can make a http.get request and return a uint256 object. You can find nodes and jobs from market.link. Each testnet (Ropsten, Mainnet, Kovan, etc) has different node addresses, so make sure you pick the right ones.
For this demo, we are going to use LinkPool's ropsten node
address ORACLE=0x83F00b902cbf06E316C95F51cbEeD9D2572a349a;
bytes32 JOB= "c179a8180e034cf5a341488406c32827";
Ideally, you'd choose a number of nodes to run your job, to make it trustless and decentralized. You can read here for more information on precoordinators and aggregating data. disclosure I am the author of that blog
Your full contract would look like:
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");
}
}
This would do about everything you need.
If you can't adjust the API to return a uint, you can return a bytes32 and then convert it to an address or a string.
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);
}

You cannot. The vm does not have any I/O outside of the blockchain itself. Instead you will need to tell your smart contract who the winner is and then the smart contract can just read the value of that variable.
This design pattern is also known as the "oracle". Google "Ethereum oracle" for more info.
Basically your web server can call your smart contract. Your smart contract cannot call your web server. If you need your smart contract to access a 3rd party service then your web server will need to make the request then forward the result to solidity by calling a function in your smart contract.

You didn't properly explain what you are trying to do. Are you having trouble with the solidity code? or rather with your server? Here is an edited version. See if it helps.
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
}
}
At least that is what I understand by your question.

Related

What to do to solve unpredictable gas fees from a function in a smart contract using boolean?

The smart contract I have written seems to have an error where the gas fees are unpredictable and even if I increase the max limit, the transaction fails and gets reverted. This specific function uses a bool and am wondering if it is the cause.
I was trying to pass the function goodsDelivered with the exchangeID which is the transaction hash from executing function openExchange, however when I try changing the goodsDelivered to 'true', I get a warning from metamask stating: unpredictable_gas_limit and gets reverted. Initially I thought it was an error from the client side where I wrote js code to be able to interact with the smart contract but when I tried writing on etherscan directly there was also the transaction failed. I have also tried debugging on Remix but it functions fine on remix.
pragma solidity ^0.8.0;
contract Exchange{
struct exchange{
uint amount;
address buyer;
address seller;
address agent;
bool goodsDelivered;
}
constructor() {
}
uint nonce;
mapping (bytes32 => exchange) public exchangeRegistry;
function openExchange(address _seller, address _agent) external payable returns (bytes32 _exchangeId) {
address _buyer = msg.sender;
uint _amount = msg.value;
_exchangeId = keccak256(abi.encodePacked(_buyer, nonce));
exchangeRegistry[_exchangeId].amount = _amount;
exchangeRegistry[_exchangeId].buyer = _buyer;
exchangeRegistry[_exchangeId].seller = _seller;
exchangeRegistry[_exchangeId].agent = _agent;
nonce += 1;
}
function withdrawAmount(bytes32 _exchangeId) external payable returns (bool){
require((exchangeRegistry[_exchangeId].goodsDelivered == true && msg.sender == exchangeRegistry[_exchangeId].seller)
|| (exchangeRegistry[_exchangeId].goodsDelivered == false && msg.sender == exchangeRegistry[_exchangeId].buyer));
payable(msg.sender).transfer(exchangeRegistry[_exchangeId].amount);
return (true);
}
function goodsDelivered(bytes32 _exchangeId, bool _goodsDelivered) external returns (bool){
require(msg.sender == exchangeRegistry[_exchangeId].agent);
exchangeRegistry[_exchangeId].goodsDelivered = _goodsDelivered;
}
}

Getting Block Hash of another contract

I've seen some problems with calling functions from other contracts but I believe my problem is fairly genuine to demand a separate question if only to be negated in its possibility.
So I am trying to call a contract within another contract. Is it possible to get the blockhash of a particular block number of the callee contract within my caller? If so how?
Every syntax I've attempted fails for some reason.
Contract A
enter code here
contract DiceGame {
uint256 public nonce = 0;
uint256 public prize = 0;
event Roll(address indexed player, uint256 roll);
event Winner(address winner, uint256 amount);
constructor() payable {
resetPrize();
}
function resetPrize() private {
prize = ((address(this).balance * 10) / 100);
}
function rollTheDice() public payable {
require(msg.value >= 0.002 ether, "Failed to send enough value");
bytes32 prevHash = blockhash(block.number - 1);
bytes32 hash = keccak256(abi.encodePacked(prevHash, address(this), nonce));
uint256 roll = uint256(hash) % 16;
console.log('\t'," Dice Game Roll:",roll);
nonce++;
prize += ((msg.value * 40) / 100);
emit Roll(msg.sender, roll);
if (roll > 2 ) {
return;
}
uint256 amount = prize;
(bool sent, ) = msg.sender.call{value: amount}("");
require(sent, "Failed to send Ether");
resetPrize();
emit Winner(msg.sender, amount);
}
receive() external payable { }
}
Contract B
enter code here
contract RiggedRoll is Ownable {
DiceGame public diceGame;
constructor(address payable diceGameAddress) {
diceGame = DiceGame(diceGameAddress);
}
//Add withdraw function to transfer ether from the rigged contract to an address
//Add riggedRoll() function to predict the randomness in the DiceGame contract and only roll when it's going to be a winner
function riggedRoll(bytes32 riggedHash) public payable {
riggedHash = address(diceGame).blockhash(block.number-1); //I am aware this syntax is broken but I am not able to find a legitimate one to access the data from contract A.
}
//Add receive() function so contract can receive Eth
receive() external payable { }
}
A contract doesn't know when it was last called, unless you explicitly store this information.
Then you can get the block hash using the native blockhash() function (accepts the block number as a parameter).
contract Target {
uint256 public lastCalledAtBlockNumber;
// The value is stored only if you invoke the function using a (read-write) transaction.
// If you invoke the function using a (read-only) call, then it's not stored.
function foo() external {
lastCalledAtBlockNumber = block.number;
}
}
bytes32 blockHash = blockhash(block.number);

A Lottery Solidity Smart Contract Accept token as payment instead of ether

pragma solidity ^0.4.21;
contract Lottery {
address public manager;
address[] public players;
constructor() public {
manager = msg.sender;
}
function enter() public payable {
require(msg.value > .01 ether);
players.push(msg.sender);
}
function random() private view returns (uint) {
return uint(keccak256(abi.encodePacked(block.difficulty, now, players)));
}
function pickWinner() public restricted {
uint index = random() % players.length;
players[index].transfer(address(this).balance);
players = new address[](0);
}
function getPlayers() public view returns (address[]) {
return players;
}
modifier restricted() {
require(msg.sender == manager);
_;
}
}
I want o change the function
function enter() public payable {
require(msg.value > .01 ether);
players.push(msg.sender);
}
Instead of ether, user use our token/erc20 to enter the lottery
You can define an interface (in your contract) of the token contract. Since you're only going to be using the transferFrom() function, this is the only function that you need to define in the interface (no matter that the token contract contains other functions as well).
interface IERC20 {
function transferFrom(address _from, address _to, uint256 _amount) external returns (bool);
}
The you can execute the transferFrom() function of the token, passing it arguments:
from: the user executing your enter() function
to: your contract
amount: Assuming the token has 18 decimals (most tokens do), you can use the ether helper unit, because it effectively calculates "0.01 * 10^18 (or 10^16, or 10000000000000000) of the token smallest units", which is 0.01 of the token. Otherwise, you'll need to recalculate this number based on the token decimals.
function enter() public payable {
IERC20 token = IERC20(address(0x123)); // Insert the token contract address instead of `0x123`
require(token.transferFrom(msg.sender, address(this), .01 ether));
players.push(msg.sender);
}
Important: Mind that the user needs to approve() your contract to spend their tokens beforehand (from their address), otherwise the token transfer would fail. There's a security reason for that, read more about it in the bottom part of this answer.

How to add method id in metamask

I have deployed smart contract using remix IDE, launched with Injected Web3 on Ropsten test network. I could call BuyTokens function within solidity IDE successfully, but when tried to buy tokens with metamask from other address transaction get reverted. I can see the difference between those operations on ropsten.etherscan explorer - the difference is in Input Data field.
Metamask transaction has value 0x and transaction via remix is:
Function: buyTokens() ***
MethodID: 0xd0febe4c
Code:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
contract Token {
// Track how many tokens are owned by each address.
mapping (address => uint256) public balanceOf;
// Modify this section
string public name = "DemoCoin";
string public symbol = "DC";
uint8 public decimals = 8;
uint256 public totalSupply = 1000000000 * (uint256(10) ** decimals);
address public owner;
//uint scaler = 10e18; // == 1 ETH in wei
//uint public coinPrice = 20; //initial price => 20 cents
event Transfer(address indexed from, address indexed to, uint256 value);
constructor() {
// Initially assign all tokens to the contract's creator.
owner = msg.sender;
balanceOf[msg.sender] = totalSupply;
emit Transfer(address(0), msg.sender, totalSupply);
}
// Might be executed automaticlly
// https://blog.chronologic.network/schedule-your-transaction-now-using-mycrypto-and-myetherwallet-17b48166b412
// function changeCoinPrice() public {
// uint newCoinPrice;
// require(msg.sender == address(0));
// coinPrice = newCoinPrice;
// }
function buyTokens() public payable {
// msg.value in wei so 1ETH = 10e18
// lets set 0.20 cents for 1 token
uint paidAmount;
require(balanceOf[msg.sender] >= paidAmount);
require(balanceOf[owner] >= value);
uint tokens;
tokens = value/10e14;
balanceOf[owner] -= tokens;
balanceOf[msg.sender] += tokens;
emit Transfer(owner, msg.sender, tokens);
}
function msgSenderBalancce() public view returns (uint) {
return balanceOf[msg.sender];
}
function withDrawEth() public view {
require(msg.sender == owner);
}
}
Why these methods are called diffrently? And how to add method id in metamask? Or am I missing something and this should be handled in other way?
MetaMask has a very basic UI. It only allows transfers of ETH and standardized tokens, but it doesn't show any buttons to call other contract functions. It also doesn't allow creating any custom buttons in their UI.
You'll need to set the data field of the transaction to 0xd0febe4c (which effectively executes the buyTokens() function).
But - they also don't allow specifying the data field value manually in the UI, so you'll need to preset it using the Ethereum provider API.
Your web app connects to the user's MetaMask acccount. It opens a MetaMask window and the user needs to manually confirm the connect.
The web app sends a request to MetaMask specifying the transaction with data field value.
The user confirms the transaction (which now includes the data field value 0xd0febe4c) in their MetaMask UI.

Transfer ownership web3

I am creating a dapp to transfer ownership of the contract from one address to another using testrpc. However,I keep encountering this problem. I have tried using sentransaction method to do perform this ownership change.Perhaps I'm calling the exchange in a wrong manner.
Solidity version 0.4.4
web3 "version": "0.20.2"
web3.js:3127 Uncaught Error: VM Exception while processing transaction: invalid opcode
at Object.InvalidResponse (web3.js:3127)
at RequestManager.send (web3.js:6332)
at Eth.send [as sendTransaction] (web3.js:5066)
at SolidityFunction.sendTransaction (web3.js:4122)
at SolidityFunction.execute (web3.js:4208)
at transferOwnership (luxcure_manu.html:309)
at HTMLButtonElement.onclick (luxcure_manu.html:378
Full solidity contract as of yet.
pragma solidity ^0.4.4;
// TODO: Hash of the cert through IPFS Hash
// Transfer ownership of smart contract
contract LuxSecure {
address public contract_owner; //Manufacturer/owner
//string public current_owner; //Current Owner of good
bytes32 public model; //Model
mapping(uint => address) public owners; //list of owners
uint256 public owners_count;
bytes32 public status; // (Public(Owned by no one), Private(Bought by another entity),stolen(Stolen from public or private))
bytes32 public date_manufactured; //Time
// Set manufacturer of the Good RUN ONCE ONLY
function manufacturer() public{
if(owners_count == 0){
contract_owner = msg.sender;
}
}
//Modifier that only allows owner of the bag to Smart Contract AKA Good to use the function
modifier onlyOwner(){
require(msg.sender == contract_owner);
_;
}
// Add a new product to the blockchain with a new serial
function addNewGoods(bytes32 _model,bytes32 _status, bytes32 _date_manufactured) public returns(bool made) {//Declare Goods struct
setOwner(msg.sender);
model = _model;
status = _status;
date_manufactured = _date_manufactured;
return true;
}
//This function transfer ownership of contract from one entity to another
function transferOwnership(address _newOwner) public onlyOwner(){
require(_newOwner != address(0));
contract_owner = _newOwner;
}
//Set the KEY to uint256 and VALUE owner Ethereum Address
function setOwner(address owner)public{
owners_count += 1 ;
owners[owners_count] = owner;
}
//Get the previous owner in the mappings
function previousOwner()constant public returns(address){
if(owners_count != 0){
uint256 previous_owner = owners_count - 1;
return owners[previous_owner];
}
}
// Getter Methods
function getManufacturer() constant public returns(address){
return contract_owner;
}
function getCurrentOwner() constant public returns(address){
return owners[owners_count] ;
}
function getOwnerCount() constant public returns(uint256){
return owners_count;
}
function getModel() constant public returns(bytes32){
return model;
}
function getStatus() constant public returns(bytes32){
return status;
}
function getDateManufactured() constant public returns(bytes32){
return date_manufactured;
}
}// end of LuxSecure
Javascript to perform the transfer of ownership
function transferOwnership(){
var account_to_transfer = document.getElementById("ethereumaddress").value;
contract.transferOwnership(account_to_transfer,{
from:web3.eth.accounts[0],
gas:4000000
});
}
I don't see any particular mistake in your code. Maybe a bad formatting on the front-side, but can't guess for sure as we have partial front here.
I don't know if it will be some help but sometimes, using truffle, it happened to me to have some functions that returned bad opcode from testrpc/ganache-cli while no apparent error was in the code.
Deleting the ABI, recompiling the smart-contracts to get a brand new ABI and then redeploying the contracts solved the problem.