Chainlink - How to pass extra parameters to the Fullfil function - chainlink

I have this piece of code to call an API and return the price of a token, which is working fine:
function requestAPIValue(string memory queryToken) public returns (bytes32 requestId) {
Chainlink.Request memory req = buildChainlinkRequest(
jobId32,
address(this),
this.fulfillValue.selector
);
req.add("get", string.concat(url, queryToken));
req.add("path", path);
req.addInt(addIntParam, multiply);
return sendChainlinkRequest(req, oraclePayment);
}
function fulfillValue(bytes32 _requestId, uint256 _value)
public
recordChainlinkFulfillment(_requestId)
{
emit RequestValue(_requestId, _value);
value = _value;
}
But, in the fulfillValue function I need to store the price for this specific token, something like:
function fulfillValue(bytes32 _requestId, uint256 _value, address token)
public
recordChainlinkFulfillment(_requestId)
{
emit RequestValue(_requestId, _value);
valuesArray[token] = _value;
}
But seems like there is no way to pass extra parameters to the buildChainlinkRequest function:
https://docs.chain.link/docs/any-api/api-reference/#buildchainlinkrequest
I think this should be a very common use case, any idea how can I do that?
Thanks

Create a mapping of request IDs to token addresses, for example
mapping(bytes32 => address) tokens;
Populate it inside requestAPIValue function
function requestAPIValue(string memory queryToken, address token) public returns (bytes32 requestId) {
.
.
.
requestId = sendChainlinkRequest(req, oraclePayment);
tokens[requestId] = token;
}
Finally, grab it from the mapping inside the fulfillValue function since it has requestId as parameter
function fulfillValue(bytes32 _requestId, uint256 _value)
public
recordChainlinkFulfillment(_requestId)
{
address token = tokens[_requestId];
valuesArray[token] = _value;
}

Related

Function call work fine in Remix but wont in JS

I've been experiencing trouble to link my chainlink VFR mapping to my Javascript.
I've mapped the result of the VRF to the address of the caller so that the result depends on the caller.
Here is the solidity code:
mapping(address => bytes32) private addressToId;
mapping(bytes32 => uint256) private IdToRandom;
function getRandomNumber() public returns (bytes32 requestId) {
require(LINK.balanceOf(address(this)) >= fee, "Not enough LINK - fill contract with faucet");
requestId = requestRandomness(keyHash, fee);
addressToId[msg.sender] = requestId;
}
function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
IdToRandom[requestId] = randomness;
getResult();
}
function getResult() public view returns (uint randomnombre) {
randomnombre = IdToRandom[addressToId[msg.sender]];
}
When I call getResult() in a solidity function to determine if the address won or not, it works fine on remix but won't work on JS.
Here is my JS call:
contract.methods.getResult().call().then(function(bal) { console.log(bal) })
It sends me back 0 and I don't know how to handle it...
I think you are running into an issue where the getRandomNumber() has been called and that transaction is complete but the callback to fulfillRandomness hasn't occurred yet.
You will need to listen for the event before calling getResult()
One of the methods below should accomplish this
web3:
https://web3js.readthedocs.io/en/v1.2.11/web3-eth-contract.html#contract-events
ethers:
https://docs.ethers.io/v5/api/providers/provider/#Provider--event-methods

Cannot call method - mocha

