Chainlink API Get Request isn't returning a value - chainlink

I am creating the following Smart contract. It makes a Chainlink request which isn't returning a value from the API. I am using the following [jobID][1] and this [node][2], but the node doesn't start the job and I don't know why. I checked that the node has a balance of LINK tokens but it doesn't have any and I don't know how to send a balance to the contract node.
I am using Kovan Testnet to try the smart contract. Could you tell me something which I can try?
// This example code is designed to quickly deploy an example contract using Remix.
pragma solidity ^0.6.0;
import "#chainlink/contracts/src/v0.6/ChainlinkClient.sol";
contract APIConsumer is ChainlinkClient {
uint256 public volume;
address private oracle;
bytes32 private jobId;
uint256 private fee;
/**
* Network: Kovan
* Chainlink - 0x2f90A6D021db21e1B2A077c5a37B3C7E75D15b7e
* Chainlink - 29fa9aa13bf1468788b7cc4a500a45b8
* Fee: 0.1 LINK
*/
constructor() public {
setPublicChainlinkToken();
oracle = 0x56dd6586DB0D08c6Ce7B2f2805af28616E082455;
jobId = "b6602d14e4734c49a5e1ce19d45a4632";
fee = 0.1 * 10 ** 18; // 0.1 LINK
}
/**
* Create a Chainlink request to retrieve API response, find the target
* data, then multiply by 1000000000000000000 (to remove decimal places from data).
************************************************************************************
* STOP! *
* THIS FUNCTION WILL FAIL IF THIS CONTRACT DOES NOT OWN LINK *
* ---------------------------------------------------------- *
* Learn how to obtain testnet LINK and fund this contract: *
* ------- https://docs.chain.link/docs/acquire-link -------- *
* ---- https://docs.chain.link/docs/fund-your-contract ----- *
* *
************************************************************************************/
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");
//request.add("path", "data.0.Myvalue");
// Multiply the result by 1000000000000000000 to remove decimals
// 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;
}
/**
* Withdraw LINK from this contract
*
* NOTE: DO NOT USE THIS IN PRODUCTION AS IT CAN BE CALLED BY ANY ADDRESS.
* THIS IS PURELY FOR EXAMPLE PURPOSES ONLY.
*/
function withdrawLink() external {
LinkTokenInterface linkToken = LinkTokenInterface(chainlinkTokenAddress());
require(linkToken.transfer(msg.sender, linkToken.balanceOf(address(this))), "Unable to transfer");
}
}
``
[1]: https://market.link/jobs/0609deab-6d61-4937-85e4-a8e810b8b272/runs
[2]: https://market.link/nodes/323602b9-3831-4f8d-a66b-3fb7531649eb/metrics?start=1631783169&end=1632387969

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 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 do i change the price feed token from eth /usd to busd/usd with the chainlink oracle?

I am trying to get the price feed for BUSD/USD using the chainlink pricefeed oracle but it tells me that the code was reverted. this is also happening when i switch to any other token, i am using the kovan test net
this is the error i get
call to PriceConsumerV3.getLatestPrice
call to PriceConsumerV3.getLatestPrice errored: VM execution error.
Reverted 0x
and this is my code
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.7;
import "#chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol";
contract PriceConsumerV3 {
AggregatorV3Interface internal priceFeed;
/**
* Network: Kovan
* Aggregator: ETH/USD
* Address: 0x9326BFA02ADD2366b30bacB125260Af641031331
*/
constructor() public {
priceFeed = AggregatorV3Interface(0xcBb98864Ef56E9042e7d2efef76141f15731B82f);
}
/**
* Returns the latest price
*/
function getLatestPrice() public view returns (int) {
(
uint80 roundID,
int price,
uint startedAt,
uint timeStamp,
uint80 answeredInRound
) = priceFeed.latestRoundData();
return price;
}
}
You have set incorrect ChainLink address!
correct ETH/USD address: 0x9326BFA02ADD2366b30bacB125260Af641031331
Address you are using in on BSC network, not Kovan network!
If you are trying to get BUSD/USD via a price feed on Kovan testnet, it is not available on that specific network. It is available on Ethereum mainnet however.
If you are testing using the price feed, you can always use a mock contract to mock the behavior of the feeds for testing purposes only.

Dialogflow CX | How to close/reset a conversation

