Web3js BatchRequest Balances from List of Tokens Fails on execute() - ethereum

This is my batch.js file which contains the relevant code. I am importing some variables from other files but I've isolated the problem to this file:
const Web3 = require('web3')
const fs = require('fs')
const { convertToNumber, getTokens } = require('./utils')
const { abi, bathEndpoint, walletAddress, blockNumber } = require('./constant.js')
const web3 = new Web3(new Web3.providers.HttpProvider(bathEndpoint))
const generateContractFunctionList = ({ tokens, blockNumber }) => {
const batch = new web3.BatchRequest()
tokens.map(async ({ address: tokenAddress, symbol, decimals }) => {
// console.log('tokenAddress :>> ', tokenAddress)
if (tokenAddress != null && tokenAddress != '') {
const contract = new web3.eth.Contract(abi)
contract.options.address = tokenAddress
try {
batch.add(
contract.methods
.balanceOf(walletAddress)
.call.request({}, blockNumber)
)
} catch (error) {
console.error('Error adding request to batch for token ', tokenAddress)
}
}
})
return batch
}
const main = async () => {
//const { tokens } = await getTokens()
const tokens = JSON.parse(fs.readFileSync('./tokenArrayFormatted1.json'));
console.log('tokens retrieved :>> ', tokens.length)
// const batch = generateContractFunctionList({ tokens })
// query block number
const batch = generateContractFunctionList({ tokens, blockNumber: blockNumber })
const tokenBalances = {}
const tokensIgnored = []
let batchData
try {
batchData = await batch.execute()
} catch (error) {
console.error('Error retrieving balances for some tokens')
batchData = error
}
try {
batchData.response.forEach((res, index) => {
const { name, decimals, symbol } = tokens[index]
if (res && res._hex) {
tokenBalances[name] = `${convertToNumber(res._hex, decimals)} ${symbol}`
} else {
tokensIgnored.push(name)
}
})
} catch (error) {
console.error('Error retrieving balances for some tokens')
batchData = error
}
console.log(
'The following tokens returned an error when checking balance:',
tokensIgnored
)
console.log('----------')
console.log(
`Balance checked for ${Object.keys(tokenBalances).length} tokens:`
)
console.log(tokenBalances)
}
main()
tokenArrayFormatted1.json looks like this:
[
{
"chainId": 1,
"address": "0xf3AE5d769e153Ef72b4e3591aC004E89F48107a1",
"name": "Deeper Network",
"symbol": "DPR",
"decimals": 18
},
{
"chainId": 1,
"address": "0xf680429328caaaCabee69b7A9FdB21a71419c063",
"name": "Butterfly Protocol Governance Token",
"symbol": "BFLY",
"decimals": 18
}
]
When I run node batch.js I keep getting an error telling me that batchData.response is undefined when the code tries to do a forEach over it. I logged batch to the console and it looked like this.
Batch {
requestManager: RequestManager {
provider: HttpProvider {
withCredentials: false,
timeout: 0,
headers: undefined,
agent: undefined,
connected: false,
host: 'https://USERNAME:PASSWORD#BASEURLOFGETHNODE.com',
httpsAgent: [Agent]
},
providers: {
WebsocketProvider: [Function: WebsocketProvider],
HttpProvider: [Function: HttpProvider],
IpcProvider: [Function: IpcProvider]
},
subscriptions: Map(0) {}
},
requests: [
{
params: [Array],
callback: undefined,
method: 'eth_call',
format: [Function: bound ]
},
{
params: [Array],
callback: undefined,
method: 'eth_call',
format: [Function: bound ]
}
]
}
Where USERNAME,PASSWORD, and BASEURLOFGETHNODE refer to my actual credentials.
And then batchData which is created by the line let batchData = await batch.execute() is undefined when logged to the console. So clearly await batch.execute() is producing nothing. I am using Chainstack Geth node API's (with an archive node) and web3js as indicated above. What seems to be the problem?

