window.solana not found in web3js - javascript

I want to connect a Solana wallet (phantom or any other) to a web application through the web3js library. I've read docs for most wallets and it seems like it's just as simple as await window.solana.request({ method: "connect" }); but window.solana is undefined in my case.
When I do console.log(window) I can see the Solana value with all its corresponding keys and values.
How can I do this?

I've found a working code that solved my issue. I am not sure what was the issue as I'm not very experienced with js, but the following code lets me connect to phantom.
I found this on StackOverflow on a similar thread, although I belive the original answer is missing some brackets.
Solana : Adding Sollet / Phantom Wallet Connect to my website - Steps?
const getProvider = async () => {
if ("solana" in window) {
await window.solana.connect(); // opens wallet to connect to
const provider = window.solana;
if (provider.isPhantom) {
console.log("Is Phantom installed? ", provider.isPhantom);
return provider;
}
} else {
document.write('Install https://www.phantom.app/');
}
};
window.onload = () => {
getProvider().then(provider => {
console.log('key', provider.publicKey.toString())
})
.catch(function(error){
console.log(error)
});
}

With your current implementation, everytime you refresh the app, you will get pop up to connect to the wallet. Instead you add {onlyIfTrusted:true} option to connect.
const getProvider = async () => {
if ("solana" in window) {
await window.solana.connect({onlyIfTrusted:true}); // opens wallet to connect to
const provider = window.solana;
if (provider.isPhantom) {
console.log("Is Phantom installed? ", provider.isPhantom);
return provider;
}
} else {
document.write('Install https://www.phantom.app/');
}
};
then instead of getting pop up when you reload the app, write a connection function to handle the connection when a user clicks on the button
const connectToWallet=async ()=>{
const {solana}=window
if(solana){
const response=await solana.connect()
console.log('address',response.publicKey.toString())
}
}
<button onClick={connectToWallet} >
Connect to Wallet
</button>
Now once user is connected, when you reload the app, it you wont get pop up to connect to the wallet

Is your website https enabled? If not then it won't work

Related

How to do 'signTransaction' after authenticated by 'signPersonalMessage' by WalletConnect of '#walletconnect/react-native-dapp'?

