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?
Related
I'm using zustand with persist plugin to store the state of my application. I want to use localstorage but the cache has to be encrypted.
For encryption, I'm using encrypt-storage. For encryption keys, I want to make an API call to the backend and initialise the encrypt storage.
The problem is while the API call is being made, the storage is still undefined. How to properly initialise zustand with encrypt-storage ?
Here is what I have tried :
import { EncryptStorage } from "encrypt-storage";
import { create } from "zustand";
import { devtools, persist, } from "zustand/middleware";
import { createJSONStorage } from "zustand/middleware"
const fake_api = (ms: number) => new Promise(resolve => {
setTimeout(resolve, ms)
})
export function KeyResolver(callback: () => void) {
const fn = async () => {
//
await fake_api(2000);
console.log("encryption key retrieved")
encryptStorage.EKEY = 'secret-key-value';
encryptStorage.storage = new EncryptStorage(encryptStorage.EKEY, {
stateManagementUse: true,
});
callback();
};
if (!encryptStorage.EKEY) {
fn();
}
}
interface IEncryptStorage {
storage: undefined | EncryptStorage,
EKEY: null | string,
}
export const encryptStorage: IEncryptStorage = {
storage: undefined,
EKEY: null,
}
const useOptimiserStore = create<IOptimiserStore>()(
devtools(
persist(
(set) => ({
...initialOtimiserStoreState,
_hasHydrated: false,
setHyderated: (val) => set({ _hasHydrated: val })
}),
{
name: "optimiser-storage",
// #ts-expect-error
storage: createJSONStorage(() => encryptStorage.storage),
onRehydrateStorage: () => {
KeyResolver(() => {
useOptimiserStore.getState().setHyderated(true)
});
}
}
),
{
name: "optimiser-storage",
}
)
);
// And i'm using it inside my component like this:
const Component = () => {
const hasHyderated = useOptimiserStore(state => state._hasHydrated);
if (!hasHyderated) {
return <>retreiving encryption keys </>
}
return <div> ... </div>
}
But I get the following error:
Uncaught TypeError: can't access property "setItem", storage is undefined
I managed to make it work by implementing a custom storage engine.
https://docs.pmnd.rs/zustand/integrations/persisting-store-data#how-can-i-use-a-custom-storage-engine
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();
}
// 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>
);
}
I want login system. My export default not working Read function. Read function query user_id in AsyncStorage. Please help me :)
app.js
var sendTabs = <TabsScreens />
var sendLogin = <LoginScreens />
var Read = readStore = async () => {
try {
const value = await AsyncStorage.getItem('user_id');
if (value !== null) {
return 1;
} else {
return 0;
}
} catch (e) {
alert(e);
}
}
var Welcome;
Read().then((response) => {
if (response == 1) {
Welcome = sendTabs
} else {
Welcome = sendLogin;
}
});
export default () => (Welcome)
You could define new component to handle this logic:
export function WelcomeScreen() {
const [isLoggedIn, setIsLoggedIn] = React.useState(null); // null | true | false
React.useEffect(() => {
void async function getUserId() {
const id = await AsyncStorage.getItem('user_id');
setIsLoggedIn(Boolean(id)); // true -> user_id found, false -> no valid user_id found
}();
}, []);
return (userId === null)
? <Text>Loading...</Text> // this will show until storage is checked
: (isLoggedIn) ? <TabsScreens /> : <LoginScreens />; // depending on the value of id from storage you show tabs or login screen
}
I would like create WebRTC connection on 2 devices.
On my first device (Initiator), i create my Offer with this method :
createOffer() {
const { pc } = this;
pc.onnegotiationneeded = () => {
pc.createOffer()
.then(offer => pc.setLocalDescription(offer))
.then(() => {
this.setState({
offer: JSON.stringify(pc.localDescription)
});
});
};
}
And on my second device (Receiver), i create the answer :
createAnswer() {
const { pc } = this;
const { dataOffer } = this.state;
if (dataOffer) {
const sd = new RTCSessionDescription(JSON.parse(dataOffer));
pc.setRemoteDescription(sd)
.then(() => pc.createAnswer())
.then(answer => {
this.setState({
offer: JSON.stringify(answer)
});
return pc.setLocalDescription(answer);
});
}
}
But, after send Answer on first device, i've this error : PeerConnection cannot create an answer in a state other than have-remote-offer or have-local-pranswer.
When Initiator receive the answer, i run createAnswer method with answer data, That may be the problem ?
I don't really understand what method/event i need use after receive the answer :/
EDIT with new method for Initiator device :
receiveAnswer() {
const { pc } = this;
const { dataOffer } = this.state;
const sd = new RTCSessionDescription(JSON.parse(dataOffer));
pc.setRemoteDescription(sd);
}
But the connection state stay on checking :(
You can see my componentDidMount :
componentDidMount() {
const { pc } = this;
pc.oniceconnectionstatechange = () => {
this.setState({
connectionState: pc.iceConnectionState
});
};
pc.onaddstream = ({ stream }) => {
if (stream) {
this.setState({
receiverVideoURL: stream.toURL()
});
}
};
}