Function call work fine in Remix but wont in JS - chainlink

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

Related

Chainlink - How to pass extra parameters to the Fullfil function

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;
}

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;
}

Closing an async Response

I'm trying to close the current response but nothing happens when I try HttpContext.Response.Body.Close() and Response.End() does not exist.
The reason I'm trying to achieve this is because of legacy validator functions that write an error and close the response, or at least stopping the parent WebAPI method.
Example:
private async Task Register_v2()
{
//Read JSON to object
UserRegisterRequest userRegisterRequest = Request.ReadBody().FromJson<UserRegisterRequest>();
//Validate object (legacy static method with a lot of logic)
//Validate() should end the response if object not validated
userRegisterRequest.Validate(isJson: true, isThrowHttpError: true);
//Code still reaches here and request does not close
string test = "hey I'm alive";
}
Can I workaround this with middleware somehow?
Thanks
There are two ways to terminate the Request pipeline.
Use app.Run in Startup.Configure
Do not invoke _next(context) in Middleware.InvokeAsync
For your scenario, you could try second option by determining whether to invoke _next(context).
public class FirstMiddleware
{
private readonly RequestDelegate _next;
public FirstMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
await context.Response.WriteAsync($"This is { GetType().Name }");
//decide whether to invoke line below based on your business logic
//await _next(context);
bool isValid = userRegisterRequest.Validate(isJson: true, isThrowHttpError: true);
//change userRegisterRequest.Validate to reutrn whether the model is valid
if(!isValid)
{
await context.Response.WriteAsync($"Model is not valid");
}
else
{
await _next(context);
}
}
}

Update data with Microsoft.AspNet.WebApi

Hey i am having a big trouble updating data in my client side REST application.
I made a Web API controller.
// PUT: api/Contacts/5
[ResponseType(typeof(void))]
public IHttpActionResult PutContact(Contact contact, int id)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != contact.ContactId)
{
return BadRequest();
}
_contactService.Update(contact);
return StatusCode(HttpStatusCode.NoContent);
}
And also client side service method:
public async Task<T> PutData<T>(T data, int dataId)
{
HttpResponseMessage resp = await this._client.PutAsJsonAsync(_serviceUrl + "/" + dataId, data);
resp.EnsureSuccessStatusCode();
return await resp.Content.ReadAsAsync<T>();
}
Service URL shows in debug mode that i goes to endpoint:
http://localhost:21855/api/Contacts/8
But it does not even go to breakpoint when i debug my server controller PutContact method.
What i am doint wrong? I need to update the data but i cant, because my client-side application won't even go to servers breakpoint on debug mode!!!
It gives me an error response 405 : Method not allowed
You can't have two different body parameters in the same method.
What you need to do is to set the id parameter to come from the URI and the Contact parameter from the body, like this:
public IHttpActionResult PutContact([FromBody]Contact contact, [FromUri]int id)
{
// method code
}
BTW, I suppose you have a GET method in your controller which looks like this:
public IHttpActionResult GetContact(int id)
{
// method code
return Contact; // pseudo-code
}
The error you getting comes from the fact that the system is not really calling your PUT method but the GET one (the system is ignoring the Contact parameter for the reason I expressed before): calling a GET method with a PUT verb results in a 405 Method Not Allowed exception.

Scoping of callback functions which modify instance variables in Dart

