Accessing accounts in metamask through web3.js 1.x - web3js

I want to retrieve the currently selected account in my metamask plugin through web3.js. And I want to do it dynamically, so when switched to another account, it should be printed to the UI.
I'm importing the library (beta.37) via:
<script src="https://cdn.jsdelivr.net/gh/ethereum/web3.js#1.0.0-beta.37/dist/web3.min.js"></script>"
To make things more complicated:
1) In Brave web3.eth.accounts[0] would log my current address in any other site than my dApp, yet here, it returns "undefined".
2) in Chrome (same build) it would always returns undefined.
To me it is inexplicable how it can return undefined, when other dApps that are built on web3.js 0.x use that exact same code.
Consequently, I can't use the following function, to dynamically print the current address:
var accountInterval = setInterval(function() {
if (web3.eth.accounts[0] !== userAccount) {
userAccount = web3.eth.accounts[0];
}
}, 100);

In web3.js 1.x you must use getAccounts() async method, e.g. as follows:
var accounts = await web3.eth.getAccounts();
var userAccount =accounts[0]
PS: web3.eth.accounts[0] in other Dapps shows your account because they are still using the old web3.js version, very likely the one injected by Metamask

Related

Using the paypalhere sdk in react web app

I'm building a web app for inventory management. I've got React on the frontend, and Nodejs+mongodb on the backend. Our company vends at local events and most of our sales are paid with cards. To process card payments we use the Paypal Here app on our phones which connects to a card reader and we manually type in the payment amount. Since we have over 200 different products (custom art), we decided to build this application so that we can quickly search for the product(s) being purchased, add them to the "cart" where the total price plus tax will be automatically calculated, and then a total of 3 payment option buttons will be present, one for cash, one for venmo, and one for card. At first, I figured the card selection button could link externally to the Paypal Here app and the payment amount would be automatically filled in when redirected, but then I realized I could actually integrate a Paypalhere sdk in the application, which sounded better than a redirect. There's three different sdks, one for ios, one for android, and one for the web, and the one for the web is what I need. I looked for an npm package, no luck, then I tried manually inserting the script and src into the document via react helment, no luck, on componentDidMount, no luck. I'm not used to not having an npm package to use, so my question today is how can I integrate this sdk into my React app?
Heres a link to the web integration documentation: https://developer.paypal.com/docs/integration/paypal-here/sdk-dev/web/#integration
Heres an the code I used to manually insert the script onComponentDidMount, I don't know if it worked, but even if it did, I don't know how to access it...
useEffect(() => {
const script = document.createElement("script");
script.src = "https://www.paypalobjects.com/pph/websdk/js/pphwebsdk-1.1.14.min.js";
script.async = true;
document.body.appendChild(script);
return () => {
document.body.removeChild(script);
};
}, []);
Don't remove the script after adding it.
You can set a callback function to have your code that uses PPH run after the script loads. Here's an example with a callback function, it's for regular PayPal buttons rather than PPH, but you can adapt it to your needs.
function loadAsync(url, callback) {
var s = document.createElement('script');
s.setAttribute('src', url); s.onload = callback;
document.head.insertBefore(s, document.head.firstElementChild);
}
loadAsync('https://www.paypal.com/sdk/js?client-id=sb&currency=USD', function() {
paypal.Buttons({
// Set up the transaction
createOrder: function(data, actions) {
return actions.order.create({
purchase_units: [{
amount: {
value: '0.01'
}
}]
});
},
// Finalize the transaction
onApprove: function(data, actions) {
return actions.order.capture().then(function(details) {
// Show a success message to the buyer
alert('Transaction completed by ' + details.payer.name.given_name);
});
}
}).render('body');
});
Alternatively, you can just load the SDK statically from in the index <head> of your application, and it'll always be there ready for use.

cn1PostMessage is undefined on Android. PostMessage is not working

