Hello Stack Overflow community,
I have developed two separate applications using Next.js and Web3, one for MetaMask and another for TrustWallet. However, I'm encountering a problem where attempting to connect with MetaMask results in the TrustWallet being triggered instead. Additionally, after installing TrustWallet, a lot of unnecessary data gets saved to my localStorage, which was not the case when TrustWallet was not installed.
Issue Details:
Environment:
LocalStorage Behavior:
Strange Behavior
MetaMask and TrustWallet Code Sample
import { useEffect, useState } from "react";
import Web3 from 'web3';
import { useLocalStorage } from "@/hooks/auth/useLocalStorage";
const TrustWalletOnlyApp = () => {
const [account, setAccount] = useLocalStorage("eth_trustwallet_account", null);
const [trustConnected, setTrustConnected] = useLocalStorage("eth_trustwallet_connected", false);
const [walletInstalled, setWalletInstalled] = useState(false);
const [balance, setBalance] = useState(null);
const [isClient, setIsClient] = useState(false);
console.log(walletInstalled, trustConnected, account, balance);
useEffect(() => {
setIsClient(true); // Marcar que estamos no cliente
const checkWalletAvailability = async () => {
if (typeof window.ethereum !== "undefined" && window.ethereum.isTrust) {
setWalletInstalled(true);
const accounts = await window.ethereum.request({ method: 'eth_accounts' });
if (accounts.length > 0) {
setAccount(accounts[0]);
getBalance(accounts[0]);
setTrustConnected(true);
} else {
setTrustConnected(false);
}
} else {
setWalletInstalled(false);
setTrustConnected(false);
}
};
checkWalletAvailability();
}, []);
const onWalletDisconnect = () => {
setAccount(null);
setBalance(null);
setTrustConnected(false);
window.localStorage.removeItem("eth_trustwallet_account");
};
const onConnectClick = async () => {
if (window.ethereum && window.ethereum.isTrust) {
const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
if (accounts.length > 0) {
setAccount(accounts[0]);
getBalance(accounts[0]);
setTrustConnected(true);
window.localStorage.setItem("eth_trustwallet_account", accounts[0]);
}
} else {
console.log("Trust Wallet is not installed");
}
};
const getBalance = async (account) => {
const web3 = new Web3(window.ethereum);
const balance = await web3.eth.getBalance(account);
setBalance(web3.utils.fromWei(balance, 'ether'));
};
if (!isClient) {
return null;
}
return (
<div style={{ padding: 30 }}>
<h1>Trust Wallet Connect Test App</h1>
<div>
{trustConnected ? (
<div>
<h3>Trust Wallet Account</h3>
<div>Address: {account}</div>
<div>Balance: {balance} ETH</div>
</div>
) : (
<div>
<h3>No account connected</h3>
</div>
)}
</div>
<div style={{ background: "lightgray", padding: 30, marginTop: 10 }}>
<button style={{ height: 30, width: 180, marginLeft: 10 }} onClick={onConnectClick}>
Connect Trust Wallet
</button>
</div>
<div>
<button onClick={onWalletDisconnect}>Disconnect</button>
</div>
</div>
);
};
export default TrustWalletOnlyApp;
import { useState, useEffect } from 'react';
import Web3 from 'web3';
import { useLocalStorage } from "@/hooks/auth/useLocalStorage";
const MetamaskOnlyApp = () => {
const [account, setAccount] = useLocalStorage("eth_metamask_account", null);
const [metamaskConnected, setMetamaskConnected] = useLocalStorage("eth_metamask_connected", false);
const [metamaskInstalled, setMetamaskInstalled] = useState(false);
const [balance, setBalance] = useState(null);
const [isClient, setIsClient] = useState(false);
console.log(metamaskInstalled, metamaskConnected, account, balance);
useEffect(() => {
setIsClient(true); // Marcar que estamos no cliente
const checkMetamaskAvailability = async () => {
if (typeof window !== "undefined" && window.ethereum && window.ethereum.isMetaMask) {
setMetamaskInstalled(true);
const accounts = await window.ethereum.request({ method: 'eth_accounts' });
if (accounts.length > 0) {
setAccount(accounts[0]);
getBalance(accounts[0]);
setMetamaskConnected(true);
} else {
setMetamaskConnected(false);
}
} else {
setMetamaskInstalled(false);
setMetamaskConnected(false);
}
};
checkMetamaskAvailability();
}, []);
const onWalletDisconnect = () => {
setAccount(null);
setBalance(null);
setMetamaskConnected(false);
window.localStorage.removeItem("eth_metamask_account");
};
const onConnectClick = async () => {
if (window.ethereum && window.ethereum.isMetaMask) {
const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
if (accounts.length > 0) {
setAccount(accounts[0]);
getBalance(accounts[0]);
setMetamaskConnected(true);
window.localStorage.setItem("eth_metamask_account", accounts[0]);
}
} else {
console.log("MetaMask is not installed");
}
};
const getBalance = async (account) => {
const web3 = new Web3(window.ethereum);
const balance = await web3.eth.getBalance(account);
setBalance(web3.utils.fromWei(balance, 'ether'));
};
if (!isClient) {
return null;
}
return (
<div style={{ padding: 30 }}>
<h1>MetaMask Connect Test App</h1>
<div>
{metamaskConnected ? (
<div>
<h3>MetaMask Account</h3>
<div>Address: {account}</div>
<div>Balance: {balance} ETH</div>
</div>
) : (
<div>
<h3>No account connected</h3>
</div>
)}
</div>
<div style={{ background: "lightgray", padding: 30, marginTop: 10 }}>
<button style={{ height: 30, width: 180, marginLeft: 10 }} onClick={onConnectClick}>
Connect MetaMask
</button>
</div>
<div>
<button onClick={onWalletDisconnect}>Disconnect</button>
</div>
</div>
);
};
export default MetamaskOnlyApp;
LocalStorage Output:
Before TrustWallet Installation:
{
"eth_metamask_account": "0xb5c6a848...6e49475d11",
"eth_metamask_connected": "true"
}
After TrustWallet Installation (same address for both wallets, the trustwallet address):
{
"binance-http://localhost:3000": "{}",
"eth_trustwallet_account": "0xb5c6a84.....A7cF6E49475d11",
"eth_metamask_account": "0xb5c6a84......A7cF6E49475d11",
"ethereum-http://localhost:3000": "{\"address\":\"0xb5c6a848f23d92d67e68b4fc1da7cf6e49475d11\",\"chainId\":\"0x1\",\"networkVersion\":1}",
"eth_trustwallet_connected": "true",
"trust:cache:timestamp": "{\"timestamp\":1717892710717}",
"eth_metamask_connected": "true",
"loglevel": "SILENT"
}
Questions:
Any insights or solutions would be greatly appreciated. Thank you!
Detecting multiple providers can be a problem because of conflicts.
EIP-6963 aims to solve that.
const [metaMaskProvider, setMetaMaskProvider] = useState(null)
const [trustWalletProvider, setTrustWalletProvider] = useState(null)
useEffect(() => {
window.addEventListener("eip6963:announceProvider", (event) => {
const provider = event.detail.provider
if (provider.isMetaMask) {
setMetaMaskProvider(provider)
}
if (provider.isTrust) {
setTrustWalletProvider(provider)
}
})
window.dispatchEvent(new Event("eip6963:requestProvider"))
}, [])
const isMetaMaskInstalled = !!metaMaskProvider
const isTrustWalletInstalled = !!trustWalletProvider