I am running some test in my first smart contract called Inbox.sol.
I want to call the method .message in order to see if it is equal to the default variable I use when I deploy the contract with web3.
My Inbox.sol code
pragma solidity >=0.6.12;
contract Inbox {
string public message;
function initialInbox(string memory initialMessage) public {
message = initialMessage;
}
function setMessage(string memory newMessage) public {
message = newMessage;
}
}
My test file with Mocha is:
let accounts
let inbox
beforeEach(async () => {
// Get a list of all accounts
accounts = await web3.eth.getAccounts()
// Use one of those account to deploy the contract
inbox = await new web3.eth.Contract(abi)
.deploy({ data: bytecode, arguments: [INITIAL_ARGUMENT]})
.send({ from: accounts[0], gas: GAS})
})
describe(('Inbox'), () => {
it("has a default message", async () => {
const message = await inbox.methods.message() // Returns a big object
console.log(message)
const messageCalled = await inbox.methods.message().call()
console.log(messageCalled) // Returns nothing
assert.strictEqual(message, INITIAL_ARGUMENT)
})
})
Your JS inbox variable is an instance of web3.eth.Contract (docs).
The inbox.methods.message() is an instance of a helper object (that's the "Returns a big object" in your comment) containing the .call() method, that you need to use when you want to perform a read-only call.
const message = await inbox.methods.message().call() // Returns the string
Docs: https://web3js.readthedocs.io/en/v1.3.4/web3-eth-contract.html#methods-mymethod-call
Change your code and add a contractor.
function constructor(string memory initialMessage) public {
message = initialMessage;
}
if you are using an older version of solidity for example ^0.4.17. you can create a constructor by create a method same name as your contract class.
function Inbox(string memory initialMessage) public {
message = initialMessage;
}

I want to post ArrayList, but return null value

POSTMAN :
{
"childDTO":[{
"age":"80",
"gender":"kavi",
"occupation":"main",
"type":"mainlife"
},
{ "age":"80",
"gender":"kavi",
"occupation":"main",
"type":"mainlife"
}
]
}
Controller.....
#PostMapping("/child")
public List<CustomerDTO> childDTO(#RequestBody CustomerDTO cus){
return calculationService.childDTO(cus.getAge(),cus.getGender(),cus.getOccupation(),cus.getType());
}
Service......
public List<CustomerDTO> childDTO(String age, String gender, String occupation, String type);
Service Impl......
#Override
public List<CustomerDTO> childDTO(String age, String gender, String occupation, String type) {
List<CustomerDTO> typeChild = new ArrayList<>();
if (type==children) {
for (CustomerDTO customer1 : typeChild) {
customer1.setAge(age);
customer1.setGender(gender);
customer1.setOccupation(occupation);
customer1.setType(type);
customer1.setBenifits(benifitDTO(beni.getRiders(), beni.getSumAssuarance()));
System.out.println("list:-"+customer1);
typeChild.add(customer1);
}
}
System.out.println("list:-"+typeChild);
return typeChild;
}
You did not post what variable children is, probably some class field, but you cannot compare Java Strings with ==. Java Strings must be compared with .equals:
...
if (type.equals(children)) {
...
Use a debugger next time, to see what's happening in your code.
Your request and controller RequestBody structure do not match.
You need to use List.
#PostMapping("/child")
public List<CustomerDTO> childDTO(#RequestBody List<CustomerDTO> cusList){ // here list needs to be used
if(cusList != null && !cusList.isEmpty()) {
CustomerDTO cus = cusList.get(0); // it will get first element from the list, if you want to process all the elements then you need to iterate the list
return calculationService.childDTO(cus.getAge(),cus.getGender(),cus.getOccupation(),cus.getType());
}
}
Now other logic will be as it or you may change it as per your requirement.

How can I pass list of complex objects to webapi from breezejs?

I found that using [fromapi] attribute I can pass one complex object.
when I try to pass list of complex objects it doesn't work.
in the client side I use breeze. server side is webapi.
How can I do this?
You can create one DTO which has property for your list of objects
public class CreateUserDto
{
public string Name {set;get;}
public List<RoleDto> Roles {set;get;}
public CreateUserDto()
{
this.Roles = new List<RoleDto>();
}
}
public class RoleDto
{
public int Id {set;get;}
public string Name {set;get;}
}
And you can use that as the argument of your Web api endpoint
public HttpResponseMesssage Save(CreateUserDto model)
{
//Check model.Roles now
// to do : Return a response
}
From client, you can send data like this.(Assuming you have jQuery library loaded to your page)
var data { Name : "TestName",Roles:[]}
data.Roles.push(new { Id:1,Name:"Admin"});
data.Roles.push(new { Id:2,Name:"Editor"});
$.post("YourEndpointHere",data,function(response){
// do something with response
});
Modelbinding will take care of converting the posted form data to an instance of CreateUserDto in your Save method. You can access model.Roles property to get the list of complex objects you wanted.
you can use Dictionary as below:
[HttpPost]
public IQueryable<Product> GetProducts(Dictionary<string, object> data)
{
var categoryId = Convert.ToInt32(data["categoryId"]);
var category = _context.Categories.Single(a => a.ID == categoryId);
var galleryId = Convert.ToInt32(data["galleryId"]);
var langId = Convert.ToInt32(data["langId"]);
var searchStr = data["str"];
return category.Products.Where(a => a.GalleryID == galleryId, a.LanguageID == langId, a.Description.Contains(searchStr))
}

ServiceStack caching strategy

I'm learning ServiceStack and have a question about how to use the [Route] tag with caching. Here's my code:
[Route("/applicationusers")]
[Route("/applicationusers/{Id}")]
public class ApplicationUsers : IReturn<ApplicationUserResponse>
{
public int Id { get; set; }
}
public object Get(ApplicationUsers request)
{
//var cacheKey = UrnId.Create<ApplicationUsers>("users");
//return RequestContext.ToOptimizedResultUsingCache(base.Cache, cacheKey, () =>
return new ApplicationUserResponse
{
ApplicationUsers = (request.Id == 0)
? Db.Select<ApplicationUser>()
: Db.Select<ApplicationUser>("Id = {0}", request.Id)
};
}
What I want is for the "ApplicationUsers" collection to be cached, and the times when I pass in an Id, for it to use the main cached collection to get the individual object out.
If I uncomment the code above, the main collection is cached under the "users" key, but any specific query I submit hits the Db again. Am I just thinking about the cache wrong?
Thanks in advance,
Mike
this line
var cacheKey = UrnId.Create<ApplicationUsers>("users");
is creating the same cache key for all the requests, you must use some of the request parameters to make a "unique key" for each different response.
var cacheKey = UrnId.Create<ApplicationUsers>(request.Id.ToString());
this will give you the "urn:ApplicationUsers:0" key for the get all and the "urn:ApplicationUsers:9" for the request with Id = 9
now you can use the extension method in this way.
return RequestContext.ToOptimizedResultUsingCache(Cache, cacheKey, () => {
if(request.Id == 0) return GetAll();
else return GetOne(request.Id);
});
I hope this helps, regards.

Resources