let batchData = await batch.execute()
batch.execute() does not return a promise if you read the source code, in fact it sends the JSON RPC batch request and call each request callback individually, so what you need to do is turn the execute function into asynchronous one returning an array of responses, and this is how to do it :
const Jsonrpc = require('web3-core-requestmanager/src/jsonrpc');
var { errors } = require('web3-core-helpers');
function executeAsync(batch) {
return new Promise((resolve, reject) => {
var requests = batch.requests;
batch.requestManager.sendBatch(requests, (err, results) => {
results = results || [];
var response = requests.map((request, index) => {
return results[index] || {};
}).map((result, index) => {
if (result && result.error) {
return errors.ErrorResponse(result);
}
if (!Jsonrpc.isValidResponse(result)) {
return errors.InvalidResponse(result);
}
return requests[index].format ? requests[index].format(result.result) : result.result;
});
resolve(response);
});
})
}
To use this function :
(async () => {
var batch = new web3.BatchRequest();
batch.add(web3.eth.getBlock.request("latest"));
var batchResponse = await executeAsync(batch);
console.log(batchResponse);
})()

Related

node js stops working on multiple api request from angular and working after restarting the node app

i am developing an app with node express js and angular js. My angular app makes an api request from node js app server on each route or button click, also a single component or button click may request multiple api to node js app. upon requesting multiple time the data loading is just got stopped and i am not getting result. Also getting status code like 304 and 204.
please check out my api code and subscribe service code.
constroller.js ///express js
getList: async (req, res) => {
try{
const result = await getList(); //from service.js (an sql query)
var serviceCalls = result[0][0];
return res.set({'Content-Type': 'application/json'}).status(200).json({
success: 1,
message: 'Successfully Data Fetched',
data: serviceCalls
});
} catch(e){
return res.json({
success: 0,
message: 'No Data Fetched' + ' ' + e.message,
data: {}
});
}
},
getDetails: async (req, res) => {
try{
const id = req.query.id
const result = await getDetails(id); //from service.js (an sql query)
var serviceCalls = result[0][0];
return res.set({'Content-Type': 'application/json'}).status(200).json({
success: 1,
message: 'Successfully Data Fetched',
data: serviceCalls
});
} catch(e){
return res.json({
success: 0,
message: {text:'No Data Fetched ', errMsg: e.message},
data: {}
});
}
},
getTroubles: async (req, res) => {
try{
const id = req.query.id
const result = await getTroubles(id); //from service.js (an sql query)
var complaintData = result[0][0];
return res.set({'Content-Type': 'application/json'}).status(200).json({
success: 1,
message: 'Successfully Data Fetched',
data: complaintData
});
} catch(e){
return res.json({
success: 0,
message: 'No Data Fetched',
data: []
});
}
},
getLogs: async (req, res) => {
try{
const id = req.query.id
const result = await getLogs(id); //from service.js (an sql query)
var feedbackData = result[0][0];
return res.set({'Content-Type': 'application/json'}).status(200).json({
success: 1,
message: 'Successfully Data Fetched',
data: logs
});
} catch(e){
return res.json({
success: 0,
message: {text:'No Data Fetched ', errMsg: e.message},
data: []
});
}
},
routes //node js express js
app.js
app.use('/serviceCall', serviceCallRoute);
serviceCallRoute
router.get("/getList", getList);
router.get("/getDetails", getDetails);
router.get("/getTroubles", getTroubles);
router.get("/getLogs", getLogs);
angular subscribe to api
getServiceCalls() {
return this.http.get(url + 'serviceCall/getList',this.httpOptions)
.pipe(
map((res: IServiceCall) => {
return res;
}),
catchError(errorRes => {
return throwError(errorRes);
})
);
}
getServiceCallDetails(id):Observable<IServiceCall> {
const params = new HttpParams().set('id', id);
const headers = new HttpHeaders({ 'Content-Type': 'application/json'})
return this.http.get(url + 'serviceCall/getDetails',{headers:headers,params: params})
.pipe(
map((res: IServiceCall) => {
return res;
}),
catchError(errorRes => {
return throwError(errorRes);
})
);
}
getServiceCallTroubles(id) {
const params = new HttpParams().set('id', id);
const headers = new HttpHeaders({ 'Content-Type': 'application/json'})
return this.http.get<IServiceCallTroubles>(url + 'serviceCall/getTroubles',{headers:headers,params: params})
.pipe(
map((res: IServiceCallTroubles) => {
return res;
}),
catchError(errorRes => {
return throwError(errorRes);
})
);
}
getServiceCallLogs(id):Observable<IServiceCallLogs>{
const params = new HttpParams().set('id', id);
const headers = new HttpHeaders({ 'Content-Type': 'application/json'})
return this.http.get<IServiceCallLogs>(url + 'serviceCall/getLogs',{headers:headers,params: params})
.pipe(
map((res: IServiceCallLogs) => {
return res;
}),
catchError(errorRes => {
return throwError(errorRes);
})
);
}
The express js is working well. It is fault in database connection limit.
the DB connection limit was set as 10. So,after 10 api request with sql query. The db connection gets disconnected.

