Unable to set provider in web3 object - smartcontracts

I am following an online course on DApp creation and I am currently trying to set up the application that will be used in the tutorials.
The Dapp is set up using Truffle and Metamask on Chrome. All the Dapp files were provided by the course (actually an Ubuntu linux instance was provided for use in a Virtual machine) but soon it became evident that due to changes in more recent versions of Metamask (the course dates from 2017 I think) the web front-end controls related to account addresses (a drop down list displaying them and a button calling a function that sends an account address to the contract) were broken. Being new to the whole ecosystem I followed a suggestion from a fellow student that modified the initWeb3 function from this
initWeb3: function() {
// Is there is an injected web3 instance?
if (typeof web3 !== 'undefined') {
App.web3Provider = web3.currentProvider;
} else {
// If no injected web3 instance is detected, fallback to the TestRPC
App.web3Provider = new Web3.providers.HttpProvider('http://127.0.0.1:9545');
}
web3 = new Web3(App.web3Provider);
App.populateAddress();
return App.initContract();
}
to this
initWeb3: function() {
// Is there is an injected web3 instance?
if (typeof web3 !== 'undefined') {
ethereum.enable().then(() => {
App.web3Provider = web3.givenProvider;
});
} else {
// If no injected web3 instance is detected, fallback to the TestRPC
App.web3Provider = new Web3.providers.HttpProvider(App.url);
}
web3 = new Web3(App.web3Provider);
App.populateAddress();
return App.initContract();
}
I understand that the key difference is the ethereum.enable() call. This did make the relevant controls appear in the (Chrome) webpage and now Metamask also displays a popup requiring approval of its communication with the account by the user. The problem is that when the webpage loads, Chrome records the following error:
Uncaught Error: Invalid provider passed to setProvider(); provider is null
at Function.setProvider (truffle-contract.js:308)
at Object.success (app.js:61)
at i (jquery.min.js:2)
at Object.fireWith [as resolveWith] (jquery.min.js:2)
at y (jquery.min.js:4)
at XMLHttpRequest.c (jquery.min.js:4)
I assume that this is related to the changes in the app.js file but I could not find a solution. I have tried replacing App.web3Provider = new Web3.providers.HttpProvider(App.url) from the "new" version with App.web3Provider = new Web3.providers.HttpProvider('http://127.0.0.1:9545'); that existed in the original version but this did not make any difference. Same with givenProvider and currentProvider.
Me and other fellow students have contracted the staff behind the course for assistance but none was provided (I have taken up the issue with them as well). I have googled the error and spent significant time reading back the results but so far no solution has been provided - some results suggest this is actually a Web3 bug but I could not be certain. If anyone can assist with the issue it will be appreciated.
Thanks

I ran into this issue following the tutorial on Truffle's site here: https://trufflesuite.com/tutorial/index.html. I resolved it slightly differently, but similar order.
initWeb3: async function() {
// Modern dapp browsers...
if (window.ethereum){
web3 = new Web3(web3.currentProvider);
try {
//Request account access
await window.ethereum.request({ method: "eth_requestAccounts" });
} catch (error) {
// User denied account access...
console.error("User denied account access");
}
App.web3Provider = web3.currentProvider;
console.log("modern dapp browser");
}
// Legacy dapp browsers...
else if (window.web3) {
App.web3Provider = window.web3.currentProvider;
console.log("legacy dapp browser");
}
// if no injected web3 instance is detected, fall back to Ganache
else {
App.web3Provider = new Web3.providers.HttpProvider('http://localhost:7545');
}
web3 = new Web3(App.web3Provider);
return App.initContract();
},

I have resolved the issue - I have used code from the Ethereum Javascript API page to initiate my web3 instance inside the if..else block and then set App.web3Provider provider to the provider of the instance
initWeb3: function() {
// Is there is an injected web3 instance?
if (typeof web3 !== 'undefined') {
ethereum.enable().then(() => {
web3 = new Web3(web3.currentProvider);
});
} else {
// If no injected web3 instance is detected, fallback to the TestRPC
web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
}
App.web3Provider=web3.currentProvider;
App.populateAddress();
return App.initContract();
},

Related

Anyone have a solution for generating server-side tokens for the ESRI JSAPI SDK?