I created a webview (BrowserComponent) and added a listener:
BComp.addWebEventListener(BrowserComponent.onMessage, e->{
Display.getInstance().callSerially(()->{
Dialog.show("Message", (String)e.getSource(), "OK", null);
});
});
Then, in the JavaScript of the embedded website, I called cn1PostMessage and postMesage. This works well in the simulator! But when building the application, on Android, it does nothing (cn1PostMessage is undefined and postMessage is not received by the main program).
var msg = "test";
if(window.cn1PostMessage) {
window.cn1PostMessage(msg);
} else {
window.parent.postMessage(msg, '*');
// Tried: window.postMessage(msg, '*');
// Tried: window.top.postMessage(msg, '*');
}
What can I do?
Thanks!
I just tried building the BrowserComponentPostMessageSample, and it seemed to work fine on my Galaxy S8, Android 8.
If you're finding that window.cn1PostMessage is undefined, then either there was a javascript error in page load that prevented that callback from being installed, or your code is running before the CodenameOne initialization code has run.
I have just added support for the "android.webContentsDebuggingEnabled" display property, which will make the app's web views debuggable using Chrome's remote development tools. This will make it easier for your to track down such issues. See usage example:
Display.getInstance().setProperty("android.webContentsDebuggingEnabled", "true");
This will be available in Friday's update (Dec. 6/19).
In the mean time, I recommend starting with the BrowserComponentPostMessageSample and modifying it to suit your needs from there.
Alternatively, if you can post a test case to demonstrate the issue, I can look at it.

auth0 parseHash can't create property '__enableIdPInitiatedLogin' on hash string

I'm trying to upgrade my React web app from auth0-js 9.6.1 to 9.7.3. After installing the new library, my Slack login flow no longer works, it appears to be breaking in the callback.
TypeError: Cannot create property '__enableIdPInitiatedLogin' on string '#access_token={token string}&token_type=Bearer&state={state string}'
My parseHash call is:
this.auth0.parseHash(hash, (err, authResult) => {
if (authResult && authResult.idToken) {
AuthService.setToken(authResult.idToken); // JWT returned from Auth0;
// Redirect user to content.
const returnUrl = localStorage.getItem(Variables.RETURN_URL_KEY);
localStorage.removeItem(Variables.RETURN_URL_KEY);
returnUrl
? window.location.replace(returnUrl)
: window.location.replace("/");
} else if (err) {
console.log("Error with auth callback", err);
window.location.replace("https://foo.com"); // If auth fails, send user to home page.
}
}
This works fine in 9.6.1, but fails in 9.7.x and I can't find anything about any breaking changes that would cause it to start failing. Any ideas?
I had the same issue as you so I opened a ticket on the Auth0.js library github page.
This is the response I got from the developers:
It was working by accident then (also, the string is being ignored in your case), considering that we expect the first parameter to either be an object or a callback function.
All of our docs mention that:
https://github.com/auth0/auth0.js#api
https://auth0.github.io/auth0.js/global.html#parseHash
https://auth0.com/docs/libraries/auth0js/v9#extract-the-authresult-and-get-user-info
In your case, the simplest fix is to just remove the first parameter and keep only the callback. window.location.hash is already used when there's no options object.
(emphasis on the fix mine)
I tested 9.7.3 with this.auth.auth0.parseHash((err, result) => ... and it worked like a charm.
I hope this'll help!

window, document and local Storage in React server side rendering

In my React application, I am using window object , document object and localStorage.
To avoid errors, I have set it up like:
var jsdom = require("jsdom");
var doc = jsdom.jsdom("");
if (typeof localStorage === "undefined" || localStorage === null) {
var LocalStorage = require('node-localstorage').LocalStorage;
localStorage = new LocalStorage('./scratch');
global.localStorage = localStorage;
}
var win = doc.defaultView
console.log("document default viewwwwwwwwwwwwwwwwww", doc);
global.document = doc
global.window = win
function propagateToGlobal (window) {
for (let key in window) {
if (!window.hasOwnProperty(key)) continue
if (key in global) continue
global[key] = window[key]
}
}
propagateToGlobal(win)
But in my application, I want real window, ,real localStorage and real document to be used instead of what I have set up above.
localStorage created this directory scratch.Does that mean browser localStorage would not be used now?
Also, the console statement gives this if I try to console doc variable and is being used in place of document variable which is creating problem:
Document { location: [Getter/Setter] }
This is the script I have :
<script dangerouslySetInnerHTML={{__html:(function(w,d,s,l,i){
console.log(d.getElementsByTagName(s)[0]);
w[l]=w[l]||[];
w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});
var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';
j.async=false;
j.src= '//www.googletagmanager.com/gtm.js?id='+i+dl;
console.log("f is",f);
f.parentNode ? f.parentNode.insertBefore(j,f) : false;
})(window,document,'script','dataLayer','ID')}}/>
Here getElementByTagName returns undefined and not an element as it should. How do I fix this?
basically, JSDom and the such should only be used if you would like to fake the window and document of the browser inside NodeJS. This is valid when running tests. I've not seen node-localstorage before, but i suspect the same is true of this package also.
You certainly do not want any of those packages to run within your app when on the client (in the browser).
You haven't specified which errors you have but I can only guess you are trying to run your app in node?
I would recommend removing all of them from your app completely and seeing where you get the errors. Then tackle the errors one by one. To start with ensure you only run that code on the client by using componentDidMount or something similar.
Once the app is working on the client and on the server, you could then look at how to improve / increase the amount the is rendered on the server.