'fetch' from router - how to have 'res.status(400).json('Enter failure message here')' be handled as an error in .then .catch?

I have the following code as part of a Button in one of my React Native components. Observe how there is no .catch to handle a possible 'no results' case from server; it is handled with if-statements instead (e.g.: else if (acceptMatchRequestData['status']==='failure') which I was what I'm trying to get away from.
await acceptMatchRequest(match['matchId'], userId, getUserInfoData[0]['ratings'][matchType])
.then(acceptMatchRequestData => {
if (acceptMatchRequestData['status']==='success') {
setMatchInvites(prevState => {
return prevState.filter((observation, i) => observation['matchId'] !== match['matchId'])
})
setMatchScreenParentState('loading')
sendToUserDeviceNotification('matchFound', userId, match['matchedUserId'])
} else if (acceptMatchRequestData['status']==='failure') {
Alert.alert('', acceptMatchRequestData['message'])
}
})
acceptMatchRequest function code:
export async function acceptMatchRequest(matchId, userId, rating) {
console.log('Attempting to accept match request');
info = { matchId, userId, rating }
const firebaseIdToken = await AsyncStorage.getItem('#firebaseIdToken')
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + firebaseIdToken },
body: JSON.stringify(info)
};
const response = await fetch(ngrokOrLocalhost + '/acceptmatchrequest', requestOptions)
const data = await response.json()
return data
}
And server code:
router.post('/acceptmatchrequest', async (req, res) => {
const { matchId, userId, rating } = req.body;
const results = await Match.find({ 'usersInfo.userId': userId, 'matchRecords.matchConfirmed': { $nin: [true] } }).limit(5)
if (results.length===5) {
res.status(400).json({'status': 'failure', 'message': 'You already have 5 active matches. Please finish a match first before you can accept this match.'})
} else {
var filter2 = { '_id': matchId }
var update2 = { 'isCustomRequest_IsAccepted': true, '$push': { 'usersInfo': { 'userId': userId, 'location': { 'type': 'Point', 'coordinates': [0, 0] }, 'rating': rating } } }
var response2 = await Match.findOneAndUpdate(filter2, update2, { new: true, sort: { 'matchCreatedTimestamp': 1 } })
if (response2) {
// Document was updated
res.status(200).json({'status': 'success', 'message': 'Match request was accepted successfully'})
} else {
console.log('Match request was not accepted successfully');
res.status(400).json({'status': 'failure', 'message': 'Match request was not accepted successfully'})
}
}
})

Export Function does not return data when Sequelize.findAll on another file