While questions of this sort have been frequently asked, I think I have a more specific constraint that makes the problem a little more interesting. I am writing a client-side application in Dart using an MVC pattern. My goal is simple: listen for clicks on a button, trigger an async request to a back-end API, and present that data to the user.
Minimally, I have one each of a model, view, and controller class. The model class implements methods to make requests and bundle up the data it receives. The view class has the DOM subtree of interest as a field and implements methods to manipulate the elements therein. The controller has a single instance each of the model and view classes as its fields and registers event handlers on the elements of the view. The controller's event handlers fire off calls to the model to make requests and return data, which will then be passed to the view for rendering.
The issue arises when I attempt to capture the incoming data from the async request into an instance variable of the model. I'd like to keep everything nicely encapsulated (that's why I'm using Dart in the first place), and I'd like to avoid using a global variable to hold the data that comes from the async request. A minimal example of my current layout looks something like below. I've made all of the fields and methods public here for clarity's sake.
// view.dart
class FooView {
// The root element of the view with which we're concerned.
static final Element root = querySelector('#thisView');
FooView() { init(); }
void init() { root.hidden = false; }
// Appends the new data into an unordered list.
void update(List<Map<String,String>> list) {
UListElement container = root.querySelector('ul#dataContainer');
container
..hidden = true
..children.clear();
for ( Map<String,String> item in list ) {
container.append(new LIElement()
..id = item['id']
..text = item['text']
);
container.hidden = false;
}
// model.dart
class FooModel {
// Instance variable to hold processed data from the async request.
List<Map<String,String>> dataList;
// Makes async request, returning data to caller.
List<Map<String,String>> getData() {
HttpRequest
.getString('example.com/api/endpoint')
.then( (String data) {
dataList = JSON.decode(data);
});
return dataList;
}
}
// controller.dart
class FooController {
FooModel model;
FooView view;
FooController() {
model = new FooModel;
view = new FooView;
}
void registerHandlers() {
// When this button is clicked, the view is updated with data from the model.
ButtonElement myButton = view.root.querySelector('#myButton');
myButton.onClick.listen( (Event e) {
view.update(model.getData());
});
}
}
The errors I'm seeing involve the model.dataList field coming up null at the end of all of this. My first blush is that I do not understand scoping of callback functions. The way I first understood it, the callback would handle the request's data when it arrived and just set the instance variable when it was ready. Perhaps the instance variable is aliased and modified within the scope of the callback, but the variable I want to return is never touched.
I have thought about passing a Future object to a method of the view, which will then just do the processing itself and add the elements to the DOM as a side effect. That technique would break my MVC design (even more than it's broken now in this minimal working example).
It is also very possible that I am using asynchronous programming completely incorrectly. Thinking more on this, my async call is useless because I basically make a blocking call to view.update() in the controller when the event fires. Maybe I should pass a request Future to the controller, and fire the request's then() method from there when the event handler is triggered.
In Dart, in what scope do callback functions reside, and how can I get data out of them with minimal side effects and maximal encapsulation?
N.B. I hate to belabor this oft-discussed question, but I have read previous answers to similar questions to no avail.
The getData method initiates the asynchronous HTTP request then immediately returns before having received/parsed the response. That is why model.datalist is null.
To make this work with minimal effort, you can make getData synchronous:
(note: I changed the dataList type, just to make it work with the sample JSON service http://ip.jsontest.com/)
// model.dart
class FooModel {
// Instance variable to hold processed data from the async request.
Map<String, String> dataList;
// Makes async request, returning data to caller.
Map<String, String> getData() {
var request = new HttpRequest()
..open('GET', 'http://ip.jsontest.com/', async: false)
..send();
dataList = JSON.decode(request.responseText);
return dataList;
}
}
Though this may violate your objective, I agree with your concerns re: blocking call and would personally consider keeping the HTTP request asynchronous and making getData return a new future that references your model class or parsed data. Something like:
// model.dart
class FooModel {
// Instance variable to hold processed data from the async request.
Map<String,String> dataList;
// Makes async request, returning data to caller.
Future<Map<String, String>> getData() {
return HttpRequest
.getString('http://ip.jsontest.com/')
.then( (String data) {
dataList = JSON.decode(data);
return dataList;
});
}
}
and in the controller:
void registerHandlers() {
// When this button is clicked, the view is updated with data from the model.
ButtonElement myButton = FooView.root.querySelector('#myButton');
myButton.onClick.listen( (Event e) {
model.getData().then((Map<String, String> dataList) {
view.update(dataList);
});
});
}
You return datalist in getData before the HttpRequest has returned.
// Makes async request, returning data to caller.
List<Map<String,String>> getData() {
return HttpRequest // <== modified
.getString('example.com/api/endpoint')
.then( (String data) {
return JSON.decode(data); // <== modified
});
// return dataList; // <== modified
void registerHandlers() {
// When this button is clicked, the view is updated with data from the model.
ButtonElement myButton = view.root.querySelector('#myButton');
myButton.onClick.listen( (Event e) {
model.getData().then((data) => view.update(data)); // <== modified
});
}
You can use Stream to make your design loosely coupled and asynchronous:
class ModelChange {...}
class ViewChange {...}
abstract class Bindable<EventType> {
Stream<EventType> get updateNotification;
Stream<EventType> controllerEvents;
}
class Model implements Bindable<ModelChange> {
Stream<ModelChange> controllerEvents;
Stream<ModelChange> get updateNotification => ...
}
class View implements Bindable<ViewChange> {
Stream<ViewChange> controllerEvents;
Stream<ViewChange> get updateNotification => ...
}
class Controller {
final StreamController<ViewChange> viewChange = new StreamController();
final StreamController<ModelChange> modelChange = new StreamController();
Controller.bind(Bindable model, Bindable view) {
view.controllerEvents = viewChange.stream;
model.controllerEvents = modelChange.stream;
view.updateNotification.forEach((ViewChange vs) {
modelChange.add(onViewChange(vs));
});
model.updateNotification.forEach((ModelChange mc) {
viewChange.add(onModelChange(mc));
});
}
ModelChange onViewChange(ViewChange vc) => ...
ViewChange onModelChange(ModelChange mc) => ...
}

Resources