I am developing a react native mobile app where user can connect their existing wallet(rainbow, metamask and many more) to app.
I have already done the authentication part, like user can successfully connect their app wallet to app by this code.
import { useWalletConnect } from '#walletconnect/react-native-dapp';
const connector = useWalletConnect();
await connector.connect();
const message = `App name is XYZ - ${new Date().toUTCString()}`;
const hexMsg = convertUtf8ToHex(message);
const address = connector.accounts[0];
await setItemToStorage('address', address);
const msgParams = [hexMsg, address];
connector.signPersonalMessage(msgParams).then(async result => {
let data = {
message,
signature: result,
};
Now every thing is working as expected.
And then I have to transfer my wallet amount to other address, and to achieve this I know I have to get permission from wallet app,
To achieve this I am trying to do like
let txObj = {
gas: Web3js.utils.toHex(100000),
to: receiver_address!,
data: data,
from: userWallet,
};
console.log('Start transaction...');
const singTrasaction = await connector.signTransaction(txObj);
The connector.signTransaction(txObj) open the wallet app but not prompted me to confirm.
I am just confused and nothing get help me.
Please help me anyone I am getting stuck on this since a week.

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');
}

React Native useEffect works on file save

I am developing a video chat app using react-native-voximplant, everything works, but when the app loads first time, the login functionality is happening inside the useEffect. So, the problem is, when my HomeScreen mounts, the useEffects' should fire and I should get logged in voximplant sdk and now if I tap on call icon, the following is shown in the console:
When HomeScreen mounts:
LOG Client: emit: no handlers for event: AuthResult
LOG Error while trying to login to voximplant: {"code": 491, "name": "AuthResult", "result": false}
LOG Client: emit: no handlers for event: ConnectionEstablished
Now when I tap on call to make a call:
// after a second...
WARN Possible Unhandled Promise Rejection (id: 0):
"NOT_LOGGED_IN"
Now when I hit save (ctrl+s), I get logged in and now if I make a call, it works! I don't know what I am doing wrong here. Is something wrong with useEffect or I'm doing something wrong?
The HomeScreen code:
import {Voximplant} from 'react-native-voximplant';
const HomeScreen = () => {
const voximplant = Voximplant.getInstance();
useEffect(() => {
const signInToVoximplant = async () => {
try {
const fqUsername = '...'; // credentials are correct, don't worry about them.
const password = '...';
await voximplant.login(fqUsername, password);
} catch (error) {
console.log('Error while trying to login to voximplant: ', error);
}
};
signInToVoximplant();
}, []);
useEffect(() => {
const connect = async () => {
const status = await voximplant.getClientState();
if (status === Voximplant.ClientState.DISCONNECTED) {
await voximplant.connect();
} else if (status === Voximplant.ClientState.LOGGED_IN) {
console.log('[INFO] VOXIMPLANT CONNECTED');
return;
}
};
connect();
}, []);
}
return ...
The call will only work if I hit ctrl+s i.e. save the file, otherwise it will throw Unhandled Promise rejection... because it is not logged in, however I have written/provided the login inside the useEffect.
According to your logs, when the application starts, login is failed with 491 error that means that the client is in invalid state. It happens because you invoke login API before the connection the Voximplant Cloud is established (connect API).
When the app is reloaded, native code is not reinitialised (but js does) and actually the client is still connected to the Voximplant Cloud, that's why login is successful the second time.
To fix the issue, you need to change the order of Voximplant SDK API invocation: first call Client.connect API and then Client.login.
It is also better to move connect/login to the a single useEffect.
Both useEffect is run on mounted, but ¿whichone first?.
Try put both functions in the same useEffect, and call them in order:
useEffect(() => {
const signInToVoximplant = async () => {
...
};
const connect = async () => {
...
};
signInToVoximplant();
connect();
}, []);

Connect to MetaMask via chrome extension

I am making a chrome extension that requires MetaMask authentication.
I started developing it as a web application, but as a chrome extension, it doesn't detect MetaMask...
This is my web application code right now:
function toggleButton() {
const loginButton = document.getElementById("login-button");
if (!window.ethereum) {
loginButton.innerText = "Install MetaMask";
loginButton.addEventListener("click", () => {
window.open("https://metamask.io");
});
return false;
}
loginButton.addEventListener("click", loginWithMetaMask);
}
async function loginWithMetaMask() {
const accounts = await window.ethereum.request({ method: "eth_requestAccounts" }).catch((e) => {
console.error(e.message);
return;
});
if (!accounts) {
return;
}
const userWallet = document.getElementById("user-wallet");
userWallet.innerText = accounts[0];
}
window.addEventListener("DOMContentLoaded", toggleButton);
Is there a way to migrate the web application to a chrome extension?
This is how to do it.
Install metamask-extension-provider in your project
https://github.com/MetaMask/extension-provider
You will need to use browserify to compile your plugin.
Create a custom connector like this
class customConnector extends Moralis.AbstractWeb3Connector {
async activate() {
const provider = createMetaMaskProvider();
if (!provider) {
console.error("MetaMask provider not detected.");
throw new Error("MetaMask provider not detected.");
}
const [accounts, chainId] = await Promise.all([
provider.request({
method: 'eth_requestAccounts',
}),
provider.request({ method: 'eth_chainId' }),
]);
const account = accounts[0] ? accounts[0].toLowerCase() : null;
this.chainId = provider.chainId;
this.account = provider.selectedAddress;
this.provider = provider;
this.subscribeToEvents(provider);
return { provider, chainId, account };
}
}
Now you can authenticate using MetaMask like this
Moralis.authenticate({ connector: customConnector })
I have created a small boilerplate of chrome extension with react and added metamask login and web3 support.
https://github.com/shaheem-khanzada/chrome-extension-react-metamask-boilerplate
so the problem is we cannot access window.eth... directly in the extension that is why metamask created a module called metamask-extension-provider but in order to run this we need to use browserify
compiler so in simple words, we need to write our own compiler that's why I created this boilerplate to help others it uses gulp to create task and then bundle files using browserify and babelify

websocket object is not connecting the socket in react native

Hy, I'm trying to connect WebSocket in react-native but it's not connected on the first attempt. I have two screen users and a chat screen. On the users screen when I click on the user it navigates to chat screen.
According to the documentation of WebSocket
In order to communicate using the WebSocket protocol, you need to create a WebSocket object; this will automatically attempt to open the connection to the server.
I created the object like this:
const client = new W3CWebSocket(
'ws://url:port/api/ws/chat/1/' + logID + '/',
);
and complete look like this
const client = new W3CWebSocket(
'ws://url:port/api/ws/chat/1/' + logID + '/',
);
useFocusEffect(
useCallback(() => {
getID()
client.onopen = () => {
console.log('WebSocket Client Connected');
};
client.onmessage=(message)=>{
const dataFromServer = JSON.parse(message.data);
console.log('got reply! ', dataFromServer);
if(dataFromServer){
setMessages(messages => [
...messages,
{msg: dataFromServer.type, sender: dataFromServer.sender},
]);
}
}
scrollHandler();
return () => {
scrollHandler();
client.close()
};
}, []),
);
But it does not connect to the WebSocket, but when I do some changes on my code or say when I save the code file after opening the chat screen, it is connected. That's means when I navigate the screens from user->chat it does not connect but on coming to the chat screen I go back to vscode and code of chat screen just press ctrl+s it connects the socket. I also try it inside the useEffect or useFocusEffect but it still behaves the same.
How I can solve this problem?

Categories

Resources