Dapp 连接TrustWallet钱包签名返回error:call backid 页面刷新
由于一开始钱包的连接使用的是ethers库,它兼容一般的钱包连接MetaMask bitkeep 等。后来没有单独使用其他的连接方式,使用了metaMask原有的方式连接trustWallet钱包。惊奇的发现能连接上,以为能使用原有的方法,但是在手机端的Dapp上连接签名是正常的,签名的时候也能吊起手机的签名页面,就是签名之后会在debug模式下发现会返回一个错误,就是无法获取签名之后的数据。
最让人诡异的事情是trustWallet 谷歌扩展钱包插件是可以正常连接钱包并且签名的,可以获取签名之后的数据(我们的登录是连接钱包之后需要签名才能进行登录)。于是我就陷入的困境是哪里不对么,最后修改了很多在其他钱包里面都正常签名使用,在pc端也可以,但是就在手机上的Dapp出现错误。百思不得其解。
后来我去查看了一下trustWallet的官方文档 https://developer.trustwallet.com/developer/develop-for-trust 文档里面有这样一段话
这句话告诉我们每个钱包都会在浏览器有自己实例,我们需要获取用户使用的对应钱包的实例才能正确操作钱包的各个功能完成对应的交互。之后我就引入了对应的操作方法。
import { ethers } from 'ethers'; export function getTrustWalletFromWindow() { const isTrustWallet = (ethereum: { isTrust: any; }) => { // Identify if Trust Wallet injected provider is present. const trustWallet = !!window.ethereum.isTrust; return trustWallet; }; const injectedProviderExist = typeof window !== "undefined" && typeof window.ethereum !== "undefined"; if (!injectedProviderExist) { return null; } if (isTrustWallet(window.ethereum)) { return window.ethereum; } if (window.ethereum?.providers) { return window.ethereum.providers.find(isTrustWallet) ?? null; } return window["trustwallet"] ?? null; } export async function getTrustWalletInjectedProvider({ timeout } = { timeout: 3000 }) { const provider = await getTrustWalletFromWindow(); if (provider) { return provider; } return listenForTrustWalletInitialized({ timeout }) } async function listenForTrustWalletInitialized( { timeout } = { timeout: 3000 } ) { return new Promise((resolve) => { const handleInitialization = () => { resolve(getTrustWalletFromWindow()); }; window.addEventListener("trustwallet#initialized", handleInitialization, { once: true, }); setTimeout(() => { window.removeEventListener("trustwallet#initialized", handleInitialization); resolve(null); }, timeout); }); } export async function connetTrust() { try { const injectedProvider = await getTrustWalletInjectedProvider(); const account = await injectedProvider.request({ method: "eth_requestAccounts", }); injectedProvider.addListener("chainChanged", out); injectedProvider.addListener("accountsChanged",out); console.log(account); // => ['0x...'] return account } catch (e) { console.log(e, 'www') if (e.code === 4001) { console.error("User denied connection."); } } } export async function getTrustProvider() { try { const injectedProvider = await getTrustWalletInjectedProvider(); const ethersProvider = new ethers.providers.Web3Provider(injectedProvider); return ethersProvider; } catch (error) { Toast.clear(); console.log(error, 'error') } } export async function getTrustAccount() { const trustWallet = await getTrustWalletInjectedProvider(); const accounts = await trustWallet.request({ method: "eth_accounts", }); return accounts } export async function switchTrustChainId ({chainId,chainName,rpcUrls}) { //这个方法需要最新的EIP3326支持如果不支持把钱包升级到最新的版本即可 const trustWallet = !!window?.ethereum?.isTrust; if (trustWallet) { const injectedProvider = await getTrustWalletInjectedProvider(); try { await injectedProvider.request({ method: 'wallet_switchEthereumChain', params: [{ chainId }], }); return true; } catch (switchError) { console.log(switchError) // This error code indicates that the chain has not been added to MetaMask. if (switchError.code === 4902) { try { await injectedProvider.request({ method: 'wallet_addEthereumChain', params: [ { chainId, chainName, rpcUrls }, ], }); } catch (addError) { // handle "add" error } } return switchError.code=== -32601 ? true : false // handle other "switch" errors } } }
export async function addListener(){ const injectedProvider = getTrustWalletFromWindow() injectedProvider.addListener("chainChanged", out); injectedProvider.addListener("accountsChanged", out); }
引入之后,感觉肯定可以了万事大吉,可是奇怪的地方出现了,仍还是获取不到对应的签名数据。啊,什么情况,见鬼了?难道做的事无用功么,后来冷静下来,开始排查错误,到底是哪一行造成了这个情况。
经过了漫长的排错后返现是metaMask的一个连接钱包的方法,把它注释了发现竟然好了,这是什么情况。
后来在不断观察TrustWallet钱包吊起签名的状态,发现返回错误的时候页面会在吊起签名的同时刷新页面,把那个链接钱包去掉后就不会刷新。但是实际情况又不能去掉。因为我们的dapp是需要使用在多个钱包中的,如果为了TrustWallet去掉其他的钱包链接是丢了西瓜捡芝麻的事情。最后翻看了trustWallet的开发文档也没有找到这个问题。后来分析和实践了一下,因为我们在TrustWallet中使用了不属于它的注入Provider,引发了这个错误刷新。因为我们的dapp在进入的时候为了提高用户的体验会自动连接钱包,然后执行监听钱包的链和地址的变化。
正式因为监听以前一进来就执行的,没有判断相应的环境所以会导致trustWallet报错解决方法:
1.在主动链接钱包之前需要判断当前是不是TrustWallet钱包,不是的话再执行其他操作
const trustWallet = !!window.ethereum.isTrust;
2.在用户链接钱包之后或者判断是不是TrustWallet钱包再执行对应的监听
const trustWallet = !!window.ethereum?.isTrust; if (!trustWallet) { //其他钱包的监听 }
3.无论是获取钱包的provider还是切换链,都需要判断当前钱包的环境。这样才不会出现奇怪的现象
总结:我们写代码的时候一定要看官方文档,严格遵循文档的方法。不要感觉其他的方法也可以就忽略了,不然很可能出现我们意想不到的情况。