When I type a wallet address and press the Save button I want to show a Metamask sign in popup to that wallet.
for now, It's just randomly connects with the selected wallet. Basically, I want to be able to connect wallets with just wallet address.
profile.jsx
import React from "react";
import { useContext, MainContext } from "../hook/Context";
import Web3 from "web3";
const Profile = () => {
const { data, setData } = useContext(MainContext);
const detectCurrentProvider = () => {
let provider;
if (window.ethereum) {
provider = window.ethereum;
} else if (window.web3) {
provider = window.web3.currentProvider;
} else {
alert(
"Non-Ethereum browser detected. You should consider trying MetaMask!"
);
}
return provider;
};
const onConnect = async (e) => {
e.preventDefault();
try {
const currentProvider = detectCurrentProvider();
if (currentProvider) {
if (currentProvider !== window.ethereum) {
alert(
"Non-Ethereum browser detected. You should consider trying MetaMask!"
);
}
await currentProvider.request({ method: "eth_requestAccounts" });
const web3 = new Web3(currentProvider);
const userAccount = await web3.eth.getAccounts();
const chainId = await web3.eth.getChainId();
const account = userAccount[0];
let ethBalance = await web3.eth.getBalance(account); // Get wallet balance
ethBalance = web3.utils.fromWei(ethBalance, "ether"); //Convert balance to wei
setData(ethBalance, account, chainId);
if (userAccount.length === 0) {
console.log("Please connect to meta mask");
}
}
} catch (err) {
alert(
"There was an error fetching your accounts. Make sure your Ethereum client is configured correctly."
);
}
};
return (
<div className="container-fluid">
<div className="wallets__wrapper">
Your wallets:
{data.account}
</div>
<form className="token__form" onSubmit={onConnect}>
<input type="text" placeholder="Account Name" />
<input type="text" placeholder="Wallet Adress" />
<button className="add__btn">SAVE</button>
</form>
</div>
);
};
export default Profile;
try to load web3 in this way, this will ask you to connect to metamask first(pop-up)
const getWeb3 = () => {
return new Promise((resolve, reject) => {
// Wait for loading completion to avoid race conditions with web3 injection timing.
window.addEventListener("load", async () => {
// Modern dapp browsers...
if (window.ethereum) {
const web3 = new Web3(window.ethereum);
try {
// Request account access if needed
await window.ethereum.enable();
// Acccounts now exposed
resolve(web3);
} catch (error) {
reject(error);
}
}
// Legacy dapp browsers...
else if (window.web3) {
// Use Mist/MetaMask's provider.
const web3 = window.web3;
console.log("Injected web3 detected.");
resolve(web3);
}
// Fallback to localhost; use dev console port by default...
else {
const provider = new Web3.providers.HttpProvider(
"http://localhost:9545"
);
const web3 = new Web3(provider);
console.log("No web3 instance injected, using Local web3.");
resolve(web3);
}
});
});
};
load this in your useEffect:
useEffect(() => {
const init = async () => {
const web3 = await getWeb3();
//set accounts ,etc to state from web3 by awaiting them
const accounts = await web3.eth.getAccounts();
}
init();
}
Related
reading through this resource, https://docs.metamask.io/guide/provider-migration.html#summary-of-breaking-changes, it seems it is still possible to interact with a contract using window.ethereum,
eg (taken from the above link)
const transactionHash = await ethereum.request({
method: 'eth_sendTransaction',
params: [
{
to: '0x...',
'from': '0x...',
value: '0x...',
// And so on...
},
],
});
but I cannot figure out how to connect to the contract.
What do Ireplace this with?
contract = await new window.web3.eth.Contract(ABI,ADDRESS);
I am using the web3 library via this resource
https://cdn.jsdelivr.net/npm/web3#latest/dist/web3.min.js
my simple script is below;
var account = null;
var contract = null;
const ABI = "the abi is here"
const ADDRESS = "contract address is here";
async function asyncCall() {
console.log('async');
if (window.ethereum) {
try {
const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
account = accounts[0];
document.getElementById('wallet-address').textContent = account;
// this is the bit i'm stuck on currently
contract = await new window.web3.eth.Contract(ABI,ADDRESS);
//
var mintButton = document.getElementById('mint');
mintButton.addEventListener("click", function(){
console.log('minting');
contract.methods.mint(account).send({from:account,value:"100"});
});
var totalsupply = await contract.methods.totalsupply().call();
document.getElementById('ttt').textContent = totalsupply;
} catch (error) {
if (error.code === 4001) {
console.log('User rejected request');
}
console.log(error);
}
}
}
asyncCall();
So instead of using window.web3 since Metamask no longer injects it into webpages, you can do this before making the call to get the contract:
const Web3 = require('web3');
// Create Web3 instance
const web3 = new Web3(window.ethereum); // Where window.etherem is your provider.
You can then get your contract by calling:
const contract = new web3.eth.Contract(ABI, ADDRESS);
try this:
let provider = window.ethereum;
if (provider) {
await provider.request({ method: "eth_requestAccounts" });
this.web3 = new Web3(provider);
this.contract = new this.web3.eth.Contract(abi_v2, address);
}
I would like my website users to pay for minting using their metamask account, but then when they press the mint button, I want the actual minting to be done via a central Ethereum account.
Before I had a simple website where in the index.html file, I had a react app that ran in the background listening for mint calls (code of App.js attached in the as the second code block).
But I have modified my approach. Now on my website server, I have a node.js app running that listens for Javascript API fetch Posts (see first code block below). The hash value gets sent from other websites via API Post - where all the minting is done in a central Ethereum account.
Now I want my website server to mint from both sources: hash sent from API POST and also from the same website. From the front end, I want the users to be able to pay for the minting using their Ethereum account from their metamask wallet. In doing so the mint function in the App.js should be called (as I want to use the account coded in that function) from the front end after the user pays for the minting from his metamask wallet. I am hoping this can be done. Having another react app running may be too much as I don't want two apps running (one to handle API Posts and one for the React app). Can I somehow make payment through the Metamask wallet and still call the mint function in the App.js?
//Current App.js that listens for API Posts
const web3 = require('web3');
const ethers = require('ethers');
const express = require('express');
const path = require('path');
const LocationNFT = require('./abis/Location.json')
const fromAddress = <address>
const nftAddress = <address>
const app = express();
const port = 3000;
app.use(express.urlencoded({extended: true}));
app.use(express.json());
const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.post('/posthash', async function (request, response) {
var hashValue = request.body.hash;
console.log('Received hash: ' + hashValue);
if (!hashValue) {
return response.status(400).send({ error:true, message: 'Please provide hash value' });
} else{
const timestamp = Date.now();
var time = getTimestamp();
console.log(time + ' Adding hash:::::', hashValue);
//call the mint function
var success = await mint(hashValue);
console.log('success' + success) ;
response.send(success);
}
var transactionRecord = {
datetimestamp: time,
hash: hashValue
};
response.json("Hash added to blockchain: " + hashValue);
});
// mint the nft
async function mint(location)
{
const Provider = require('#truffle/hdwallet-provider');
const privateKey = <>;
let provider = ethers.getDefaultProvider('rinkeby');
//maybe this is not needed
var fromAddress = <>;
let wallet = new ethers.Wallet(privateKey, provider);
let balancePromise = wallet.getBalance();
console.log('balancePromise: ' + balancePromise + ' balancePromise ' +
JSON.stringify(balancePromise));
balancePromise.then((balance) => {
console.log('Balance before minting : ' + balance);
});
let transactionCountPromise = wallet.getTransactionCount();
transactionCountPromise.then((transactionCount) => {
console.log('transactionCount: ' + transactionCount);
});
//console.log ('wallet: ' + JSON.stringify(wallet));
let nftAddress = <>;
console.log ('nftAddress: ' + nftAddress);
try {
var mainContract = new ethers.Contract(nftAddress, LocationNFT.abi, wallet);
console.log("Mining...please wait.");
if(mainContract)
console.log ('mainContract data there: ');
const nft2 = await mainContract.mintNFT(location)
return nft2.hash;
} catch (e) {
console.log(e) // This gets executed when process times out in ~30 minutes
}
} //end of mint function
Before, I had a react app running in the background on my website server. Here is the code from the App.js
//Previous App.js
import React, { Component, useState } from 'react';
import Web3 from 'web3'
import './App.css';
import Location from '../abis/Location.json'
class App extends Component {
async componentWillMount() {
await this.loadWeb3()
await this.loadBlockchainData()
}
async loadWeb3() {
if (window.ethereum) {
window.web3 = new Web3(window.ethereum)
await window.ethereum.enable()
}
else if (window.web3) {
window.web3 = new Web3(window.web3.currentProvider)
}
else {
window.alert('Non-Ethereum browser detected. You should consider trying MetaMask!')
}
}
async loadBlockchainData() {
const web3 = window.web3
// Load account
const accounts = await web3.eth.getAccounts()
this.setState({ account: accounts[0] })
const networkId = await web3.eth.net.getId()
const networkData = Location.networks[networkId]
if(networkData) {
const abi = Location.abi
const address = networkData.address
const contract = new web3.eth.Contract(abi, address)
this.setState({ contract })
const totalSupply = await contract.methods.totalSupply().call()
this.setState({ totalSupply })
// Load Colors
for (var i = 1; i <= totalSupply; i++) {
const location = await contract.methods.locations(i - 1).call()
this.setState({
locations: [...this.state.locations, location]
})
}
} else {
window.alert('Smart contract not deployed to detected network.')
}
}
// mint the nft
mint = (location) => {
this.state.contract.methods.mint(location).send({ from: this.state.account })
.once('receipt', (receipt) => {
this.setState({
locations: [...this.state.locations, location]
})
})
}
constructor(props) {
super(props)
this.state = {
account: '',
contract: null,
totalSupply: 0,
locations: []
}
}
render() {
return (
<div>
<nav className="navbar navbar-dark fixed-top bg-dark flex-md-nowrap p-0 shadow">
<a className="navbar-brand col-sm-3 col-md-2 mr-0"
href="http://www.dappuniversity.com/bootcamp"
target="_blank" rel="noopener noreferrer">
Location Tokens
</a>
<ul className="navbar-nav px-3">
<li className="nav-item text-nowrap d-none d-sm-none d-sm-block">
<small className="text-white"><span id="account">{this.state.account}</span></small>
</li>
</ul>
</nav>
<div className="container-fluid mt-5">
<div className="row">
<main role="main" className="col-lg-12 d-flex text-center">
<div className="content mr-auto ml-auto">
<h1>Issue Token</h1>
<form onSubmit={(event) => {
event.preventDefault()
const location = this.location.value
this.mint(location)
}}>
<input type='text' className='form-control mb-1'
placeholder='e.g. #FFFFFF' ref={(input) => { this.location = input }} />
<input type='submit' className='btn btn-block btn-primary' value='MINT' />
</form>
</div>
</main>
</div>
<hr/>
<div className="row text-center">
{ this.state.locations.map((location, key) => {
return(
<div key={key} className="col-md-3 mb-3">
<div>{location}</div>
</div>
)
})}
</div>
</div>
</div>
);
}
}
export default App;
This is the code in the index.html file I am currently working on to check for metamask account. I want users to pay using their matamask wallet using their account within the wallet.
//The code in the index.html that I am working on
<script type="text/javascript">
window.addEventListener('load', function ()
{
if (typeof web3 !== 'undefined') {
console.log('Web3 Detected! ' + web3.currentProvider.constructor.name)
window.web3 = new Web3(web3.currentProvider);
} else {
console.log('No Web3 Detected... using HTTP Provider')
}
web3.eth.defaultAccount = web3.eth.accounts[1];
})
addEventListener('DOMContentLoaded', function() {
loadWeb3();
loadBlockchainData();
});
async function loadWeb3() {
console.log('Inside loadWeb3() fn')
if (window.ethereum) {
window.web3 = new Web3(window.ethereum)
await window.ethereum.enable()
console.log('window.ethereum')
}
else if (window.web3) {
window.web3 = new Web3(window.web3.currentProvider)
}
else {
console.log('Non-Ethereum browser detected. You should consider trying MetaMask!')
}
}
async function loadBlockchainData() {
console.log('Inside loadBlockchainData() fn')
const web3 = window.web3
// Load account
const accounts = await web3.eth.getAccounts()
this.setState({ account: accounts[0] })
console.log('web3' + web3)
const networkId = await web3.eth.net.getId()
const networkData = Location.networks[networkId]
if(networkData) {
const abi = Location.abi
const address = networkData.address
const contract = new web3.eth.Contract(abi, address)
this.setState({ contract })
const totalSupply = await contract.methods.totalSupply().call()
this.setState({ totalSupply })
// Load Locations
for (var i = 1; i <= totalSupply; i++) {
const location = await contract.methods.locations(i - 1).call()
this.setState({
locations: [...this.state.locations, location]
})
}
} else {
window.alert('Smart contract not deployed to detected network.')
}
}
i have issue on web3.eth.defaultAccount = web3.eth.getAccounts();
below is the code. it said Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'getAccounts')
the version of my web3 is "^1.3".
var contract = "";
if (typeof web3 !== 'undefined') {
console.log('inside web3')
Web3 = new Web3 (Web3.currentProvider);
} else {
console.log('else web3');
var web3 = new Web3(new Web3.providers.HttpProvider(provider));
}
window.ethereum.enable()
.then(function (accounts) {
console.log(accounts[0]);
web3.eth.defaultAccount = web3.eth.getAccounts();
var contractabi = web3.eth.contract([ABI])
function must be async
window.addEventListener("load", async () => {
// Modern dapp browsers...
if (window.ethereum) {
const web3 = new Web3(window.ethereum);
try {
// Request account access if needed
await window.ethereum.enable();
// Acccounts now exposed
resolve(web3);
} catch (error) {
reject(error);
}
}
try this to fit your case
if possible send me the complete implementation of this code, or you can add this
class App extends Component {
async UNSAFE_componentWillMount() {
await this.loadWeb3();
await this.loadBlockchainData();
}
async loadWeb3() {
if (window.ethereum) {
window.web3 = new Web3(window.ethereum);
await window.ethereum.enable();
} else if (window.web3) {
window.web3 = new Web3(window.web3.currentProvider);
} else {
window.alert('No ethereum broswer detected! You can check out MetaMask!');
}
}
async loadBlockchainData() {
const web3 = window.web3;
const account = await web3.eth.getAccounts();
this.setState({ account: account[0] });
const networkId = await web3.eth.net.getId();
}
}
please use async function to interact with web3 and await the response
const showAccount = document.querySelector('.showAccount');
const showBalance = document.querySelector('.showBalance');
const Web3 = require("web3");
getAccount();
getBalance();
getAccount returns the wallet id
async function getAccount() {
const accounts = await ethereum.request({ method: 'eth_requestAccounts' });
const account = accounts[0];
showAccount.innerHTML = account;
}
getBalance returns undefined instead of the amount of Ether from Metamask
async function getBalance() {
const accounts = await ethereum.request({ method: 'eth_requestAccounts' });
const account = accounts[0];
showBalance.innerHTML = account.balance;
}
Maybe someone knows a good API for retrieving more values than just these two with examples or got a nice video to learn from.
I found the properties for my accounts object: here
I customized your code and now it displays the amount of Ether from the Account:
const showAccount = document.querySelector('.showAccount');
const showBalance = document.querySelector('.showBalance');
getAccount();
loadBalance();
async function getAccount() {
const accounts = await ethereum.request({ method: 'eth_requestAccounts' });
const account = accounts[0];
showAccount.innerHTML = account;
}
function loadBalance(){
web3Provider = null;
contracts = {};
account = '0x0';
const Web3 = require("web3");
const ethEnabled = async () => {
if (window.ethereum) {
await window.ethereum.send('eth_requestAccounts');
window.web3 = new Web3(window.ethereum);
return true;
}
}
if (typeof web3 !== 'undefined') {
// If a web3 instance is already provided by Meta Mask.
web3Provider = web3.currentProvider;
web3 = new Web3(web3.currentProvider);
} else {
// Specify default instance if no web3 instance provided
web3Provider = new Web3.providers.HttpProvider('http://localhost:7545');
web3 = new Web3(App.web3Provider);
}
$.getJSON("Market.json", function (market) {
console.log("initializing Market contract")
// Instantiate a new truffle contract from the artifact
contracts.Market = TruffleContract(market);
// Connect provider to interact with contract
contracts.Market.setProvider(web3Provider);
});
$.getJSON("Users.json", function (users) {
console.log("initializing User contract")
// Instantiate a new truffle contract from the artifact
contracts.Users = TruffleContract(users);
// Connect provider to interact with contract
contracts.Users.setProvider(App.web3Provider);
});
var marketInstance;
var userInstance;
var loader = $("#loader");
var content = $("#content");
//loader.show();
content.show();
// Load account data
console.log("loading account data")
var currentAccount;
web3.eth.getCoinbase(function (err, account) {
if (err === null) {
console.log("Your Account: " + account)
account = account;
currentAccount = account;
web3.eth.getBalance(account, function(err, balance) {
if (err === null) { //Note:set id="accountBalance" in your html page
$("#accountBalance").text(web3.fromWei(balance, "ether") + " ETH");
}
});
}
});
}
I would get cached token using react-adal api
import { authContext, } from '../auth'
const AvatarContainer = () => {
getPersonPhoto(authContext)
return (
<Avatar />
)
}
async function getPersonPhoto(authContext) {
const user = await authContext.getCachedUser();
const token = authContext.getCachedToken('https://graph.microsoft.com')
console.log("the token", token)
var photoUrl = "https://graph.microsoft.com/v1.0/users/" + user.profile.aud + "/photo/$value";
var Avatar = null;
// try {
// Avatar = await client
// .api(photoUrl)
// .get();
// return Avatar;
// } catch (e) { return null; }
}
export default AvatarContainer
The token value is always null, I have specified the ressource uribut it's not working.
I checked my localstorage I have the token
You might try opening an issue in Github, or upgrading to MSAL now that ADAL is being deprecated.