I am working on a project that will need to use ChainLink to make external API calls from the Ethereum blockchain. I was testing out the demo code like so:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "#chainlink/contracts/src/v0.8/ChainlinkClient.sol";
/**
* Request testnet LINK and ETH here: https://faucets.chain.link/
* Find information on LINK Token Contracts and get the latest ETH and LINK faucets here: https://docs.chain.link/docs/link-token-contracts/
*/
/**
* THIS IS AN EXAMPLE CONTRACT WHICH USES HARDCODED VALUES FOR CLARITY.
* PLEASE DO NOT USE THIS CODE IN PRODUCTION.
*/
contract APIConsumer is ChainlinkClient {
using Chainlink for Chainlink.Request;
uint256 public volume;
address private oracle;
bytes32 private jobId;
uint256 private fee;
/**
* Network: Kovan
* Oracle: 0xc57B33452b4F7BB189bB5AfaE9cc4aBa1f7a4FD8 (Chainlink Devrel
* Node)
* Job ID: d5270d1c311941d0b08bead21fea7747
* Fee: 0.1 LINK
*/
constructor() {
setPublicChainlinkToken();
oracle = 0xc57B33452b4F7BB189bB5AfaE9cc4aBa1f7a4FD8;
jobId = "d5270d1c311941d0b08bead21fea7747";
fee = 0.1 * 10 ** 18; // (Varies by network and job)
}
/**
* Create a Chainlink request to retrieve API response, find the target
* data, then multiply by 1000000000000000000 (to remove decimal places from data).
*/
function requestVolumeData() public returns (bytes32 requestId)
{
Chainlink.Request memory request = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);
// Set the URL to perform the GET request on
request.add("get", "https://min-api.cryptocompare.com/data/pricemultifull?fsyms=ETH&tsyms=USD");
// Set the path to find the desired data in the API response, where the response format is:
// {"RAW":
// {"ETH":
// {"USD":
// {
// "VOLUME24HOUR": xxx.xxx,
// }
// }
// }
// }
request.add("path", "RAW.ETH.USD.VOLUME24HOUR");
// Multiply the result by 1000000000000000000 to remove decimals
int timesAmount = 10**18;
request.addInt("times", timesAmount);
// Sends the request
return sendChainlinkRequestTo(oracle, request, fee);
}
/**
* Receive the response in the form of uint256
*/
function fulfill(bytes32 _requestId, uint256 _volume) public recordChainlinkFulfillment(_requestId)
{
volume = _volume;
}
However, I am consistently running into a Gas estimation error after deploying and trying to call requestVolumeData.
What am I doing wrong?
Video
Gas estimation error is a common error meaning that you don't have enough of "something" to send your transaction, maybe it's layer 1 (ETH) gas, or an ERC20 token like LINK (technically, erc677, but I digress...)
Whenever you work with a Chainlink feature that uses the request and receive method of using chainlink, you need to fund the consumer contract with LINK token.
The issue here is you sent LINK to the oracle address and not the address of your deployed contract.
If you copy the address of your deployed contract and send the LINK there, it should work for you.
Related
I'm trying to build an external adapter for a chainlink node to import API information. On the chainlink node and API, everything seems like it worked, however when I try to call the stored value from the smart contract, it's always 0 despite the logs indicating that it ran successfully.
type = "directrequest"
schemaVersion = 1
name = "Mimi-Fund-EA"
externalJobID = "834d2179-321d-49ac-bf63-140635e3a606"
forwardingAllowed = false
maxTaskDuration = "0s"
contractAddress = "0xAf644831B57E5625ac64cDa68248b810bE4D4D01"
minContractPaymentLinkJuels = "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=bridge name="mimifund" requestData="{\\"id\\": $(jobSpec.externalJobID), \\"data\\": { \\"year\\": $(decode_cbor.year), \\"discount_rate\\": $(decode_cbor.discount_rate)}}"]
parse [type=jsonparse path="data,result" data="$(fetch)"]
ds_multiply [type="multiply" times=1000000000000000000]
encode_data [type=ethabiencode abi="(uint256 value)" data="{ \\"value\\": $(ds_multiply) }"]
encode_tx [type=ethabiencode
abi="fulfillOracleRequest(bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes32 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="0xAf644831B57E5625ac64cDa68248b810bE4D4D01" data="$(encode_tx)"]
decode_log -> decode_cbor -> fetch -> parse -> ds_multiply-> encode_data -> encode_tx -> submit_tx
"""
These are the run logs from the Node. Everything compiled just fine and the values look good however, they never update in a smart contract, it's always 0.
This is my smart contract for reference.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "#chainlink/contracts/src/v0.8/ChainlinkClient.sol";
import "#chainlink/contracts/src/v0.8/ConfirmedOwner.sol";
import "#openzeppelin/contracts/utils/Strings.sol";
contract mimifundCO2 is ChainlinkClient, ConfirmedOwner {
using Chainlink for Chainlink.Request;
uint256 public volume;
bytes32 private jobId;
uint256 private fee;
event RequestVolume(bytes32 indexed requestId, uint256 volume);
/**
* #notice Initialize the link token and target oracle
*
* Goerli Testnet details:
* Link Token: 0x326C977E6efc84E512bB9C30f76E30c160eD06FB
* Oracle: 0xCC79157eb46F5624204f47AB42b3906cAA40eaB7 (Chainlink DevRel)
* jobId: ca98366cc7314957b8c012c72f05aeeb
*
*/
constructor() ConfirmedOwner(msg.sender) {
setChainlinkToken(0x326C977E6efc84E512bB9C30f76E30c160eD06FB);
setChainlinkOracle(0xAf644831B57E5625ac64cDa68248b810bE4D4D01);
jobId = "834d2179321d49acbf63140635e3a606";
fee = (1 * LINK_DIVISIBILITY) / 10; // 0,1 * 10**18 (Varies by network and job)
}
/**
* Create a Chainlink request to retrieve API response, find the target
* data, then multiply by 1000000000000000000 (to remove decimal places from data).
*/
function requestCO2PricingData(uint256 _year) public returns (bytes32 requestId) {
Chainlink.Request memory req = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);
req.add('year', Strings.toString(_year)); // Chainlink nodes 1.0.0 and later support this format
req.add('discount_rate', '0.0'); // Chainlink nodes 1.0.0 and later support this format
// Sends the request
return sendChainlinkRequest(req, fee);
}
/**
* Receive the response in the form of uint256
*/
function fulfill(bytes32 _requestId, uint256 _volume) public recordChainlinkFulfillment(_requestId) {
emit RequestVolume(_requestId, _volume);
volume = _volume;
}
/**
* Allow withdraw of Link tokens from the contract
*/
function withdrawLink() public onlyOwner {
LinkTokenInterface link = LinkTokenInterface(chainlinkTokenAddress());
require(link.transfer(msg.sender, link.balanceOf(address(this))), 'Unable to transfer');
}
}
I feel like there is some update to either my fulfill function or submit_tx that I need to update, but I am out of ideas about what to change.
I've tried changing all the parameters and both the API and chainlink node accurate update and reflect the correct input. The smart contract seems to work perfectly, it's just that calling volume in the code always returns 0 and I've got no clue what the issue is.
According to your description, Chainlink node received your request and run the job successfully, but failed to write the result back to your contract. There might be multiple reasons, but most likely something is wrong when the Chainlink node calls the function fulfillOracleRequest2 in the Operator.sol and fails to write the result back to your contract.
Please check the following:
Check if you fund your Chainlink node. You can check the balance of the chainlink node in the top right of the Chainlink node UI which is usually with port number 6688(eg. http://localhost:6688). Because the Chainlink node changes the state of the blockchain when calling the function in the contract operator, there has to be a minimum balance of ETH remaining in your Chainlink node. The solution to the issue is just to transfer some ETH(not LINK) tokens to your chainlink node address.
Check if you grant the Chainlink node permission to call function fulfillOracleRequest2 in the contract operator. Search your Chainlink node address in the blockchain explorer like etherscan, goerliscan, polygonscan, etc. and if the node has no permission to call function fulfillOracleRequest, error Fail with error 'Not authorized sender' will be thrown. The solution to the issue is to use the function setAuthorizedSenders to grant the node address permission to call the function fulfillOracleRequest2.
I am testing this code on Remix:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "#chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
contract PriceConsumerV3 {
AggregatorV3Interface internal priceFeed;
/**
* Network: Kovan
* Aggregator: ETH/USD
* Address: 0x9326BFA02ADD2366b30bacB125260Af641031331
*/
constructor() {
priceFeed = AggregatorV3Interface(0x9326BFA02ADD2366b30bacB125260Af641031331);
}
/**
* Returns the latest price
*/
function getLatestPrice() public view returns (int) {
(
uint80 roundID,
int price,
uint startedAt,
uint timeStamp,
uint80 answeredInRound
) = priceFeed.latestRoundData();
return price;
}
}
Compilation and deployment seem to be executed correctly, however I receive this error:
call to PriceConsumerV3.getLatestPrice errored: Returned error: VM
Exception while processing transaction: revert
Any suggestion?
I've seen this error when deploying to the Javascript VM
It's due to the contract you are trying to call not being available on the VM you are using. You'll need to switch to the Injected Web3 option.
The deployment will also require having a wallet set up as you interact with an actual blockchain. The example you reference is using the Kovan Testnet.
Here are a few other links for reference
Metamask
Faucet if you need ETH
I'm trying to call claimItem function on my ERC721 token. This function basically calls _safeMint() function from OpenZeppelin ERC721 contract. Here is the question. As a first step I managed deploy a contract on polygon mumbai testnet and I can see my contract on polygon scan. But when I tried to call claimItem function, inside a Transaciton Transaction(from:EthereumAddress.fromHex(session!.accounts[0]) ,to:EthereumAddress.fromHex("CONTRACT_ADDRESS"), data: bytes); like this. Transaction completes but NFT is not minted.
Here Are Some Scree Shots
This one is from a successful one (This is deployed and minted with truffle & web3)
This one is from unsuccessful one (This is signed by MetaMask)
I don't know what I'm doing wrong and I'm really new into blockchain.
EDIT Contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "#openzeppelin/contracts/token/ERC721/ERC721.sol";
import "#openzeppelin/contracts/access/Ownable.sol";
import "#openzeppelin/contracts/utils/Counters.sol";
contract ArtItemNew is ERC721, Ownable {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
using Strings for uint256;
// Optional mapping for token URIs
mapping(uint256 => string) private _tokenURIs;
constructor(string memory _tokenName, string memory _tokenSymbol) ERC721(_tokenName, _tokenSymbol) public {
}
function _setTokenURI(uint256 tokenId, string memory _tokenURI)
internal
virtual
{
require(
_exists(tokenId),
"ERC721Metadata: URI set of nonexistent token"
);
_tokenURIs[tokenId] = _tokenURI;
}
function claimItem(string memory tokenURI) public returns (uint256) {
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
_mint(msg.sender, newItemId);
_setTokenURI(newItemId, tokenURI);
return newItemId;
}
}
I've been learning the Chainlink API and trying to build a simple contract that will make an external call to an API and charge the user based on the result of the request from the Oracle.
For example, "We will charge you $1 if the API results in true and $0.25 if it results in false"
I am running this on the Kovan Testnet, the contract is funded with LINK. The transaction is successful every time I run the "requestCompletedData" function. But the callback/fulfill function never gets ran. I've checked it in various ways.
For reference, it should result completed == true based on the data from the URL.
Here are the Contract Address and Job ID for Chainlink's Kovan test nodes: https://docs.chain.link/docs/decentralized-oracles-ethereum-mainnet/
//
constructor() public {
setPublicChainlinkToken();
oracle = 0x2f90A6D021db21e1B2A077c5a37B3C7E75D15b7e;
jobId = "6d914edc36e14d6c880c9c55bda5bc04";
fee = 0.1 * 10 * 18; // 0.1 LINK
}
// Make Chainlink request
function requestCompletedData() public returns (bytes32 requestId) {
Chainlink.Request memory request = buildChainlinkRequest(jobId, address(this), this.fulfill.selector);
// URL for request
request.add("get", "https://jsonplaceholder.typicode.com/todos/4");
// Path to the final needed data point in the JSON response
request.add("path", "completed");
return sendChainlinkRequestTo(oracle, request, fee);
}
function fulfill(bytes32 _requestId, bool _completed) public recordChainlinkFulfillment(_requestId) {
validateChainlinkCallback(_requestId);
completed = _completed;
}
Thank you for your help!
Remove the validateChainlinkCallback(_requestId) line in your fulfill() method and it will work.
function fulfill(bytes32 _requestId, bool _completed) public recordChainlinkFulfillment(_requestId) {
completed = _completed;
}
The fulfill() method already has the recordChainlinkFulfillment modifier which runs the same validation as the validateChainlinkCallback(_requestId) method anyway.
Reference: ChainlinkClient source code.
I am developing smart contract based on openzeppelin-solidity and I want to write an easy Crowdsale contract, only I did is inherit Contract.sol:
// FloatFlowerTokenCrowdsale.sol
pragma solidity 0.4.23;
import "openzeppelin-solidity/contracts/crowdsale/Crowdsale.sol";
contract FloatFlowerTokenCrowdsale is Crowdsale{
constructor(ERC20 _token) public Crowdsale(1000, msg.sender, _token)
{
}
}
Here is my FloatFlowerToken.sol
// FloatFlowerToken.sol
pragma solidity 0.4.23;
import "openzeppelin-solidity/contracts/token/ERC20/StandardToken.sol";
contract FloatFlowerToken is StandardToken {
string public name = "FLOATFLOWER TOKEN";
string public symbol = "FFT";
uint8 public decimals = 18;
constructor() public {
totalSupply_ = 36000000;
balances[msg.sender] = totalSupply_;
}
}
And this is my 2_deploy_contract.js
const FloatFlowerToken = artifacts.require('./FloatFlowerToken.sol');
const FloatFlowerTokenCrowdsale =
artifacts.require('./FloatFlowerTokenCrowdsale.sol');
module.exports = function(deployer, network, accounts) {
return deployer
.then(() => {
return deployer.deploy(FloatFlowerToken);
})
.then(() => {
return deployer.deploy(FloatFlowerTokenCrowdsale, FloatFlowerToken.address);
})
};
After I execute the truffle test and I got the error Error: VM Exception while processing transaction: revert
And here is my test code:
it('one ETH should buy 1000 FLOATFLOWER TOKEN in Crowdsale', function(done) {
FloatFlowerTokenCrowdsale.deployed().then(async function(instance) {
const data = await instance.sendTransaction({from: accounts[7], value: web3.toWei(1, "ether")}, function(error, txhash) {
console.log(error);
});
const tokenAddress = await instance.token.call();
const FloatFlowerToken = FloatFlowerToken.at(tokenAddress);
const tokenAmount = await FloatFlowerToken.balanceOf(accounts[7]);
assert.equal(tokenAmount.toNumber(), 1000000000000000000000, 'The sender didn\'t receive the tokens as crowdsale rate.');
})
})
I don't know how to check the error log and to know which line cause this problem.
You have 2 issues:
First, the units you're working with aren't correct. You've initialized your crowdsale to sell 1000 tokens for every Wei sent. From the documentation in the Zeppelin contract:
#param _rate Number of token units a buyer gets per wei
#param _wallet Address where collected funds will be forwarded to
#param _token Address of the token being sold
You're passing in 1 ether in your transaction, which means you're attempting to buy 1000 * (10^18) token units, but you've only allocated 36000000 total supply. You need to increase your total supply and/or lower your rate.
Second, only token owners can do a transfer unless an approve has been done first. When you deploy the token contract, all of the tokens are owned by msg.sender. However, when someone makes a purchase through your crowdsale contract, the request to do the transfer is coming from the address of the crowdsale contract, not the token owner when your token contract was deployed. The simplest way around this is after deploying your contracts, transfer enough tokens for the crowdsale from the address you used to create the token contract over to the address of the crowdsale contract.