How can I close or reset a conversation programmatically from Java app?. According to Dialogflow CX documentation "A session remains active and its data is stored for 30 minutes after the last request is sent for the session."
I want to keep the session active for less time. For example, if I want the session to be active for 5 minutes, when user sends a message 5 minutes or more after last message, conversation must start again and previous flows must be closed and context parameters must be deleted.
With Dialogflow ES it is posible using ContextsClient, however new version does not offer ContextsClient class.
Dialogflow CX uses State Handlers to control conversation paths, unlike Dialogflow ES which uses Contexts.
For Dialogflow CX, you can end the current session by using the END_SESSION symbolic transition target. Once the END_SESSION transition target is invoked, it clears the current session and the next user input will restart the session at the start page of the Default Start Flow.
To achieve your desired use case, you’ll have to create your own implementation for it. Note that the solution below will only work if you integrate your Dialogflow CX agent to a custom front-end.
First, you should add an Event Handler to all of your Pages - so that the Event Handler will be accessible in any part of the conversation flow. In this Event Handler, define a custom event - for example: clearsession. Then, set its Transition to End Session Page. Once the clearsession event is invoked, it will end the current session.
Then, using your own business logic, you can create a custom function that could act as a timer for each user query. Once the timer reaches 5 minutes, your custom application should send a detectIntent request to your CX agent programmatically. This detectIntent request must contain the current session ID and the custom event (from the previously created Event Handler).
Here’s a sample detectIntent request that invokes a custom event using the Java Client Library:
// [START dialogflow_cx_detect_intent_event]
import com.google.api.gax.rpc.ApiException;
import com.google.cloud.dialogflow.cx.v3.*;
import com.google.common.collect.Maps;
import java.io.IOException;
import java.util.List;
import java.util.Map;
public class DetectIntent {
// DialogFlow API Detect Intent sample with event input.
public static Map<String, QueryResult> detectIntentEvent(
String projectId,
String locationId,
String agentId,
String sessionId,
String languageCode,
String event)
throws IOException, ApiException {
SessionsSettings.Builder sessionsSettingsBuilder = SessionsSettings.newBuilder();
if (locationId.equals("global")) {
sessionsSettingsBuilder.setEndpoint("dialogflow.googleapis.com:443");
} else {
sessionsSettingsBuilder.setEndpoint(locationId + "-dialogflow.googleapis.com:443");
}
SessionsSettings sessionsSettings = sessionsSettingsBuilder.build();
Map<String, QueryResult> queryResults = Maps.newHashMap();
// Instantiates a client
try (SessionsClient sessionsClient = SessionsClient.create(sessionsSettings)) {
// Set the session name using the projectID (my-project-id), locationID (global), agentID
// (UUID), and sessionId (UUID).
SessionName session = SessionName.of(projectId, locationId, agentId, sessionId);
System.out.println("Session Path: " + session.toString());
EventInput.Builder eventInput = EventInput.newBuilder().setEvent(event);
// Build the query with the EventInput and language code (en-US).
QueryInput queryInput =
QueryInput.newBuilder().setEvent(eventInput).setLanguageCode(languageCode).build();
// Build the DetectIntentRequest with the SessionName and QueryInput.
DetectIntentRequest request =
DetectIntentRequest.newBuilder()
.setSession(session.toString())
.setQueryInput(queryInput)
.build();
// Performs the detect intent request.
DetectIntentResponse response = sessionsClient.detectIntent(request);
// Display the query result.
QueryResult queryResult = response.getQueryResult();
System.out.println("====================");
System.out.format(
"Detected Intent: %s (confidence: %f)\n",
queryResult.getIntent().getDisplayName(), queryResult.getIntentDetectionConfidence());
}
return queryResults;
}
public static void main(String[] args) {
String projectId = "<project-id>";
String locationId = "<location-id>";
String agentId = "<agent-id>";
String sessionId = "<current-session-id>";
String languageCode = "<language-code>";
String event = "clearsession";
try{
detectIntentEvent(projectId,locationId,agentId,sessionId, languageCode, event);
} catch (IOException e){
System.out.println(e.getMessage());
}
}
}
// [END dialogflow_cx_detect_intent_event]

My chainlink request isn't getting fulfilled?

