Better pattern to detect web3 default account when using metamask - javascript

Context: I want to use blockies to render an identicon on the page, i get the defaultAccount from web3, for this, the user has to be logged on to metamask with a selected address from his wallet.
The problem: the web app seems to not detect the web3 object on the load event of the page, wchih is the recommended place to detect it.
The code: below is inspired from recommendations at:
https://github.com/MetaMask/metamask-plugin/issues/1158
https://github.com/MetaMask/faq/blob/master/DEVELOPERS.md#partly_sunny-web3---ethereum-browser-environment-check
I keep having intermittent behaviour, sometimes web3 is there and sometimes it is not, the only solution I can think of is to have a timer, but that seems to me a bit too simplistic, I would prefer something more elegant.
Question: Is there a better solution to detect the defaultAccount from web3 when the page loads?
function startApp() {
GenerateIdenticon();
}
window.addEventListener('load', function () {
// Checking if Web3 has been injected by the browser (Mist/MetaMask)
if (typeof web3 !== 'undefined') {
// Use Mist/MetaMask's provider
window.web3 = new Web3(web3.currentProvider);
if (web3.currentProvider.isMetaMask === true) {
if (typeof web3.eth.defaultAccount === 'undefined') {
document.body.innerHTML = '<body><h1>Oops! Your browser does not support Ethereum Ðapps.</h1></body>';
}
else {
startApp();
}
}
else {
alert('No web3? Please use google chrome and metamask plugin to enter this Dapp!', null, null);
// fallback - use your fallback strategy (local node / hosted node + in-dapp id mgmt / fail)
window.web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
}

function _Connect(callback){
if(typeof web3 !== 'undefined') {
web3 = new Web3(window.web3.currentProvider);
web3.version.getNetwork((err, netId) => {
switch (netId) {
case "1":
callback('Switch Network', null);
break
case "2":
console.log('This is the deprecated Morden test network.');
callback('Switch Network', null);
break
case "3":
console.log('Connected to the ropsten test network.');
web3.eth.defaultAccount = web3.eth.accounts[0];
if(!web3.eth.defaultAccount){
console.log('Log into metamask');
_Connect(callback);
}else{
// Success
console.log(`Web3 ETH Account: ${web3.eth.defaultAccount}`);
callback(false, web3.eth.defaultAccount);
}
break
default:
console.log('This is an unknown network.');
callback('Switch Network', null);
}
});
} else {
console.log(`Failed: Web3 instance required, try using MetaMask.`);
callback('Install Metamask', null);
}
}

There is a delay when Chrome inserts the MetaMask Web3 library so the timeout is necessary (1 second timeout should be enough).
After the timeout, you check if the web3 global object exists and then read the default account.
If it doesn't exist, then insert your own web3 object.

Related

How to Approve spend of one Token with Web3.js?

I seek via my web page of test to create a button which allows to authorize the expenditure of a Contract (Token)..
If I go to the page and I click I would like the script to load web3 (it loads well) then if I press the button that Metamask authorizes the spending of the contract.
Metamask opens fine and does request the connection for my test site on the BSC in Web3js. However I can't find the exact code for the approve function.
Here is the code:
<head>
<script src='https://cdnjs.cloudflare.com/ajax/libs/web3/1.7.0/web3.min.js'></script>
</head>
<button onclick="approvebutton();">Approve button to authorize tokens to be spent</button>
<script type="text/javascript">
if (typeof window.ethereum !== 'undefined') {
ethereum.request({ method: 'eth_requestAccounts' });
} else {
alert('Please install metamask')
}
var Web3 = require('web3');
const web3 = new Web3('https://bsc-dataseed1.binance.org:443');
async function approvebutton() {
/// APPROVE FUNCTION WITH THE CONTRACT
}
</script>
I tried this approach but it doesn't work (metamask confirmation won't show up):
if (typeof window.ethereum !== 'undefined') {
ethereum.request({ method: 'eth_requestAccounts' });
} else {
alert('Please install metamask')
}
var Web3 = require('web3');
const web3 = new Web3('https://bsc-dataseed1.binance.org:443');
const Contract = ('0xContractAddress');
const spenderAdr = ('0xSpenderAddress');
const amount = ('AmountTokensNumber')
async function approvebutton(Contract,spenderAdr){
Contract.methods.approve(spenderAddr, amount).send({
from: ownerAddr
})
}
Metamask won't show up to confirm the TX.
First of all, the approve method takes 2 parameters, the spender and the amount so it will be something like this:
Contract.methods.approve(spenderAddr, amount).send({
from: ownerAddr
})
The gas parameter is optional.
From the example code, I think you're missing the ABI (or Json Interface) for the contract and the instantiation of the contract via web3.eth.Contract() with the ABI and the contract address.
var Contract = new web3.eth.Contract(jsonInterface[, address][, options])
From that Contract instance you can then call the methods in the ABI, and that should trigger the Metamask modal.
From the docs:
Contract.methods.myMethod(123).send({from: '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe'})
.then(function(receipt){
// receipt can also be a new contract instance, when coming from a "contract.deploy({...}).send()"
});
Or in your case, something along the lines of:
await Contract.methods.approve(spenderAddr, amount).send({ from: ownerAddr })
I also think that you're missing an await inside the approvebutton() function, to await the "promisevent" that the method call on the contract returns.
(Source https://web3js.readthedocs.io/en/v1.2.11/web3-eth-contract.html#, https://web3js.readthedocs.io/en/v1.2.11/web3-eth-contract.html#id26)

stop chrome extension if identity check fails

I am trying to implement an identity check for my chrome extension since I want to sell it to some users.
The code below is usig the chrome.identity API to get the unique OpenID in combination with the email that is logged in. Then it is fetching data from a pastebin and checks if the id is included or not.
If not I would want to block the user from using the extension. What would be the best approach?
My code:
// license check
chrome.identity.getProfileUserInfo({ 'accountStatus': 'ANY' }, async function (info) {
email = info.email;
console.log(info.id);
let response = await fetch('https://pastebin.com/*****');
let data = await response.text();
console.log(data.indexOf(info.id));
if (data.indexOf(info.id) !== -1) {
console.log('included');
} else {
console.log('not included');
// block chrome extension usage;
}
});
I found a pretty simple solution that should work for most cases.
// license check
chrome.identity.getProfileUserInfo({ 'accountStatus': 'ANY' }, async function (info) {
email = info.email;
console.log(info.id);
let response = await fetch('https://pastebin.com/*****');
let data = await response.text();
console.log(data.indexOf(info.id));
if (data.indexOf(info.id) !== -1) {
console.log('included');
} else {
console.log('not included');
// block chrome extension usage;
chrome.browserAction.setPopup({ popup: 'index.html' }); // index.html has to
be in the
extension folder and can have e. g. a <h1> which says "Invalid license"
}
});

Web3 not treating web3.eth.Contract as a contructor

I'm writing a JS file to provide functionality to a smart contract. From what I can tell, MetaMask is injecting web3 into the webpage, but when I attempt to create a contract object, my browsers console (brave browser) is stating that web3.eth.Contract is not a constructor.
I've looked in the object provided by my browsers console, and I don't see the Contract constructor. Is this normal? Or do you think web3 may be incorrectly installed? I've hit a wal at this point.
var blockContractJSON = $.getJSON("../build/contracts/Blocks.json", function(data) {
return data;
});
console.log(blockContractJSON)
// console.log(blocksContract)
var blocksContract;
var currentUser;
var web3js;
console.log(web3js);
// console.log(blockContractJSON);
// defines contract address and initializes contract functions
var startApp = function() {
var contractAddress = "0x2520127E14E8A14C67Ee2B561714ADae53D48110";
console.log('got the contract'); <- web3 not passing to this line?
blocksContract = new web3js.eth.Contract(blockContractJSON, contractAddress);
// console.log(blocksContract);
var checkAccounts = setInterval(function() {
if (web3js.eth.accounts[0] !== currentUser) {
currentUser = web3js.eth.accounts[0];
}
}, 100)();
};
// adds in web3
window.addEventListener('load', function() {
console.log('item loaded from page is')
console.log()
// Checking if Web3 has been injected by the browser (Mist/MetaMask)
if (typeof web3 !== 'undefined') {
console.log('using metamask');
console.log(web3)
// Use Mist/MetaMask's provider
web3js = new Web3(web3.currentProvider);
console.log(web3js)
} else {
alert('install metamask')
}
startApp();
});
Works for me
const Web3 = require('web3')
var web3 = new Web3(new Web3.providers.HttpProvider("infuralink"));
const ABI=[];
const Contract = new web3.eth.Contract(ABI,"contractaddress");
Try using Ethereum lib instead (web3-eth)
[https://web3js.readthedocs.io/en/v1.2.0/web3-eth.html][1]
var eth = new Eth('http://localhost:8545');
check first parameter is the abi node of the JSON not the abi metadata
new eth.Contract(contractAbi.abi, CONTRACT_ADDRESS);

ReactJS, Redux and DexieJS (IndexedDB) - Error in incognito mode and Chrome v69

I'm currently learning ReactJS and I decided to create a simple application.
The stack is:
React
Redux
React-router
DexieJS (IndexedDB)
The application is working. The problem is that when I try to test it on Firefox or incognito mode (in Chrome), I get this error:
TypeError: Cannot read property 'apply' of undefined
Anyone knows why I get this error and how I could handle that? I found that IndexedDB is not available in Firefox and incognito mode, so I tried to make a simple check:
if(!window.indexedDB) {
alert('Indexed DB is not supported by your browser. If you are running in incognito mode, please use the normal mode.')
}
But this is not working, I get the error again.
Here is the Github repo if you want to see the whole code:
https://github.com/Webd01/BM
Thanks for your help!
IndexedDB works fine in Chrome incognito mode, so if you have a problem there, it might be caused my something else.
But you are right that IndexedDB is not good in Firefox private browsing mode, although you're wrong about specifically how. window.indexedDB is not null in Firefox private browsing mode, but it does give you an error on upgradeneeded. I use something like this to detect it (this has some other browser compatibility checks too):
var checkIDB = function () {
if (typeof window.indexedDB === "undefined") {
console.log("IndexedDB not supported at all!");
return;
}
try {
keyRange.only([1]);
} catch (e) {
console.log("Buggy Microsoft IndexedDB implementation");
return;
}
var openRequest = window.indexedDB.open('firefox-private-test', 1);
openRequest.onerror = function (evt) {
console.error(evt.target.error);
if (evt.target.error.message.includes("aborted")) {
console.log("Some other error, maybe quota related:");
console.log(evt.target.error);
} else {
console.log("Firefox private mode, probably:");
console.log(evt.target.error);
}
}
openRequest.onupgradeneeded = function (evt) {
var db = evt.target.result;
var one = db.createObjectStore('one', {
autoIncrement: true,
keyPath: 'key'
});
one.createIndex('one', 'one');
one.add({one: 1});
var two = db.createObjectStore('two', {
autoIncrement: true,
keyPath: 'key'
});
two.createIndex ('two', 'two');
two.add({two: 2});
};
openRequest.onsuccess = function (evt) {
var db = evt.target.result;
var transaction;
try {
transaction = db.transaction(['one', 'two'], 'readwrite');
} catch (e) {
console.log("Some browser failed here, maybe an old version of Safari, I forget");
console.log(e.target.error);
return;
}
var count = 0;
transaction.objectStore('one').index('one').openCursor().onsuccess = function (evt) {
cursor = evt.target.result;
if (cursor) {
count += 1;
cursor.continue();
}
};
transaction.oncomplete = function () {
db.close();
if (count === 1) {
console.log("All good!");
} else {
console.log("Buggy Safari 10 IndexedDB implementation")
}
};
};
};

What should I do with the redundant state of a ServiceWorker?

I gotta a companion script for a serviceworker and I'm trialling right now.
The script works like so:
((n, d) => {
if (!(n.serviceWorker && (typeof Cache !== 'undefined' && Cache.prototype.addAll))) return;
n.serviceWorker.register('/serviceworker.js', { scope: './book/' })
.then(function(reg) {
if (!n.serviceWorker.controller) return;
reg.onupdatefound = () => {
let installingWorker = reg.installing;
installingWorker.onstatechange = () => {
switch (installingWorker.state) {
case 'installed':
if (navigator.serviceWorker.controller) {
updateReady(reg.waiting);
} else {
// This is the initial serviceworker…
console.log('May be skipwaiting here?');
}
break;
case 'waiting':
updateReady(reg.waiting);
break;
case 'redundant':
// Something went wrong?
console.log('[Companion] new SW could not install…')
break;
}
};
};
}).catch((err) => {
//console.log('[Companion] Something went wrong…', err);
});
function updateReady(worker) {
d.getElementById('swNotifier').classList.remove('hidden');
λ('refreshServiceWorkerButton').on('click', function(event) {
event.preventDefault();
worker.postMessage({ 'refreshServiceWorker': true } );
});
λ('cancelRefresh').on('click', function(event) {
event.preventDefault();
d.getElementById('swNotifier').classList.add('hidden');
});
}
function λ(selector) {
let self = {};
self.selector = selector;
self.element = d.getElementById(self.selector);
self.on = function(type, callback) {
self.element['on' + type] = callback;
};
return self;
}
let refreshing;
n.serviceWorker.addEventListener('controllerchange', function() {
if (refreshing) return;
window.location.reload();
refreshing = true;
});
})(navigator, document);
I'm a bit overwhelmed right now by the enormity of the service workers api and unable to "see" what one would do with reg.installing returning a redundant state?
Apologies if this seems like a dumb question but I'm new to serviceworkers.
It's kinda difficult to work out what your intent is here so I'll try and answer the question generally.
A service worker will become redundant if it fails to install or if it's superseded by a newer service worker.
What you do when this happens is up to you. What do you want to do in these cases?
Based on the definition here https://www.w3.org/TR/service-workers/#service-worker-state-attribute I am guessing just print a log in case it comes up in debugging otherwise do nothing.
You should remove any UI prompts you created that ask the user to do something in order to activate the latest service worker. And be patient a little longer.
You have 3 service workers, as you can see on the registration:
active: the one that is running
waiting: the one that was downloaded, and is ready to become active
installing: the one that we just found, being downloaded, after which it becomes waiting
When a service worker reaches #2, you may display a prompt to the user about the new version of the app being just a click away. Let's say they don't act on it.
Then you publish a new version. Your app detects the new version, and starts to download it. At this point, you have 3 service workers. The one at #2 changes to redundant. The one at #3 is not ready yet. You should remove that prompt.
Once #3 is downloaded, it takes the place of #2, and you can show that prompt again.
Write catch function to see the error. It could be SSL issue.
/* In main.js */
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('./sw.js')
.then(function(registration) {
console.log("Service Worker Registered", registration);
})
.catch(function(err) {
console.log("Service Worker Failed to Register", err);
})
}

Categories

Resources