issue when interacting with Trust Wallet - javascript

I'm developing a dApp using web3 library. In some case the user has to decide how they want to connect, either MetaMask or WalletConnect.
So, when the user decides to connect with WalletConnect there is no problem with the connection itself but I have an issue when I want to interact with Smart Contract. Suppose I want to approve to user amount for stake or check the balance.
export const schooseProvider = provider => {
// debugger
console.log("schooseProvider: ", provider );
if(provider === "MetaMask"){
console.log("Selected Provider: ", window.ethereum)
W3 = new Web3(window.ethereum)
}
else if(provider === "WalletConnect"){
const provider = new WalletConnectProvider({
infuraId: '20c6beb49cd1402db84120a858bc74af',
bridge: 'https://bridge.walletconnect.org',
supportedChainIds,
rpc: {
3: 'https://ropsten.infura.io/v3/20c6beb49cd1402db84120a858bc74af'
}
})
console.log("Selected Provider: ", provider)
W3 = new Web3(provider)
}
}
export const approve = async (account) => {
try{
store.dispatch(updateAproveButtonsLoader(true))
await contract.methods.approve(stakeAddress,'10000000000000000000000000000000000000000000000000').send({from: account})
.once('receipt', function(receipt){
store.dispatch(updateApproveButtonsLoader(false))
store.dispatch(updateApproved(true))
checkAllowence(account)
})
.on('error', () => {
store.dispatch(updateApproveButtonsLoader(false))
})
}
catch(error){
store.dispatch(updateApproveButtonsLoader(false))
console.log(error)
}
}

Trust Wallet doesn't support testnets for now, however you can add it as custom chain

Related

Using Metamask but get Error: Returned error: The method eth_sendTransaction does not exist/is not available