Can someone help me investigate why my Chainlink requests aren't getting fulfilled. They get fulfilled in my tests (see hardhat test etherscan events(https://kovan.etherscan.io/address/0x8Ae71A5a6c73dc87e0B9Da426c1b3B145a6F0d12#events). But they don't get fulfilled when I make them from my react app (see react app contract's etherscan events https://kovan.etherscan.io/address/0x6da2256a13fd36a884eb14185e756e89ffa695f8#events).
Same contracts (different addresses), same function call.
Updates:
Here's the code I use to call them in my tests
const tx = await baseAgreement.connect(user).withdraw(
jobId,
oracleFee
);
Here's the code I use to call them in my UI
const signer = provider.getSigner();
const tx = await baseAgreement.connect(signer).withdraw(jobId, oracleFee);
Here's my Solidity Chainlink functions
function withdraw(
bytes32 _jobId,
uint256 _oracleFee
)
external
onlyContractActive()
returns(bytes32 requestId)
{
// check Link in this contract to see if we need to request more
checkLINK(_oracleFee);
// Build request
Chainlink.Request memory req = buildChainlinkRequest(_jobId, address(this), this.fulfillWithdraw.selector);
bytes memory url_bytes = abi.encodePacked(BASE_URL, mediaLink, API_KEY);
req.add("get", string(url_bytes));
req.add("path", "items.0.statistics.viewCount");
return sendChainlinkRequestTo(chainlinkOracleAddress(), req, _oracleFee);
}
/**
* #dev Callback for chainlink, this function pays the user
*/
function fulfillWithdraw(
bytes32 _requestId,
bytes32 _response
)
external
recordChainlinkFulfillment(_requestId)
{
// Convert api string response to an int
string memory _responseString = bytes32ToString(_response);
uint256 response = uint256(parseInt(_responseString, 0));
emit IntResponse(response);
// Pay the user
payUser(response);
}
function payUser(
uint256 _number
)
internal
{
// Calculate pay
uint256 budgetRemaining = getAgreementBalance();
uint256 accumulatedPay = budget - budgetRemaining;
uint256 pay = (payPerNumber * _number) - accumulatedPay;
if (pay > budgetRemaining) {
pay = budgetRemaining;
}
// Calculate platform fee
uint256 totalPlatformFee = (pay * PLATFORM_FEE) / 100;
// Transfer funds
paySomeone(payable(address(this)), user, pay-totalPlatformFee);
paySomeone(payable(address(this)), platformAddress, totalPlatformFee);
}
Full contract code can be viewed here: https://github.com/colinsteidtmann/dapplu-contracts/blob/main/contracts/BaseAgreement.sol
Update 2:
I figured out that my UI was deploying my contracts using a factory contract and a clones pattern (based on EIP 1167 standard and OpenZepplin's clones https://docs.openzeppelin.com/contracts/4.x/api/proxy#Clones ). But, my hardhat tests were deploying my contracts without the factory. Once I made my hardhat tests deploy the contracts using the factory contract, then they stopped working. So, does chainlink not work with Proxy contracts and the EIP 1167 standard?
Remove your agreement vars in MinimalClone.sol, and either have the user input them as args in your init() method or hardcode them into the request like this:
Chainlink.Request memory req = buildChainlinkRequest(_jobId, address(this), this.fulfillWithdraw.selector);
req.add("get", "https://youtube.googleapis.com/youtube/v3/videos?part=statistics&id=aaaaaakey=aaaaa");
The reason it wasn't working is that proxy contracts do not inherit the state of the implementation contracts, just the logic through the delegatecall() method. Thus, your proxy clones were reading essentially blank values when replacing those variables in the request.
Reference: Here is a good article on how proxies and delegate call works.

AutoReconnect with multiple server uri's

Consider the scenario where I have below code.
MqttConnectOptions connOpt = new MqttConnectOptions();
connOpt.setServerURIs(new String[]{"tcp://localhost:1883", "tcp://some-other-host:1883"});
connOpt.setAutomaticReconnect(true);
client.setCallback( new TemperatureSubscriber() );
client.connect(connOpt);
So when I say connect, it connects to localhost.
Then I get connection lost, due to say any reason. So at this point of time, since automaticReconnect is true, will it connect to localhost or some-other-host ?
Let me show how to find such answers yourself -
First you visit the Github repository for Paho source code.
Then you enter setAutomaticReconnect into the search field:
This is of course just the public name. You need to spot the corresponding private member.
In the MqttConnectOptions.java with the very simple code you find that member:
private boolean automaticReconnect = false;
Then you perform another search, this time for the automaticReconnect word:
And that leads you to the hint in the MqttAsyncClient.java file -
// Insert our own callback to iterate through the URIs till the connect
// succeeds
MqttToken userToken = new MqttToken(getClientId());
ConnectActionListener connectActionListener = new ConnectActionListener(this, persistence, comms, options,
userToken, userContext, callback, reconnecting);
userToken.setActionCallback(connectActionListener);
userToken.setUserContext(this);
Finally, in the ConnectActionListener.java file you can confirm that the URLs are being tried one after each other:
/**
* The connect failed, so try the next URI on the list.
* If there are no more URIs, then fail the overall connect.
*
* #param token the {#link IMqttToken} from the failed connection attempt
* #param exception the {#link Throwable} exception from the failed connection attempt
*/
public void onFailure(IMqttToken token, Throwable exception) {
int numberOfURIs = comms.getNetworkModules().length;
int index = comms.getNetworkModuleIndex();
...
...
comms.setNetworkModuleIndex(index + 1);

Chrome Sender: media.playerState is not properly updated

I'm working on Chrome Sender application now and noticed that media.playerState was not properly updated to chrome.cast.media.PlayerState.PLAYING when play request is successfully executed. I understand that it's possible to subscribe to media state update via media.addUpdateListener(onMediaUpdate); but it's not very convenient. Are there any plans to make API more consistent?
/**
* #param {chrome.cast.media.Media} media
* #return {Promise.defer}
*/
function playMedia(media) {
let playDefer = Promise.defer();
let playRequest = new chrome.cast.media.PlayRequest();
media.play(playRequest, onPlaySuccess.bind(null, playDefer, media), onPlayError);
return playDefer;
}
/**
* #param {Promise.defer} defer
* #param {chrome.cast.media.Media} media
*/
function onPlaySuccess(defer, media) {
// Actually `media.playerState` is still chrome.cast.media.PlayerState.PAUSED
// Is it expected behaviour?
console.log('play request success', media.playerState === chrome.cast.media.PlayerState.PLAYING);
defer.resolve(media);
}
/**
* #param {chrome.cast.Error} error
*/
function onPlayError(error) {
console.log(error.code, error.description);
}

Resources