I am developing my first Dapp, I am using metamask and web3 for this. As far now, I am able to get my wallet balance and connect account to metamask. Now I am trying switch between two networks, I am using handleChainChanged, also I am passing chainId and Networkversion but, it is giving me error. I am uncertain about returning anything from changeNetwork function or I only have to pass chainId and Networkversion.
import { useStoreApi } from "./storeApi";
import { useState } from "react";
import useWeb3 from "./useWeb3";
import { Button, TextField } from "#material-ui/core";
import "./App.css";
function App() {
const { balance, address, message, setAddress, setBalance } = useStoreApi();
const web3 = useWeb3();
// get user account on button click
const getUserAccount = async () => {
if (window.ethereum) {
try {
await window.ethereum.enable();
web3.eth.getAccounts().then((accounts) => {
setAddress(accounts[0]);
updateBalance(accounts[0]);
console.log(accounts);
});
} catch (error) {
console.error(error);
}
} else {
alert("Metamask extensions not detected!");
}
web3.eth.getChainId().then(console.log);
};
const updateBalance = async (fromAddress) => {
await web3.eth.getBalance(fromAddress).then((value) => {
setBalance(web3.utils.fromWei(value, "ether"));
});
};
const changeNetwork = async () => {
if (window.ethereum) {
try {
await window.ethereum.enable();
window.ethereum._handleChainChanged({
chainId: 0x1,
networkVersion: 1,
});
} catch (error) {
console.error(error);
}
}
};
return (
<div className="App">
<header className="App-header">
{address ? (
<>
<p> Balance: {balance} </p>
</>
) : null}
<Button
onClick={() => getUserAccount()}
variant="contained"
color="primary"
>
Connect your account
</Button>
<Button onClick={changeNetwork} variant="contained" color="primary">
Switch to mainnet ethereum
</Button>
</header>
</div>
);
}
export default App;
What if the user doesn't have the required network added? Here is an expanded version which tries to switch, otherwise add the network to MetaMask:
const chainId = 137 // Polygon Mainnet
if (window.ethereum.networkVersion !== chainId) {
try {
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: web3.utils.toHex(chainId) }]
});
} catch (err) {
// This error code indicates that the chain has not been added to MetaMask
if (err.code === 4902) {
await window.ethereum.request({
method: 'wallet_addEthereumChain',
params: [
{
chainName: 'Polygon Mainnet',
chainId: web3.utils.toHex(chainId),
nativeCurrency: { name: 'MATIC', decimals: 18, symbol: 'MATIC' },
rpcUrls: ['https://polygon-rpc.com/']
}
]
});
}
}
}
You can use wallet_switchEthereumChain method of RPC API of Metamask
Visit: https://docs.metamask.io/guide/rpc-api.html#wallet-switchethereumchain
const changeNetwork = async () => {
if (window.ethereum) {
try {
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: Web3.utils.toHex(chainId) }],
});
});
} catch (error) {
console.error(error);
}
}
changeNetwork()
export async function switchToNetwork({
library,
chainId,
}: SwitchNetworkArguments): Promise<null | void> {
if (!library?.provider?.request) {
return
}
const formattedChainId = hexStripZeros(
BigNumber.from(chainId).toHexString(),
)
try {
await library.provider.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: formattedChainId }],
})
} catch (error) {
// 4902 is the error code for attempting to switch to an unrecognized chainId
// eslint-disable-next-line #typescript-eslint/no-explicit-any
if ((error as any).code === 4902) {
const info = CHAIN_INFO[chainId]
await library.provider.request({
method: 'wallet_addEthereumChain',
params: [
{
chainId: formattedChainId,
chainName: info.label,
rpcUrls: [info.addNetworkInfo.rpcUrl],
nativeCurrency: info.addNetworkInfo.nativeCurrency,
blockExplorerUrls: [info.explorer],
},
],
})
// metamask (only known implementer) automatically switches after a network is added
// the second call is done here because that behavior is not a part of the spec and cannot be relied upon in the future
// metamask's behavior when switching to the current network is just to return null (a no-op)
try {
await library.provider.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: formattedChainId }],
})
} catch (error) {
console.debug(
'Added network but could not switch chains',
error,
)
}
} else {
throw error
}
}
}
Related
I'm trying to learn how to use firestore and I can't solve this error when trying to add a client:
(I'm using Next.Js, Typescript and Firebase 8.8.0)
TypeError: this._delegate.toFirestore is not a function
return client;
} else {
const docRef = await this.collection().add(client);
| ^
const doc = await docRef.get();
return doc.data() as Client;
}
I tried to use Firebase V9 but got the same error.
If I add clients collection manually in firebase page I get this error:
TypeError: this._delegate.fromFirestore is not a function
async showAll(): Promise<Client[]> {
const query = await this.collection().get()
return query.docs.map((doc: any) => doc.data()) ?? [];
^
}
My code:
import ClientRepository from "../../core/ClientRepository";
import Client from "../../core/Clients";
import firebase from "../config";
export default class ClientCollection implements ClientRepository {
#converter: any = {
toFireStore(client: Client) {
return {
name: client.name,
taxId: client.taxId,
ie: client.ie,
zipCode: client.zipCode,
state: client.state,
city: client.city,
district: client.district,
street: client.street,
number: client.number,
};
},
fromFireStore(snapshot: firebase.firestore.QueryDocumentSnapshot, options: firebase.firestore.SnapshotOptions): Client {
const data = snapshot.data(options);
return new Client(
data.name,
data.taxId,
data.ie,
data.zipCode,
data.state,
data.city,
data.district,
data.street,
data.number,
snapshot.id
);
},
};
private collection() {
return firebase.firestore().collection("clients").withConverter(this.#converter);
}
async save(client: Client): Promise<Client> {
if (client?.id) {
await this.collection().doc(client.id).set(client);
return client;
} else {
const docRef = await this.collection().add(client);
const doc = await docRef.get();
return doc.data() as Client;
}
}
async delete(client: Client): Promise<void> {
return this.collection().doc(client.id).delete();
}
async showAll(): Promise<Client[]> {
const query = await this.collection().get()
return query.docs.map((doc: any) => doc.data()) ?? [];
}
}
I want connect to Metamask on mobile browser using JavaScript. It works on Desktop browser. However, it doesn't work on mobile browsers.
export const getProvider = (wallet: string) => {
let provider;
if (wallet === 'metamask') {
provider = window.ethereum?.providers
? window.ethereum.providers.find((x: any) => x.isMetaMask)
: window.ethereum;
} else if (wallet === 'coinbase') {
provider = window.ethereum?.providers
? window.ethereum.providers.find((x: any) => x.isCoinbaseWallet)
: window.ethereum;
}
return provider;
};
const connectMetamask = async() => {
try {
let addressArray;
let provider = getProvider('metamask');
addressArray = await provider.request({
method: 'eth_requestAccounts',
});
changeNetwork();
const obj = {
address: addressArray[0],
};
setCookie('wallet', 'metamask');
setLocalStorage('address', checkSumAddress(addressArray[0]));
setLocalStorage('recent', 'metamask');
return obj;
} catch (err: any) {
return {
address: '',
};
}
}
I know mobile doesn't have Chrome extensions, but I want to call the Metamask app at least. I use #metamask/detect-provider and #web3-react for testing connection to Metamask.
import { useWeb3React } from '#web3-react/core';
import { connectors } from 'utils/wallet/connector';
const ProviderTest = () => {
const { library, chainId, account, active, activate, deactivate } = useWeb3React();
const onClickMetamask = () => {
if (active) {
deactivate();
alert('Metamask exit');
}
activate(connectors.metamask, error => {
console.log(error);
});
};
return (
<button className="button large" onClick={onClickMetamask}>
{' '}
METAMASK
</button>
);
};
export default ProviderTest;
It doesn't work on mobile browser too. Is there a solution on this situation?
// constants
import Web3EthContract from "web3-eth-contract";
import Web3 from "web3";
// log
import { fetchData } from "../data/dataActions";
const connectRequest = () => {
return {
type: "CONNECTION_REQUEST",
};
};
const connectSuccess = (payload) => {
return {
type: "CONNECTION_SUCCESS",
payload: payload,
};
};
const connectFailed = (payload) => {
return {
type: "CONNECTION_FAILED",
payload: payload,
};
};
const updateAccountRequest = (payload) => {
return {
type: "UPDATE_ACCOUNT",
payload: payload,
};
};
export const connect = () => {
return async (dispatch) => {
dispatch(connectRequest());
const abiResponse = await fetch("/config/abi.json", {
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
});
const abi = await abiResponse.json();
const configResponse = await fetch("/config/config.json", {
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
});
const CONFIG = await configResponse.json();
const { ethereum } = window;
const metamaskIsInstalled = ethereum && ethereum.isMetaMask;
if (metamaskIsInstalled) {
Web3EthContract.setProvider(ethereum);
let web3 = new Web3(ethereum);
try {
const accounts = await ethereum.request({
method: "eth_requestAccounts",
});
const networkId = await ethereum.request({
method: "net_version",
});
if (networkId == CONFIG.NETWORK.ID) {
const SmartContractObj = new Web3EthContract(
abi,
CONFIG.CONTRACT_ADDRESS
);
dispatch(
connectSuccess({
account: accounts[0],
smartContract: SmartContractObj,
web3: web3,
})
);
// Add listeners start
ethereum.on("accountsChanged", (accounts) => {
dispatch(updateAccount(accounts[0]));
});
ethereum.on("chainChanged", () => {
window.location.reload();
});
// Add listeners end
} else {
dispatch(connectFailed(`Change network to ${CONFIG.NETWORK.NAME}.`));
}
} catch (err) {
dispatch(connectFailed("Something went wrong."));
}
} else {
dispatch(connectFailed("Install Metamask."));
}
};
};
export const updateAccount = (account) => {
return async (dispatch) => {
dispatch(updateAccountRequest({ account: account }));
dispatch(fetchData(account));
};
};
Metamask extension is working fine in desktop but the issue is it not working on Mobile, it is not openning the metamask app, we dont know now how to solve this issue...
If anyone here who can help will be great. We have a solution for this instead of using metamask, we want to use wallet connect integration, but we dont know how to do that too. We have read the docs but we are too confuse in it....
On mobile connecting to wallets will not work in your normal chrome/safari browsers- apple or other mobile creators do not allow this you can only connect your wallet by using the browser in your metamask app on your phone.
ie.
navigate to metamask or whichever wallet you are using on your phone
open the browser in that app
go to the website you need
I've found a way with deeplinking to work with Android and IOS from the browser, it solved my problem with opening MetaMask and redirect my app there ( or installing if its not installed )
It's the complete code, you can find this part of code here :
https://github.com/EhsanParsania/MetaMask-React-sample/blob/main/src/components/MetaMaskInstallModal.js#L74
or clone it and run to use and check all features :
https://github.com/EhsanParsania/MetaMask-React-sample
and this is the specific code :
function connectToPhoneMetamsk() {
const metamaskAppDeepLink = "https://metamask.app.link/dapp/" + YOUR_ORIGIN ; // put your origin here without http:// ro https://
return (
<a href={metamaskAppDeepLink}>
<Button>Connect to MetaMask</Button>
</a>
);
}
im having trouble downloading an image from supabase. I get this errror
{
"statusCode": "400",
"error": "Bad Request",
"message": "headers should have required property 'authorization'"
}
I've followed the docs
https://supabase.io/docs/guides/with-nextjs
here is my code for the request
const myQuote = ({ dataFetch }) => {
const [avatarUrl, setAvatarUrl] = useState(null);
async function downloadImage(path) {
try {
const { data, error } = await supabase.storage
.from("job-photo")
.download(`jobphotos/${path}.jpg`);
if (error) {
throw error;
}
const url = URL.createObjectURL(data);
setAvatarUrl(url);
} catch (error) {
console.log("Error downloading image: ", error.message);
}
}
return (
<>
<Dashboard />
<QuoteTable tableData={dataFetch} downloadImg={downloadImage} />
</>
);
};
export default myQuote;
Managed to get it working by adding public/ to from
const { data, error } = await supabase.storage
.from("public/job-photo")
.download(`jobphotos/${path}.jpg`);
do you have the following code?
import { createClient } from '#supabase/supabase-js'
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
export const supabase = createClient(supabaseUrl, supabaseAnonKey)
As a beginner to vue.js I'm struggling with this problem for days.
I know that there are few plugins for that:
vue-google-auth
and
vue-google-signin-button
and
vue-authenticate
But none of these come with good documentations, so my attempts to make use of them failed.
I also could not find any tutorial on vue.js with OAuth2 authentication after extensive googling. So appreciate if someone could come up with a full working example or refer me to some complete code.
Example of usage without any plugins:
index.html
<meta name="google-signin-client_id" content="your-client-id.apps.googleusercontent.com"
/>
<script src="https://apis.google.com/js/platform.js"></script>
App.vue
<template>
<div v-show="!profile" id="g-signin2"></div>
<div v-if="profile">
<pre>{{ profile }}</pre>
<button #click="signOut">Sign Out</button>
</div>
</template>
<script>
export default {
mounted() {
this.initGoogleAuth();
this.renderGoogleAuthButton();
},
data() {
return {
profile: null
};
},
methods: {
onSignIn(user) {
const profile = user.getBasicProfile();
const fullName = profile.getName();
const email = profile.getEmail();
const imageUrl = profile.getImageUrl();
this.profile = { fullName, email, imageUrl };
},
signOut() {
var auth2 = window.gapi.auth2.getAuthInstance();
auth2.signOut().then(() => {
console.log("User signed out");
this.profile = null;
});
},
initGoogleAuth() {
window.gapi.load("auth2", function () {
window.gapi.auth2.init();
});
},
renderGoogleAuthButton() {
window.gapi.signin2.render("g-signin2", {
onsuccess: this.onSignIn
});
}
}
};
</script>
This is a working example with vue-google-oauth2.
You can install it with:
npm i vue-google-oauth2
Then you need to place these 2 lines of code in your APP ENTRY file, e.g. src/main.js
import GAuth from 'vue-google-oauth2'
Vue.use(GAuth, {clientId: 'XXXXXXXX'})
Where XXXXXXXX is the clientId you get from https://console.cloud.google.com/apis/
I will assume you have been there if you've tried to login with Google before.
Then you create this component
<template>
<div>
<h1>Test</h1>
<button #click="handleClickGetAuth" :disabled="!isInit">get auth code</button>
<button #click="handleClickSignIn" v-if="!isSignIn" :disabled="!isInit">signIn</button>
<button #click="handleClickSignOut" v-if="isSignIn" :disabled="!isInit">signOout</button>
</div>
</template>
<script>
export default {
name: 'test',
data () {
return {
isInit: false,
isSignIn: false
}
},
methods: {
async handleClickGetAuth() {
try {
const authCode = await this.$gAuth.getAuthCode()
const response = await this.$http.post('http://your-backend-server.com/auth/google', { code: authCode, redirect_uri: 'postmessage' })
} catch (error) {
// On fail do something
}
},
async handleClickSignIn(){
try {
const googleUser = await this.$gAuth.signIn()
console.log('user', googleUser)
this.isSignIn = this.$gAuth.isAuthorized
} catch (error) {
// On fail do something
console.error(error);
return null;
}
},
async handleClickSignOut(){
try {
await this.$gAuth.signOut()
this.isSignIn = this.$gAuth.isAuthorized
} catch (error) {
// On fail do something
}
}
},
mounted(){
let that = this
let checkGauthLoad = setInterval(function(){
that.isInit = that.$gAuth.isInit
that.isSignIn = that.$gAuth.isAuthorized
if(that.isInit) clearInterval(checkGauthLoad)
}, 1000);
}
}
</script>
All credits goes to
https://github.com/guruahn/vue-google-oauth2/blob/master/sample.html