I want to call a payable function in a smart contract I deployed, but it does not work. This is the error I am getting:
Error: Returned error: The method eth_sendTransaction does not exist/is not available
The answer I could find is to just use a private key, because infura does not cater this method, however I want the user to sign the transaction to the smart contract with MetaMask.
This is my code:
export async function helloworld() {
const rpcURL =
"https://ropsten.infura.io/v3/KEY";
const web3 = new Web3(rpcURL);
let provider = window.ethereum;
if (typeof provider !== "undefined") {
provider
.request({ method: "eth_requestAccounts" })
.then((accounts) => {
selectedAccount = accounts[0];
console.log(`Selected account is ${selectedAccount}`);
})
.catch((err) => {
console.log(err);
return;
});
window.ethereum.on("accountsChanged", function (accounts) {
selectedAccount = accounts[0];
console.log(`Selected account changed to ${selectedAccount}`);
});
}
const networkId = await web3.eth.net.getId();
const thecontract = new web3.eth.Contract(
simpleContractAbi,
"0x50A404efF9A057900f87ad0E0dEfA0D485931464"
);
isInitialized = true;
investit(thecontract, selectedAccount);
}
and this is the code that actually throws the error:
export const investit = async (thecontract, selectedAccount) => {
if (!isInitialized) {
await helloworld();
}
thecontract.methods
.invest()
.send({ from: selectedAccount, value: 10000 })
.catch(function (err) {
console.log(err);
});
};
I am completely lost, since if I use the normal window.ethereum.request (https://docs.metamask.io/guide/sending-transactions.html#example) to send a transaction, metamask opens up and I can sign it. With the contract call it simply does not work.
Do you know the reason? How can I fix this?
This must be the issue. you are just passing an url:
const rpcURL ="https://ropsten.infura.io/v3/KEY";
instead:
const web3 = new Web3(new Web3.providers.HttpProvider("https://ropsten.infura.io/v3/KEY"))
eth_sendTransaction requires you holding the private key to sign the transaction before broadcasting it to the network. Infura doesn’t maintain any private keys. In order to send a transaction, you need to sign the transaction on your end with your private key.
The way you check providers is not correct. window.ethereum is also a provider which is provided by metamask. provider itself is meaningless, it has to be injected into the new Web3().

WalletConnect transactions Issue with eth_sendTransaction Popup Not Showing

import { WalletConnectConnector } from "#web3-react/walletconnect-connector";
I have used this for connecting walletconnect through QR code and used useWeb3React for connecting to wallet.
Metamask extension is working fine but not able to use walletconnect for transactions.
const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY;
const { createAlchemyWeb3 } = require("#alch/alchemy-web3");
var web3 = createAlchemyWeb3(alchemyKey);
const transactionParameters = [
{
method: "eth_sendTransaction",
params: [
{
to: contractAddress, // Required except during contract publications.
from: acct, // must match user's active address.
data: window.contract.methods.mintNFT(acct, tokenURI).encodeABI(), //make call to NFT smart contract
},
],
},
];
await web3.eth
.sendTransaction(transactionParameters)
.on("receipt", (receipt) => {
console.log(receipt, "receipt");
})
.on("transactionHash", (hash) => {
console.log(hash, "hash");
})
.on("error", (err) => {
console.log(err, "error");
});
The above works fine with metamask extension but not with Walletconnect.
I think the issue is with your web3 provider. Try to get web3 provider from useWeb3React itself.
eg:
const provider = connector.getProvider();
const web3 = new Web3(provider)
use this web3 to send transaction

How to check if Metamask is connected after page refreshing

My dApp have to connect to MetaMask. There are two rude solutions in the docs: make user to click connect btn every time manually or just pop up connection confirmation after page load. I want to implement the only convenient solution: first time user connect manually by clicking the connect btn and interacting with MetaMask popup and then my dApp detect that connection is still established and use this connection. I can't find the solution, but i saw this in other dApps (Capture the ether for example) I use:
import detectEthereumProvider from '#metamask/detect-provider';
const provider = await detectEthereumProvider();
if (provider) {
connect(provider)
} else {
// kind of "Install the MetaMask please!"
}
function connect(provider) {
// How to check if the connection is here
if (//connection established) {
// Show the user connected account address
} else {
// Connect
provider.request({ method: "eth_requestAccounts" })
.then // some logic
}
}
I finally found a possible solution and it turned out to be as simple as it should be. There is an eth_accounts method in Ethereum JSON-RPC which allow us to ask for available accounts without actually requesting them. This way we can check if metamask is still connected (if there are any accounts) and avoid auto requesting or need of manually clicking "connect" every time. Simple example implementation could be:
// detect provider using #metamask/detect-provider
detectEthereumProvider().then((provider) => {
if (provider && provider.isMetaMask) {
provider.on('accountsChanged', handleAccountsChanged);
// connect btn is initially disabled
$('#connect-btn').addEventListener('click', connect);
checkConnection();
} else {
console.log('Please install MetaMask!');
}
});
function connect() {
ethereum
.request({ method: 'eth_requestAccounts' })
.then(handleAccountsChanged)
.catch((err) => {
if (err.code === 4001) {
console.log('Please connect to MetaMask.');
} else {
console.error(err);
}
});
}
function checkConnection() {
ethereum.request({ method: 'eth_accounts' }).then(handleAccountsChanged).catch(console.error);
}
function handleAccountsChanged(accounts) {
console.log(accounts);
if (accounts.length === 0) {
$('#connection-status').innerText = "You're not connected to MetaMask";
$('#connect-btn').disabled = false;
} else if (accounts[0] !== currentAccount) {
currentAccount = accounts[0];
$('#connection-status').innerText = `Address: ${currentAccount}`;
$('#connect-btn').disabled = true;
}
}
Use window.onload to initiate the isConnected() function when the webpage is loaded. The browser console will return a wallet address if it is connected.
window.onload = (event) => {
isConnected();
};
async function isConnected() {
const accounts = await ethereum.request({method: 'eth_accounts'});
if (accounts.length) {
console.log(`You're connected to: ${accounts[0]}`);
} else {
console.log("Metamask is not connected");
}
}
I assume you have already found Metamask docs on Ethereum Provider API. This section specifies three steps you need to do to make your app work:
Detect the Ethereum provider (window.ethereum)
Detect which Ethereum network the user is connected to
Get the user's Ethereum account(s)
Your snippet does the first part - it detects the provider.
As per this section, to detect network you can use the following code
const chainId = await ethereum.request({ method: 'eth_chainId' });
handleChainChanged(chainId);
ethereum.on('chainChanged', handleChainChanged);
function handleChainChanged(_chainId) {
window.location.reload();
}
And the most crucial part - fetching user account.
let currentAccount = null;
function handleAccountsChanged(accounts) {
if (accounts.length === 0) {
console.log('Please connect to MetaMask.');
} else if (accounts[0] !== currentAccount) {
currentAccount = accounts[0];
}
}
document.getElementById('connectButton', connect);
function connect() {
ethereum
.request({ method: 'eth_requestAccounts' })
.then(handleAccountsChanged)
.catch((err) => {
if (err.code === 4001) {
console.log('Please connect to MetaMask.');
} else {
console.error(err);
}
});
After the user logs in the first time, Metamask won't show the pop-up next time.
I think it's help you. In some case you noticedethereum.window.once('connect',()=>{}) is not worked and then disconnect event too.. i also face this problem and i don't know how to get userAccount address automatically after refresh so i started research on many youtube video and metamask api document. finally i got the answer.
import React, {useState,useEffect} from 'react';
import { ethers} from 'ethers';
function App(){
let [userAccount,setUserAccount] = useState({
isConnect:false,
Account:""
})
let isItConnect = async()=>{
let provider = new ethers.providers.Web3Provider(window.ethereum);
let accounts = await provider.send("eth_requestAccounts",[]);
console.log(accounts.length)
if(accounts.length>0){
return {
status:true,
userAddress:accounts[0]
}
}
else{
return {
status:false,
userAddress:""
}
}
}
let connect = async()=>{
let Status = await isItConnect();
localStorage.setItem('isConnected',Status.status)
setUserAccount((prev)=>{
return {...prev,Account:Status.userAddress}
})
}
window.ethereum.on('accountsChanged',async()=>{
localStorage.removeItem('isConnected');
setUserAccount((prev)=>{
return {...prev,Account:""}
})
connect()
})
useEffect(()=>{
let status = localStorage.getItem('isConnected')
if(status){
connect()
}
if(status === null){
if(window.ethereum.selectedAddress === null){
console.log('welcome User!')
}
else{
connect()
}
}
},[])
return (
<>
{userAccount.Account===""&& <button onClick={connect}>Connect Metamask!
</button>}
{userAccount.Account !==""&& <>
<p>{userAccount.Account}</p>
<p>Connected</p>
</>
)
}
Try using window.ethereum._state.account it will show array of accounts if connected else it will show an empty array, and use the length property to further access if connected to metamask or not.
This would get you the wallet address. returns false if not connected.
const getAccount = async () => await window.ethereum.request({method: 'eth_accounts'})[0] || false;
basic call from the DOM:
window.onload = event => {
const account = getAccount();
console.log(account ? `You're connected to: ${accounts}` : 'Metamask is not connected');
};
if using react:
componentDidMount() {
const account = getAccount();
console.log(account ? `You're connected to: ${accounts}` : 'Metamask is not connected');
}

sendMessage() of react-native-firebase/messaging doesn't work in version 6.2.0

I have used react-native-firebase version 6.2.0
When I use react-native-firebase/messaging, I found out any sendMessage() function doesn’t work.
(I use android devices and virtual machine.)
I just follow the document here
At first, I registered remoteNotification and got FCM token from it. => init()
Then, I sent upstream remoteMessage => sendMessage()
But, I could not find out where are the messages. => could not receive any messages in device and in firebase cloud messaging console.
When I sent messages from firebase cloud messaging console, I could got the message at devices.
import messaging from '#react-native-firebase/messaging';
import firestore from '#react-native-firebase/firestore';
import store from 'project3/redux/store.js';
export async function init() {
const enabled = await messaging().hasPermission();
if (enabled) {
console.log('user has permissions');
} else {
console.log('user doesnt have permission');
const enabled2 = await messaging().requestPermission();
if (enabled2) {
console.log('requestPermission');
} else {
console.log('not requestPermission');
}
}
console.log('getToken');
await messaging().registerForRemoteNotifications();
const fcmToken = await messaging().getToken();
const uid = store.getState().email;
console.log('fmcToken : ' + fcmToken);
await firestore()
.doc(`users/${uid}`)
.update({
fcmToken: fcmToken,
});
console.log(
'isRegisteredForRemoteNotifications ' +
messaging().isRegisteredForRemoteNotifications,
);
messaging().onMessage(async remoteMessage => {
console.log('FCM Message Data:', remoteMessage.data);
});
messaging().onSendError(event => {
console.log(event.messageId);
console.log(event.error);
});
}
export async function sendMessage() {
console.log('sendMessage');
await messaging()
.sendMessage({
data: {
loggedIn: Date.now().toString(),
uid: store.getState().email,
},
})
.then(msg => {
console.log(msg);
});
}
Please help me.
I found many cases about below version 5.x.x of react-native-firebase.
But, there are very few cases about 6.x.x and guide isn't also sufficient.
You may save my weeks.

Using Firebase reauthenticate

I'll appreciate assistance with how to reauthenticate a user in Firebase. I wonder if it makes any sense adding all these great features if the documentation doesn't explain how to use it:
Currently, this is what I'm trying, and it ain't working. Errors as cannot read property 'credential' of undefined
In constructor:
constructor(#Inject(FirebaseApp) firebaseApp: any) {
this.auth = firebaseApp.auth();
console.log(this.auth);
}
then the function
changePassword(passwordData) {
if(passwordData.valid) {
console.log(passwordData.value);
// let us reauthenticate first irrespective of how long
// user's been logged in!
const user = this.auth.currentUser;
const credential = this.auth.EmailAuthProvider.credential(user.email, passwordData.value.oldpassword);
console.log(credential);
this.auth.reauthenticate(credential)
.then((_) => {
console.log('User reauthenticated');
this.auth.updatePassword(passwordData.value.newpassword)
.then((_) => {
console.log('Password changed');
})
.catch((error) => {
console.log(error);
})
})
.catch((error) => {
console.log(error);
})
}
}
The reauthenticate() method is called on a firebase.User, not on firebase.auth.Auth itself.
var user = firebase.app.auth().currentUser;
var credentials = firebase.auth.EmailAuthProvider.credential('puf#firebaseui.com', 'firebase');
user.reauthenticate(credentials);
Update (July 2017):
There are some breaking change in the 4.0 version of the Firebase Web SDK. From the release notes:
BREAKING: firebase.User.prototype.reauthenticate has been removed in favor of firebase.User.prototype.reauthenticateWithCredential.
As far as I can tell the reauthenticateWithCredentialis a drop-in replacement for the old method.
Here's some code that enabled users to (a) reauthenticate in Firebase and (b) change their passwords after reauthenticating for me. I researched for about an hour while writing this, so hopefully it saves someone a minute.
Wrote in VueJS:
changePassword() {
let self = this; // i use "self" to get around scope issues
var user = firebase.auth().currentUser;
var credential = firebase.auth.EmailAuthProvider.credential(
this.$store.state.userId, // references the user's email address
this.oldPassword
);
user.reauthenticateWithCredential(credential)
.then(function() {
// User re-authenticated.
user.updatePassword(self.newPassword)
.then(function() {
console.log("Password update successful!");
})
.catch(function(error) {
console.log(
"An error occurred while changing the password:",
error
);
});
})
.catch(function(error) {
console.log("Some kinda bug: ", error);
// An error happened.
});
Slight changes as of May 2019, see more details here. Code is as follows:
var user = firebase.auth().currentUser;
var credential = firebase.auth.EmailAuthProvider.credential(user.email, password);
// Prompt the user to re-provide their sign-in credentials
return user.reauthenticateWithCredential(credential);
Call changeEmail("new email","password") in onPressed directly to update the user email with no reauthentication required error
RaisedButton(
onPressed: () {
changeEmail(_emailController.text, _passwordController.text);
}
Future<void> changeEmail(String email, String password) async {
User user = await FirebaseAuth.instance.currentUser;
print(email);
print(password);
try {
try {
var authResult = await user.reauthenticateWithCredential(
EmailAuthProvider.getCredential(
email: user.email,
password: password,
),
);
user.updateEmail(email).then((_) {
print("Succesfull changed email");
_backthrow();
}).catchError((error) {
showAlertDialog(context, error.message);
print("email can't be changed" + error.toString());
});
return null;
} catch (e) {
print("2");
}
} catch (e) {
print(e.message);
showAlertDialog(context, e.message);
}
}
Hers a full example how to reauthenticate with Firebase
var pass = "abcdefg";
var user = firebase.auth().currentUser;
var credential = firebase.auth.EmailAuthProvider.credential(user.email, pass);
user.reauthenticateWithCredential(credential).then(() => {
console.log("Its good!");
}).catch((error) => {
console.log(error);
});
Since 2021: If you use Firebase JS API 9.x (the tree shakable version) this is the most recent way:
https://cloud.google.com/identity-platform/docs/web/reauth
With credentials
import { getAuth, reauthenticateWithCredential } from "firebase/auth";
const auth = getAuth();
const user = auth.currentUser;
// todo for you: prompt the user to re-provide their sign-in credentials
const credential = promptForCredentials();
reauthenticateWithCredential(user, credential).then(() => {
// ...
}).catch((error) => {
// ...
});
With popup
import { getAuth, reauthenticateWithPopup, OAuthProvider } from "firebase/auth";
const auth = getAuth();
// todo for you: change to appropriate provider
const provider = new OAuthProvider('apple.com');
reauthenticateWithPopup(auth.currentUser, provider)
.then((result) => {
// ...
})
.catch((error) => {
// ...
});
This is how I re-authenticate a user in Firebase:
import { getAuth, EmailAuthProvider, reauthenticateWithCredential } from "firebase/auth";
const auth = getAuth()
const reauthenticateUser = async (email, password) => {
const user = auth.currentUser;
try {
const credential = EmailAuthProvider.credential(email, password);
await reauthenticateWithCredential(user, credential)
} catch (error) {
Alert.alert("Error", "The email or password is incorrect. Please try again.")
}
}
I was getting that re-authentication error auth/requires-recent-login when saving the primary email.
I couldn't figure out how to implement that poorly documented reauthenticateWithCredential(credential) method, so, I simply logged-out the user and redirected to login page. It's a hack but It works like charm!
firebase.auth().signOut();

Categories

Resources