Open umangveerma opened 1 year ago
Okay I've done some digging
On your link, the behaviour I'm seeing is:
Obviously there are a lot of things that could be causing this, so I've done some tests to try to narrow it down a bit
First, here's a codesandbox with two buttons that deeplink into Phantom/Solflare respectively using the same JS as wallet-adapter: https://zfn6gj.csb.app/ . This works correctly, nothing stops me going phantom - back - phantom - back - phantom. So it's probably not a wallet issue, or a limitation of iOS redirects/universal links
Second I modified our starter/example app to add Phantom + Solflare adapters. When I open this with iOS I find:
I think that the behaviour I'm seeing on your site is consistent with that when using the dialog/modal button and selecting Phantom/Solflare.
Without seeing your code it's hard to know exactly what would get the behaviour that you expect, or if there's a bug here.
But speculating a bit, here's what I think is probably happening:
On load, walletName
in WalletProvider.tsx
is being set from localstorage: https://github.com/solana-labs/wallet-adapter/blob/fef26c290398520fa7044e3573fa6ab8637f006f/packages/core/react/src/WalletProvider.tsx#L81-L84 This is why the behaviour is sticky, localstorage stays around when you close and reopen a tab
At some point you're calling selectWallet
: https://github.com/solana-labs/wallet-adapter/blob/fef26c290398520fa7044e3573fa6ab8637f006f/packages/core/react/src/WalletProvider.tsx#L162-L168
This calls changeWallet
with the same walletName
you already have in localstorage (eg Phantom): https://github.com/solana-labs/wallet-adapter/blob/fef26c290398520fa7044e3573fa6ab8637f006f/packages/core/react/src/WalletProvider.tsx#L89-L94
This returns without updating walletName
, which I suspect is what triggers the connect redirect:
if (walletName === nextWalletName) {
return;
}
But even if it did call setWalletName
, that wouldn't trigger anything because it's already set to that (per the localstorage line). This is why it works when you choose Solflare, it changes walletName
and then when you choose Phantom again it works.
But this should only be an issue if there is already a localstorage key containing a previously selected wallet, and you select the same one again.
For apps like yours where storing the wallet name doesn't make sense (since you're not using autoconnect or displaying the connected wallet in the UI), it might make sense for us to add an option to WalletProvider
to disable localstorage?
You can verify this by using Safari to debug your page, if you delete the walletName
localstorage key and refresh it'll work correctly. If you click Phantom/Solflare, then back, you'll see walletName
is set in localstorage. And whichever one that was won't work, but if you delete the localstorage key and refresh then it will.
I also faced that problem.
if I use a dialog/modal button, select Phantom, it redirects correctly. But if I go back, the connect and multi-buttons show the Phantom logo, tapping the dialog/modal again does not redirect. Same with Solflare. But using the connect or multi-button correctly redirects
@mcintyre94 thanks for your dive into it, it really looks logical and makes it clear!
I tested it a bit, and it seems to be a valid point about walletName
in LocalStorage. So if there's no value initially, any wallet will work. Then it will work only if you switch between wallets. But there's also a thing that if you manually delete the value, it won't work anyway, only after the refresh, as you said. Buut is that really expected behavior?
For your understanding that's how I'm using it:
// app.ts
import { ConnectionProvider, WalletProvider } from "@solana/wallet-adapter-react";
import { WalletModalProvider } from "@solana/wallet-adapter-react-ui";
export default function app() {
// ...
const network = WalletAdapterNetwork.Mainnet;
const wallets = useMemo(
() => [
new PhantomWalletAdapter(),
new SolflareWalletAdapter(),
new TorusWalletAdapter(),
new MathWalletAdapter(),
new Coin98WalletAdapter(),
new CloverWalletAdapter(),
new HuobiWalletAdapter(),
new CoinbaseWalletAdapter(),
new BitKeepWalletAdapter(),
new NekoWalletAdapter(),
new TrustWalletAdapter(),
new NightlyWalletAdapter(),
new SalmonWalletAdapter(),
new FoxWalletWalletAdapter(),
],
[network]
);
return (
// ...
<ConnectionProvider>
<WalletProvider wallets={wallets} autoConnect>
<WalletModalProvider>
{/* ... */}
</WalletModalProvider>
</WalletProvider>
</ConnectionProvider>
)
}
And that's my ConnectWallet component that triggers the modal.
// connect wallet button
import { useWalletModal } from "@solana/wallet-adapter-react-ui";
export const ConnectWallet = () => {
// ...
const { setVisible, visible } = useWalletModal();
return (
// ...
<button
onClick={() => {
setVisible(true);
}}
type="button"
>
{connecting ? t`Connecting...` : t`Connect wallet`}
</button>
)
}
@mcintyre94 let me know please if that info helps you identify the problem.
Tbh I'm not sure I can ask you for anything, since seems like you're not the part of team. If I'm correct, please let me know, so I'll understand if I need to address it to someone else or try to fix myself :)
Any updates on this?
I'm stuck here too... My code looks exactly the same as above (with just PhantoWallet). This code works great on a laptop with the Chrome extension but on my iphone it throws my app into the Phantom Wallet app and the modal overlay never triggers off. Here's a link https://deadco.info/sphere (don't worry there's no transactions going on ...) I'm just trying to connect to get the public key so I can send it to my server for a claim. i want to launch this during the Dead and Company shows in Las Vegas.
// pages/_app.js
import React from 'react';
import { WalletModalProvider, WalletMultiButton } from '@solana/wallet-adapter-react-ui';
import { ConnectionProvider, WalletProvider } from '@solana/wallet-adapter-react';
import { PhantomWalletAdapter } from '@solana/wallet-adapter-wallets';
// Import the CSS for the wallet adapter
require('@solana/wallet-adapter-react-ui/styles.css');
const MyApp = ({ Component, pageProps }) => {
const endpoint = "mainnet"; // This can be adjusted to your preferred network
const wallets = [new PhantomWalletAdapter()]; // Add other wallets like Sollet, Ledger, etc.
return (
<ConnectionProvider endpoint={endpoint}>
<WalletProvider wallets={wallets} autoConnect>
<WalletModalProvider>
<Component {...pageProps} />
</WalletModalProvider>
</WalletProvider>
</ConnectionProvider>
);
};
export default MyApp;
Source for the component is here https://github.com/DeadCoSol/deadco-website/blob/main/nextjs-deadco/components/ClaimForm.js
Okay I've done some digging
On your link, the behaviour I'm seeing is:
- If I click Phantom on first visit, it works correctly. If I then tap back, then Phantom doesn't work again
- Same with Solflare
- The "last visited" seems to be sticky across tabs, for example if I open Phantom, go back, close tab, open a new one at the same URL, Phantom still doesn't work
Obviously there are a lot of things that could be causing this, so I've done some tests to try to narrow it down a bit
First, here's a codesandbox with two buttons that deeplink into Phantom/Solflare respectively using the same JS as wallet-adapter: https://zfn6gj.csb.app/ . This works correctly, nothing stops me going phantom - back - phantom - back - phantom. So it's probably not a wallet issue, or a limitation of iOS redirects/universal links
Second I modified our starter/example app to add Phantom + Solflare adapters. When I open this with iOS I find:
- if I use a multi-button, select Phantom, it redirects correctly. If I go back, the connect and multi-buttons shows the Phantom logo, tapping it again redirects correctly. Same with Solflare
- if I use a dialog/modal button, select Phantom, it redirects correctly. But if I go back, the connect and multi-buttons show the Phantom logo, tapping the dialog/modal again does not redirect. Same with Solflare. But using the connect or multi-button correctly redirects
I think that the behaviour I'm seeing on your site is consistent with that when using the dialog/modal button and selecting Phantom/Solflare.
Without seeing your code it's hard to know exactly what would get the behaviour that you expect, or if there's a bug here.
But speculating a bit, here's what I think is probably happening:
- On load,
walletName
inWalletProvider.tsx
is being set from localstorage: https://github.com/solana-labs/wallet-adapter/blob/fef26c290398520fa7044e3573fa6ab8637f006f/packages/core/react/src/WalletProvider.tsx#L81-L84 This is why the behaviour is sticky, localstorage stays around when you close and reopen a tab- At some point you're calling
selectWallet
: https://github.com/solana-labs/wallet-adapter/blob/fef26c290398520fa7044e3573fa6ab8637f006f/packages/core/react/src/WalletProvider.tsx#L162-L168- This calls
changeWallet
with the samewalletName
you already have in localstorage (eg Phantom): https://github.com/solana-labs/wallet-adapter/blob/fef26c290398520fa7044e3573fa6ab8637f006f/packages/core/react/src/WalletProvider.tsx#L89-L94- This returns without updating
walletName
, which I suspect is what triggers the connect redirect:if (walletName === nextWalletName) { return; }
But even if it did call
setWalletName
, that wouldn't trigger anything because it's already set to that (per the localstorage line). This is why it works when you choose Solflare, it changeswalletName
and then when you choose Phantom again it works.But this should only be an issue if there is already a localstorage key containing a previously selected wallet, and you select the same one again.
For apps like yours where storing the wallet name doesn't make sense (since you're not using autoconnect or displaying the connected wallet in the UI), it might make sense for us to add an option to
WalletProvider
to disable localstorage?You can verify this by using Safari to debug your page, if you delete the
walletName
localstorage key and refresh it'll work correctly. If you click Phantom/Solflare, then back, you'll seewalletName
is set in localstorage. And whichever one that was won't work, but if you delete the localstorage key and refresh then it will.
I can't get the example to run. If I do an npm install it complains about 'workspace:' not being set I see it's used in the package.json but do I need to set it for each package?
Faced the same issue, which complicated stuff in my dapp. Read through the whole tread and the localStorage
thing is true. Basically, Phantom acts different from Solflare (only tested those two for now), and in a scenario where both wallets are in a locked state, Solflare would show a popup, library would write the value of Solflare
to walletName
key or whatever you provided as a property localStorageKey
to <WalletProvider />
, but if you never unlocked Solflare wallet and just closed the popup it will erase the walletName
key from local storage, while in Phantom case even if you closed the modal popup without actually unlocking the wallet the key in local storage stays set to Phantom
, which is why you are unable to press the button for the second time.
Have to update you on my last comment, this is not the case, I implemented a fix in my dapp which removes walletName
after pressing the button on iOS but when I came back to the browser's tab my button was still not responding to my clicks.
Ok, so I actually found out the issue, it's here:
if (this._readyState !== WalletReadyState.Unsupported) {
if (isIosAndRedirectable()) {
// when in iOS (not webview), set Phantom as loadable instead of checking for install
this._readyState = WalletReadyState.Loadable;
this.emit('readyStateChange', this._readyState);
} else {
scopePollingDetectionStrategy(() => {
if (window.phantom?.solana?.isPhantom || window.solana?.isPhantom) {
this._readyState = WalletReadyState.Installed;
this.emit('readyStateChange', this._readyState);
return true;
}
return false;
});
}
}
What this does is basically it sets the connecting
state of Phantom to true
and, as far as I understood, never sets it to false afterwards, so here is what I did to make it work on iOS:
walletsToCheck.forEach(({ name, globalObject }) => {
if (localStorageWallet === name && globalObject === undefined) {
localStorage.removeItem('walletName')
const wallet = wallets.find((wallet) => wallet.adapter.name === name)
if (wallet) {
wallet.adapter.disconnect()
}
}
})
in Phantom case:
{ name: 'Phantom', globalObject: window.phantom }
What's sad is that it still doesn't work for Android as Phantom only has the isIosAndRedirectable
check, which in our case is false
and we fallback to Phantom button doing nothing.
Describe the bug Trying to connect Phantom wallet in safari browser on mobile, and Phantom's in-app browser is not opening. I'm required to click Solflare from the list of wallets (which is not installed), redirected to Solflare's website and on trying again after coming back to the initial page Phantom as an option works fine.
To Reproduce Steps to reproduce the behavior:
Note - If Phantom works for you in the first try and the payment page opens up in it's in-app browser, please come back to the payment page in your browser and try clicking on Phantom again and it will not work in the second try, which is unexpected behaviour and not great for UX in case user wasn't able to connect wallet in first try for some reason.
Screenshots https://github.com/solana-labs/wallet-adapter/assets/91828247/4385eb0b-658a-411a-834e-622a561911d9
Smartphone