I get some data from request query & with that requested param send to MySQL database. When Request successful then it returns data (Main controller File) & on behalf of that data I need to call some other functions that exist on other model class(Common Method File). Common Method File function also have some database dependency. When I am debugging from Common Method File it returns data as per expectation. But it does not return any data from (Main controller File). What should I do now?
I also try this to convert async await but it fail.
Main controller File
const MenuData = require('../../models/common/MenuData');
const MenuModel = require('../../models/menu/menuModel');
exports.listMenu = function(req, res) {
let menu_id = 0;
if(req.query.menuID){
menu_id=req.query.menuID;
MenuModel.findOne({
where:{
id:menu_id
}
}).then(menu=>{
if(menu){
let menu_details = MenuData.menuDetails(menu.dataValues);
if(menu_details){
return res.send({status: 200, message: menu_details});
}else{
return res.send({status: 302, message: "Data Not Found"});
}
}
}).catch(err =>{
return res.send('error: '+err);
});
}else{
return res.send('error: '+err);;
}
};
Common Method File
const MenuRelationalModel = require('../../models/menu/menuRelationModel');
exports.menuDetails=(content_data)=>{
let data = [];
if(content_data){
if(content_data.id && content_data.id == 1){
MenuRelationalModel.findAll({
where:{
menu_id:content_data.id
}
}).then(menu_all_data =>{
if(menu_all_data){
menu_all_data.map((menu_item,index)=>{
data.push(menu_item.dataValues);
})
return data;
}
}).catch(err =>{
data.push(err);
return data;
});
}
}
}
Coming Output
{
"status": 302,
"message": []
}
Expected Output
{
"status": 200,
"message": [
{
"id": 4,
"menu_id": 1,
"parent_page_id": 0,
"page_id": 1,
"item_title": "Ongoing",
"item_type": "category",
"parent_type": "category"
},
{
"id": 5,
"menu_id": 1,
"parent_page_id": 0,
"page_id": 2,
"item_title": "Completed",
"item_type": "category",
"parent_type": "category"
}
]
}
Your common method is async, so you've to return a promise.
const MenuRelationalModel = require('../../models/menu/menuRelationModel');
exports.menuDetails=(content_data)=>{
return new Promise((resolve, reject) => {
let data = [];
if(content_data){
if(content_data.id && content_data.id == 1){
MenuRelationalModel.findAll({
where:{
menu_id:content_data.id
}
}).then(menu_all_data =>{
if(menu_all_data){
menu_all_data.map((menu_item,index)=>{
data.push(menu_item.dataValues);
})
resolve(data);
}
}).catch(err =>{
data.push(err);
resolve(data);
});
}
} else {
resolve(data);
}
})
}
And in you main code
if (menu) {
MenuData.menuDetails(menu.dataValues).then(menu_details => {
if (menu_details) {
return res.send({ status: 200, message: menu_details });
} else {
return res.send({ status: 302, message: "Data Not Found" });
}
});
}

Interacting with deployed Ethereum/Quorum contract