There are a number of solutions to this:
use the build-in dialog provided by esri/IdentityManager (https://developers.arcgis.com/javascript/3/jsapi/identitymanagerbase-amd.html)
use a server-side proxy (https://github.com/Esri/resource-proxy)
use the identity manager initialize() method (https://developers.arcgis.com/javascript/3/jsapi/identitymanagerbase-amd.html#initialize)
But there what is missing is the ability to hook into the request for a token. I am working with ArcGISDynamicMapServiceLayer and there is no way to know if the server return a 498/499, and no way to update the url to update the token.
I started hacking around in the API to try to hook into various events with no real promise of success. What seems to be missing:
a way to detect when a token is needed
a way to update the token
Closes I came up with is listening for "dialog-create" but there is no way to disable the dialog apart from throwing an exception, which disables the layer.
I tried replacing the "_createLoginDialog" method and returning {open: true} as a trick to pause the layers until I had a token ready but since there is no way to update the layer endpoint I did not pursue this hack. It seems the only way this might work is to use the initialize() method on the identity manager.
Does anyone have knowledge of options beyond what I have outlined?
EDIT: The goal is to provide a single-sign-on experience to users of our product.
"User" is already signed in to our application
"User" wishes to access a secure ESRI ArcGIS Server MapServer or FeatureServer services from the ESRI JSAPI
"User" is prompted for user name and password
The desired flow is to acquire a token on the users behalf using a RESTful services in our product and return the appropriate token that will allow the "User" to access the secure services without being prompted.
I do not wish to use a proxy because I do not want all that traffic routed through the proxy.
I do not wish to use initialize() because it is complicated and not clear how that works apart for re-hydrating the credentials.
I do wish for an API that simply allows me to set the token on any layer services that report a 499 (missing token) or 498 (invalid token), but I cannot find any such API. The solution I am focusing on hinges on being able to update the url of an ArcGISImageServiceLayer instance with a new token.
This answer lacks in satisfaction but delivers on my requirements. I will start with the code (client-side typescript):
class TokenProxy {
private tokenAssuranceHash = {} as Dictionary<Promise<{ token: string, expiration: string }>>;
private service = new TokenService();
private timeoutHandle = 0;
watchLayer(esriLayer: ArcGISDynamicMapServiceLayer) {
setInterval(async () => {
const key = esriLayer._url.path;
const token = await this.tokenAssurance(key);
esriLayer._url.query.token = token;
}, 5000);
}
updateRefreshInterval(ticks: number) {
clearTimeout(this.timeoutHandle);
this.timeoutHandle = setTimeout(() => {
Object.keys(this.tokenAssuranceHash).forEach(url => {
this.tokenAssuranceHash[url] = this.service.getMapToken({serviceUrl: url});
});
this.updateRefreshInterval(ticks);
}, ticks);
}
async tokenAssurance(url: string) {
if (!this.tokenAssuranceHash[url]) {
this.tokenAssuranceHash[url] = this.service.getMapToken({serviceUrl: url});
}
try {
const response = await this.tokenAssuranceHash[url];
await this.recomputeRefreshInterval();
return response.token;
} catch (ex) {
console.error(ex, "could not acquire token");
return null;
}
}
async recomputeRefreshInterval() {
const keys = Object.keys(this.tokenAssuranceHash);
if (!keys.length) return;
const values = keys.map(k => this.tokenAssuranceHash[k]);
const tokens = await Promise.all(values);
const min = Math.min(...tokens.map(t => new Date(t.expiration).getTime()));
if (Number.isNaN(min)) return; // error occured, do not update the refresh interval
const nextRefreshInTicks = min - new Date().getTime();
this.updateRefreshInterval(0.90 * nextRefreshInTicks);
}
}
And highlight the hack that makes it work:
const key = esriLayer._url.path;
const token = await this.tokenAssurance(key);
esriLayer._url.query.token = token;
The "_url" is a hidden/private model that I should not be using to update the token but it works.

ConfidentialClientApplication Caching - GetAccountAsync

the
Lately I joined a project that is using Azure AD Open ID connect authentication code to authenticate with the ASP.Net Core web application.
When I am trying to run it locally I am facing issues with retrieving info with the GetAccountAsync method (Return null). From what I read, I think the code is missing a caching helper to cache the user/application tokens.
public async Task<string> GetUserAccessTokenAsync(string userId)
{
var account = await _app.GetAccountAsync(userId);
try
{
var result = await _app.AcquireTokenSilent(_scopes, account).ExecuteAsync();
return result.AccessToken;
}
// Unable to retrieve the access token silently.
catch (Exception)
{
throw new ServiceException(new Error
{
Code = GraphErrorCode.AuthenticationFailure.ToString(),
Message = "Caller needs to authenticate. Unable to retrieve the access token silently."
});
}
}
If anyone has any idea what I could do to fix this issue Ill be happy to know :)
Thank you!

IPFS js cant connect to ws://127.0.0.1:8081/p2p/

I am running a ipfs js instance which is working well but i get some error with web sockets and I am unsure why it is even calling a local IP ?
Firefox can’t establish a connection to the server at ws://127.0.0.1:8081/p2p/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64.
In firefox on deployed site no errors but in safari
The page at https://alpha.nodenogg.in/ was not allowed to run insecure content from ws://127.0.0.1:8081/p2p/Qmbut9Ywz9YEDrz8ySBSgWyJk41Uvm2QJPhwDJzJyGFsD6.
here is some of the code I am using, any pointers and to where this error is coming from would be great. The site however does what I want with IPFS so I am not sure what this error is related to. Thank you
import VueIpfs from 'ipfs'
const ipfs = VueIpfs.create()
mounted: function () {
// console.log(VueIpfs)
this.getIpfsNodeInfo()
},
methods: {
async getIpfsNodeInfo() {
try {
// Await for ipfs node instance.
node = await ipfs
} catch (err) {
// Set error status text.
this.status = `Error: ${err}`
}
},
onFileSelected(event) {
this.selectedFile = event.target.files[0]
this.saveIPFS()
},
async saveIPFS() {
try {
this.fileContents = await node.add(this.selectedFile)
this.getIPFS()
} catch (err) {
// Set error status text.
this.status = `Error: ${err}`
}
},
}
This is not indeed an issue with your code.
I believe you are using webrtc-star for transport and discovery (it is the default for browser environment in js-ipfs, if you did not custom it, you should have it).
So, you use webrtc-star to discover other peers to talk to. Once you get to know these peers, your node will get to know all the multiaddrs that peer is announcing to the network. There will be nodes announcing several addresses, some of which are local IP addresses. js-libp2p has a feature to specify announce and noAnnounce addresses in its configuration, which allows people to be able to specify in these cases if you should not announce your local addr and announce the public one. However, this feature is not widely known at the moment.
However, from a libp2p/IPFS stand point, we need to find a better way of catching and logging these errors since they seem error related to your node/code, but they are a result of bad propagation of multiaddrs that other peers announce in the network.
I hope that I answered your question, and we will look into a patch to get rid of these errors

Firebase authentication not working as expected

I'm following along with the firebase docs about anonymous authentication, but I must be missing something.
Here is my attempt at authenticating:
var dataRef = new Firebase('https://myfirebaseurl.firebaseio.com');
// Log me in
dataRef.authAnonymously(function(error, authData) {
if (error) {
console.log('Login Failed!', error);
} else {
console.log('Authenticated successfully with payload:', authData);
}
});
The result is that I get a 'TypeError: undefined is not a function' message because 'authAnonymously()' is supposedly not defined.
I have 'Enable Anonymous User Authentication' checked for my firebase though... and I don't know what else would keep this from being an option. Is it not offered in the 'Hacker' version?
I am running this locally, so it shouldn't be a domain permissions issue since 'localhost' is included in the default accepted domains.
David was right.
I followed a tutorial on Firebase to setup my angular app, and I assumed that the version that tutorial was using was up to date with the version used in the docs. It was not. Just change your version to the most recent one if you were also silly enough to run into this :P.

Detecting if someone is logged into Meteor from a regular Node.js application

Is there a way for me to check to see if someone is logged into Meteor from outside of Meteor; for example, from an Express.js application? I would like to know from the Express app who the currently logged in user is on a particular client so that if the API were called, we would know who to apply the results of the API call to.
So this is best done it two parts.
A method to check whether the user is online in meteor
You can probably do it with a meteor smart package (community package repo) : https://github.com/erundook/meteor-profile-online
Make sure you have meteorite, installed via npm install meteorite -g
In your package repo use : mrt add profile-online
Accessing meteor's data using Express
To access the stuff in Express you would need a DDP client, I know this one works with pre1 (The version of DDP with Meteor 0.57+): https://github.com/EventedMind/node-ddp-client
You can have a method that checks for you in meteor
Server js (Meteor)
Meteor.methods({
'isonline: function(id) {
return Meteor.users.find(id).profile.online;
}
}
Express:
var client = new DDPClient({
host: "localhost",
port: 3000
});
userid = '1' //The user _id of the person you want to check
client.connect(function () {
console.log("Connected to Meteor at localhost:3000");
client.call("isonline", [userid], function(err,result) {
client.close();
if(!err) {
if(result) {
console.log("User " + userid + " is online");
}
else
{
console.log("That user isn't online");
}
}
else
{
console.log(err)
}
});
});