Chainlink request not returning the integer at the followed path - google-search-api

I have a very simple smart contract which creates and sends a Chainlink request to the Kovan Linkpool node using the get>uint256 job. The contract looks like this (API private key removed).
contract OracleChainlink is ChainlinkClient {
using Chainlink for Chainlink.Request;
uint256 public H_Index;
address private Oracle;
bytes32 private JobId;
uint256 private Fee = .1 * 10 ** 18; //kovan is .1 link per call
constructor() public {
setPublicChainlinkToken();
Oracle = 0x56dd6586DB0D08c6Ce7B2f2805af28616E082455; //Chainlink linkpool node on Kovan
JobId = "b6602d14e4734c49a5e1ce19d45a4632";
}
function getChainlinkToken() public view returns (address) {
return chainlinkTokenAddress();
}
function RequestH_index() public returns (bytes32 Reqid) {
Chainlink.Request memory Req = buildChainlinkRequest(JobId, address(this), this.fulfill.selector);
Req.add("get", "https://serpapi.com/.....");
Req.add("path", "cited_by.table.1.h_index.all");
return sendChainlinkRequestTo(Oracle, Req, Fee);
}
function fulfill(bytes32 Reqid, uint256 _Hindex) public recordChainlinkFulfillment(Reqid) {
H_Index = _Hindex;
}
The Google Scholar Author API https://serpapi.com/google-scholar-author-api returns a pretty large json, seen at the link if you scroll down. The snippet/path I need to follow is shown below (cited_by is at the top level of the json).
"cited_by": {
"table": [
{
"citations": {
"all": 23351,
"since_2016": 13660
}
},
{
"h_index": {
"all": 46,
"since_2016": 37
}
},
{
"i10_index": {
"all": 60,
"since_2016": 53
}
}
],
When ran, I get logs of Chainlink request events, but the public H_Index value remains 0. Am I missing something in terms of adapters? I have tried all sorts of path formats through the JSON with no luck. I have also tried different nodes and jobs. Is there any way to ensure that the API is even being called? What am I missing?

Your JSON Path should look like this:
Req.add("path", "cited_by.table.1.h_index.all");
Looking at the Etherscan activity, it looks like the node you are using may be inactive. Try this node and jobId:
Oracle = 0xc57B33452b4F7BB189bB5AfaE9cc4aBa1f7a4FD8;
JobId = "d5270d1c311941d0b08bead21fea7747";
These were taken from the Chainlink Official Docs.
To check to see if a node is running or not, check out the oracle address in a block explorer. You can see here that the original node you tried to use hasn't posted a transaction in quite a long time.
If a node is inactive you will need to find a new one or host one yourself. To find more nodes and jobs, you can check market.link or use the one found in the docs as mentioned earlier.

Related

How to create a time-based upkeep directly from my contract rather than use the GUI?

I want to create a time-based upkeep directly from my contract. I was able to register and fund the upkeep but for some reason the function is not getting executed automatically.
Here's the code
`
// Goerli network
address public cronFactoryAddress = 0x1af3cE8de065774B0EC08942FC5779930d1A9622;
address public keeperRegistrar = 0x57A4a13b35d25EE78e084168aBaC5ad360252467;
constructor(){
cronFactory = ICronFactory(cronFactoryAddress);
}
function createUpkeep(string memory _cronString) public{
address _target = address(this);
bytes memory functionToCall = bytes(abi.encodeWithSignature("sendSalary(string)", _cronString));
bytes memory job = cronFactory.encodeCronJob(_target, functionToCall, _cronString);
uint256 maxJobs = cronFactory.s_maxJobs();
address delegateAddress = cronFactory.cronDelegateAddress();
address newCronUpkeep = address(new CronUpkeep(msg.sender, delegateAddress, maxJobs, job));
allUpkeeps.push(newCronUpkeep);
}
function fundUpkeep(uint256 _linkAmount, address _upkeepAddress) public{
bytes4 reg = bytes4(keccak256("register(string,bytes,address,uint32,address,bytes,bytes,uint96,address)"));
bytes memory _data = abi.encode(
"TestV2",
"",
_upkeepAddress,
uint32(500000),
address(this),
"",
"",
_linkAmount,
address(this)
);
bytes memory combinedData = abi.encodePacked(reg, _data);
LinkContract.transferAndCall(keeperRegistrar, _linkAmount, combinedData);
}
sendSalary is the function in my contract that I want to be executed at regular intervals.
cronFactory is the cron factory contract.
cronUpkeep is the cronUpkeep.sol contract from the chainlink github repo.
To create these functions, I created a time-based upkeep manually and used the transaction logs to find what all function are being called and implemented the same here.
But, Once I execute both these functions nothing happens, however, I am able to find the upkeep registered on chainlink's website . And also it shows the trigger as custom trigger on upkeep page on chainlink:
chanlink upkeep
Please let me know how I can solve this? Any help would be appreciated. Thanks in advance
Contracts cannot execute themselves. Function needs to be called. While contract (function) is not called, contract is sleeping, because every time it makes operations, they should be payed (aka gas), so there is no way to throw an allways-active-timer inside of the contract (infinite gas). It means that you have to make calls manually or use automation services like ChainLink, Openzepplin Defender etc.
You can make a requirement by time-passed with
uint256 private lastTimeStamp;
uint256 private interval;
constructor() {
lastTimeStamp = block.timestamp;
interval = 7 days;
}
function isTimePassed() public view returns (bool timePassed) {
timePassed = ((block.timestamp - lastTimeStamp) > /*7 days */ interval);
return timePassed;
}
function smth() public {
(bool timePassed) = isTimePassed();
...
}
Something like this.

Testing ChainlinkClient callbacks - how to bypass recordChainlinkFulfillment?

I've got a Chainlink client contract which makes a DirectRequest to an oracle. The oracle does its thing and then returns the answer via the typical callback selector passed in via the ChainlinkRequest. It all works well, but I'd like to write some tests that test the callback implementation
My client contract is as follows:
contract PriceFeed is Ownable, ChainlinkClient {
function updatePrice() onlyOwner returns (bytes32 requestId) {
// makes Chainlink request specifying callback via this.requestCallback.selector
}
function requestCallback(bytes32 _requestId, uint256 _newPrice) public
recordChainlinkFulfillment(_requestId) {
price = _newPrice;
}
}
The problem arises when the test code calls requestCallback(...) and the code hits the recordChainlinkFulfillment(...) modifier. The ChainlinkClient complains that the requestId being passed in by the test below isn't in the underling private pendingRequests mapping maintained by the ChainlinkClient.
The simplified version of ChainlinkClient looks like this:
contract ChainlinkClient {
mapping(bytes32 => address) private pendingRequests;
modifier recordChainlinkFulfillment(bytes32 _requestId) {
require(msg.sender == pendingRequests[_requestId], "Source must be the oracle of the request");
delete pendingRequests[_requestId];
emit ChainlinkFulfilled(_requestId);
_;
}
}
My Foundry/Solidity test is as follows:
contract PriceFeedTest is Test {
function testInitialCallback() public {
priceFeed.requestCallback("abc123", 1000000); // fails on this line
assertEq(1000000, priceFeed.price(), "Expecting price to be 1000000");
}
}
The code fails on first line of the testInitialCallback() line with: Source must be the oracle of the request
How can I trick the ChainklinkClient into allowing my callback to get past the modifier check? AFAIK I can't access and pre-populate the private pendingRequests mapping. Is there another way?
I know that Foundry provides Cheatcodes to help in testing and there's a stdstorage cheatcode, but I'm not familiar on how to construct a call to stdstorage to override pendingRequests if thats even possible with a cheatcode.
contract PriceFeedTest is Test {
function testInitialCallback2() public {
stdstore
.target(address(priceFeed))
.sig("pendingRequests()")
.with_key("abc123")
.checked_write(address(this));
priceFeed.requestCallback("abc123", 1000000);
assertEq(1000000, priceFeed.price(), "Expecting price to be 1000000");
}
}
The above code throws the following error: No storage use detected for target
Any help would be greatly appreciated. Many thanks.
When you execute the updatePrice function in your test, you should be able to strip out the requestId from the transaction receipt event. Once you have that, you can then use it in your call to requestCallback. Check out this example unit test from the hardhat starter kit for an example of this

Chainlink- Issue when fetching an API in form of `bytes32`

I've been learning the chainlink API and trying to modify the example from the Chainlin's documentation to get a byets32 value from an API. The original code of the example works well, but since the API I was hitting is returning a byets32 foem, the Chainlink job need to be configured to handle this type of return. The node is given here with Kovan testnet. Here is my code
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;
//Result of the api
bytes32 public martket;
address private oracle;
bytes32 private jobId;
uint256 private fee;
/**
* Get -> bytes32
* Network: Kovan
* Oracle: 0xc57B33452b4F7BB189bB5AfaE9cc4aBa1f7a4FD8 (Chainlink Devrel
* Node)
* Job ID: 7401f318127148a894c00c292e486ffd
* Fee: 0.1 LINK
*/
constructor() {
setPublicChainlinkToken();
// Get -> bytes32 node taken from documentation
oracle = 0xc57B33452b4F7BB189bB5AfaE9cc4aBa1f7a4FD8;
jobId = "7401f318127148a894c00c292e486ffd";
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":
// {
// "MARKET": "CCCAGG",
// }
// }
// }
// }
//Get the MARKET field of API
request.add("path", "RAW,ETH,USD,MARKET"); // Chainlink nodes 1.0.0 and later support this format
// Sends the request
return sendChainlinkRequestTo(oracle, request, fee);
}
/**
* Receive the response in the form of bytes32
*/
function fulfill(bytes32 _requestId, bytes32 _market) public recordChainlinkFulfillment(_requestId)
{
martket = _market;
}
// function withdrawLink() external {} - Implement a withdraw function to avoid locking your LINK in the contract
}
The value of market should be a byets32 reprsenting "CCCAGG" as shown in the API. But what I got was just 0x0...00 all the time, which means market has not been modified yet. I've checked this various ways and found out that the fulfill function never get rans. Then same thing happens when I changed the jobId and oracle to handle get-> int256, get -> bool (of course I did change the return type of the variable such that it's consistent with the returning form of API). I also noticed that only the job get -> uint256 works well (the example from documentation also used this job). Does anyon know why? Was my code wrong or the problem came from the node/job? Since I was able the get the example right, I don't think the problem cam from my wallet.
Any help would be appreciated!
You can test with a GET>STRING JOB
Go to this link: https://docs.chain.link/docs/any-api-testnet-oracles/
Take the Oracle contract address relevant to your network. For instance: 0xCC79157eb46F5624204f47AB42b3906cAA40eaB7 for Goerli
Use a Get>String job: 7d80a6386ef543a3abb52817f6707e3b
Make sure that your callback function expects to receive a string. (see a full example here: https://docs.chain.link/docs/api-array-response/)

Requesting data with Chainlink, response not what expected

I've been trying to integrate Chainlink into my contract, managed to get the random number thingy working, but the API call doesn't work for me. Here's what I got:
contract ValorantCards is Ownable, ERC1155, VRFConsumerBase, ChainlinkClient {
using Chainlink for Chainlink.Request;
address private linkToken;
// Chainlink VRF
bytes32 private keyHash;
uint256 private vrfFee;
uint256 public randomResult;
// Chainlink API calls
address private oracle;
bytes32 private jobId;
uint256 private oracleFee;
uint256 public playerLevel;
constructor(
address _vrfCoordinator,
address _linkToken,
bytes32 _keyHash,
address _oracle,
bytes32 _jobId,
uint256 _oracleFee
) ERC1155("") VRFConsumerBase(_vrfCoordinator, _linkToken) {
setPublicChainlinkToken();
linkToken = _linkToken;
keyHash = _keyHash;
vrfFee = 0.1 * 10**18;
oracle = _oracle;
jobId = _jobId;
oracleFee = _oracleFee;
}
function requestUserLevel() public returns (bytes32 requestId) {
Chainlink.Request memory request = buildChainlinkRequest(
jobId,
address(this),
this.fulfill.selector
);
request.add(
"get",
"https://api.henrikdev.xyz/valorant/v1/account/draven/2023"
);
request.add("path", "data.account_level");
return sendChainlinkRequestTo(oracle, request, oracleFee);
}
function fulfill(bytes32 _requestId, uint256 _level)
public
recordChainlinkFulfillment(_requestId)
{
playerLevel = _level;
}
I'm deploying from hardhat, with the following parameters (ignoring the ones for VRF since that's working):
Oracle: 0x9C0383DE842A3A0f403b0021F6F85756524d5599
JobId: 0x3766623533366265383635623433333662323766633130313437633139336337
OracleFee: 0.1 * 10**18
The function runs fine, the transaction doesn't revert or anything, but when I check "playerLevel", it's always just 0
Looking at the Etherscan activity, it looks like the node you are using may be inactive. Try this node and jobId:
Oracle = 0xc57B33452b4F7BB189bB5AfaE9cc4aBa1f7a4FD8;
JobId = "d5270d1c311941d0b08bead21fea7747";
These were taken from the Chainlink Official Docs.
To check to see if a node may be inactive or not, check out the oracle address in a block explorer. You can see here that the original node you tried to use hasn't posted a transaction in awhile.
If a node is inactive you will need to find a new one or host one yourself. To find more nodes and jobs, you can check market.link or use the one found in the docs as mentioned earlier.

Example Oraclize files return 0: string: when called in Remix

I want to use Oraclize in Remix, to test it. I'm too stupid to use their examples.
How can I make this work?
From their Github I took the YouTube-Views code and copied it into Remix
pragma solidity >= 0.5.0 < 0.6.0;
import "github.com/oraclize/ethereum-api/oraclizeAPI.sol";
contract YoutubeViews is usingOraclize {
string public viewsCount;
event LogYoutubeViewCount(string views);
event LogNewOraclizeQuery(string description);
constructor()
public
{
update(); // Update views on contract creation...
}
function __callback(
bytes32 _myid,
string memory _result
)
public
{
require(msg.sender == oraclize_cbAddress());
viewsCount = _result;
emit LogYoutubeViewCount(viewsCount);
// Do something with viewsCount, like tipping the author if viewsCount > X?
}
function update()
public
payable
{
emit LogNewOraclizeQuery("Oraclize query was sent, standing by for the answer...");
oraclize_query("URL", 'html(https://www.youtube.com/watch?v=9bZkp7q19f0).xpath(//*[contains(#class, "watch-view-count")]/text())');
}
}
When I use the viewCount it returns:
0: string:
This happens with all the other examples aswell.
With WolframAlpha eg. I also get the following error:
transact to WolframAlpha.update errored: VM error: revert.
revert The transaction has been reverted to the initial state.
Note: The constructor should be payable if you send value. Debug the transaction to get more information.
Ok you don't see the answer like a normal result in Remix:
You have to go under settings and open the Oraclize plug in.
If you then deploy the contract and or click update, you get the result shown in the plug in.