I'm trying to deploy a contract in a private Ethereum (Quorum with RAFT but executing only public tx) using solc version 0.5.1 and web3 version 1.0.0-beta.36. Number contract is deployed and contractAddress is returned upon receiving receipt.
Though might seem cumbersome, for easier reproducibility, I've written all code here. The problem I'm having is in the last block (interacting with contract).
const solc = require('solc');
const Web3 = require('web3');
const input = {
language: 'Solidity',
sources: {
solContract: {
content: `
pragma solidity ^0.5.1;
contract Number {
uint private num = 100;
function getNum() public view returns(uint) {
return num;
}
function setNum(uint _num) public returns(bool success) {
num = _num;
return true;
}
}`
}
},
settings: {
outputSelection: {
'*': {
'*': ['*']
}
}
}
}
const output = JSON.parse(solc.compile(JSON.stringify(input)))
const abi = output.contracts.solContract.Number.abi
const bytecode = `0x${output.contracts.solContract.Number.evm.bytecode.object}`
const web3 = new Web3('http://127.0.0.1:8555');
const privateKey = '0x82f74b773d7f948153d7eb8c192bd9819e3e94073d8bdc0e03d659aa42cd34ba';
// const account = web3.eth.accounts.privateKeyToAccount(privateKey);
const contract = new web3.eth.Contract(abi);
web3.eth.personal.unlockAccount('0x27f4cD26d7e4eAde2052ec5B61f6594D1481C4A2', 'passwordstring', 600)
.then(
contract.deploy({
data: bytecode,
arguments: []
})
.send({
from: '0x27f4cD26d7e4eAde2052ec5B61f6594D1481C4A2',
gas: 0x47b760
}, (error, transactionHash) => {
if (error) {
console.log(error)
} else if (transactionHash) {
web3.eth.getTransaction(transactionHash)
.then((data) => {
console.log(data)
})
}
})
.on('receipt', (receipt) => {
console.log(receipt.contractAddress)
}))
// Interacting with deployed contract
// contract address returned from above
// 0x101ea6703717Fa3CF70e96FD3C84FE74ddca50eB
contract.options.address = '0x101ea6703717Fa3CF70e96FD3C84FE74ddca50eB'
contract.methods.getNum().call({
from: 0x27f4cD26d7e4eAde2052ec5B61f6594D1481C4A2,
gas: 0x47b760
}, (err, data) => {
err ? console.log(err) : console.log(data)
})
The issue you're having here is that when you execute the last block of code, the contract has actually not yet been deployed.
You need to perform the call to getNum() within the on() block, e.g.:
.on('receipt', (receipt) => {
console.log("CONTRACT CREATED, ADDRESS = ", receipt.contractAddress)
performGetNum(receipt.contractAddress)
})
function performGetNum(contractAddress) {
sampleContract.options.address = contractAddress
sampleContract.methods.getNum().call({
from: "0xed9d02e382b34818e88b88a309c7fe71e65f419d",
gas: 0x47b760
}, (err, data) => {
err ? console.log(err) : console.log("getNum() returned value: ", data)
})
}

My response from api is undefined on frontend

I got list of items from my database mySql and also button 'edit'.
When I clicked edit (by id) I want to see all fields filled by data.
But I only have in my console: undefined
If I tested my api by postman it works fine.
There is how I am getting list.
{
const id = this.actRoute.snapshot.paramMap.get('id');
this.studentApi.GetStudent(id).subscribe((res: any) => {
console.log(res.data);
this.subjectArray = res.data;
console.log(this.subjectArray);
this.studentForm = this.fb.group({
id: [res.id, [Validators.required]],
domain_id: [res.domain_id, [Validators.required]],
source: [res.source, [Validators.required]],
destination: [res.destination]
});
});
}
There is my api.service.ts
GetStudent(id): Observable<any> {
const API_URL = `${this.endpoint}/read-student/${id}`;
return this.http.get(API_URL, { headers: this.headers })
.pipe(
map((res: Response) => {
return res || {};
}),
catchError(this.errorMgmt)
);
}
And there is my route
studentRoute.get('/read-student/:id', (request, response) => {
const id = request.params.id;
con.query('SELECT * FROM students WHERE id = ?', id, (error, result) => {
if (error) throw error;
response.send(result);
});
});
There is response from 'postman'
[
{
"id": 5,
"domain_id": 2,
"source": "tester0700#test.pl",
"destination": "testw#test.pl"
}
]
It seems like the response is an array, containing an object.
In that case, there is no need to use res.data, as that would imply the returned observable, res has a property named data, and that you are trying to access the value within that property. You can simply assign res to the subjectArray property. I am pretty sure res would be defined.
this.studentApi.GetStudent(id).subscribe((res: any) => {
console.log(res);
this.subjectArray = res;
// handle the rest here.
});