Off-line Chrome Packaged App: how to manage Prod and Dev modes

To switch between dev/stage/prod on the server, I set an ENV variable. This is pretty standard.
With an Off-line Chrome App, how do I switch between dev/stage/prod? Especially around REST API URL's?
During development my app is installed in chrome as an "unpacked" app.
SOLUTION:
I combined these answers. Here's what I did:
On install, if unpacked extension, I set a value in localStorage.
On app run, I set a variable to the localstorage value, or to production if undefined.
FWIW, here's the code:
background.js:
chrome.runtime.onInstalled.addListener(function () {
console.log('onInstalled');
// Note: this event is fired every time the "Reload" link is clicked in
// 'Chrome Apps & Extensions Developer Tool'. So only set if not set.
// If unpacked extension,
if(!chrome.runtime.getManifest().update_url) {
// Load existing value
chrome.storage.local.get('APIBaseURL', function(data) {
// Has value already been set?
if (!data.hasOwnProperty('APIBaseURL')) {
// set API server to localhost
chrome.storage.local.set({'APIBaseURL': DEV_APIBASEURL }, function() {
// Ok, notify the console.
console.log('Installed in dev mode: APIBaseURL = '+DEV_APIBASEURL);
} );
}
});
}
});
App.js (this is Angular, but you should see the pattern. Promises are ES6)
var PROD_APIBASEURL = 'https://production.com';
angular.module('wmw', ['wmw.routes'])
// Listen for online/offline events and set status in $rootScope
.run(['$rootScope', function($rootScope){
// Determine which server to run on
$rootScope.isDev = chrome.runtime.getManifest().hasOwnProperty('update_url');
// Async init code is in a Promise
$rootScope.promiseAppReady = new Promise(function(resolve, reject) {
// Get the Base URL
chrome.storage.local.get('APIBaseURL', function(data) {
// Apply it to our scope. If not set, use PROD.
$rootScope.$apply(function() {
if (data.hasOwnProperty('APIBaseURL')) {
$rootScope.APIBaseURL = data.APIBaseURL;
} else {
$rootScope.APIBaseURL = PROD_APIBASEURL;
}
resolve($rootScope.APIBaseURL);
});
});
});
}]);
$rootScope.promiseAppReady let's me know when the code is done and the app is ready.
$rootScope.$apply() bubbles changes up to other scopes. If you're not using Angular, you can remove this.
I also included this code with some debug tools:
var debugTools = {
setAPIBaseURL: function (url) {
chrome.storage.local.set({'APIBaseURL': url});
},
showAPIBaseURL: function() {
chrome.storage.local.get('APIBaseURL', function(data) {console.log(data)});
}
}
so it was easy to change in the console.
In the console chrome.runtime.getManifest().update_url will have a value if installed from the store. Undefined if not.
See How to distinguish between dev and production in a Firefox extension I'm building?
And Check if Chrome extension installed in unpacked mode
From your description, I don't think you want the Chrome App to only talk to the remote server when it's installed from the Chrome Web Store and only talk to the local server when it's installed unpacked. I would think that you'd want the option of talking to either server no matter how it's installed.
So, I'd program the app to choose its server based on a key in Local Storage. You can then easily set that key from the Developer Tools panel (the Resources) tab. If the key is undefined, it uses the remote server.

Resources