Uniswap / web3-react

A simple, maximally extensible, dependency minimized framework for building modern Ethereum dApps
https://web3-react-mu.vercel.app/
GNU General Public License v3.0
5.55k stars 1.52k forks source link

[V8] Provider block on first connector defined #609

Closed FabienCoutant closed 2 years ago

FabienCoutant commented 2 years ago

Hi guys, I'm using web3-react v8 versions and decided to add the Network connector setup in order to fetch some basic data before the user login. However, I'm struggling on an issue with my provider that get block on my first connector define in my array (Metamask in my cases) and return as ChainId undefined when user isn't login.

Here my setup:

Web3-react versions used:

  "dependencies": {
    "@coinbase/wallet-sdk": "^3.3.0",
    "@walletconnect/ethereum-provider": "1.7.1",
    "@web3-react/coinbase-wallet": "^8.0.33-beta.0",
    "@web3-react/core": "^8.0.33-beta.0",
    "@web3-react/metamask": "^8.0.26-beta.0",
    "@web3-react/network": "^8.0.26-beta.0",
    "@web3-react/types": "^8.0.19-beta.0",
    "@web3-react/walletconnect": "^8.0.34-beta.0",
    "eth-ens-namehash": "^2.0.8",
    "ethereum-blockies": "^0.1.1",
    "react-use": "^17.3.2"
  }

My connectors setup:

import { Web3ReactHooks } from '@web3-react/core';
import { CoinbaseWallet } from '@web3-react/coinbase-wallet';
import { MetaMask } from '@web3-react/metamask';
import { WalletConnect } from '@web3-react/walletconnect';
import { Network } from '@web3-react/network';

import { injected, injectedHooks } from './metaMask';
import { walletConnect, walletConnectHooks } from './walletConnect';
import { coinbaseWallet, coinbaseWalletHooks } from './coinbaseWallet';
import { network, networkHooks } from './network';

const RPC = {
  [ChainId.mainnet]: ['https://cloudflare-eth.com'],
  [ChainId.polygon]: ['https://polygon-rpc.com/'],
  [ChainId.mumbai]: ['https://rpc-mumbai.matic.today'],
  [ChainId.bsc]: ['https://bsc-dataseed.binance.org/'],
  [ChainId.bscTestnet]: ['https://data-seed-prebsc-1-s1.binance.org:8545/'],
};

const onError = (error: Error) => {
  console.debug(`web3-react error: ${error}`);
};

const [network, networkHooks] = initializeConnector<Network>(
  (actions) =>
    new Network({ actions, urlMap: RPC, defaultChainId: ChainId.mumbai })
);

const [injected, injectedHooks] = initializeConnector<MetaMask>(
  (actions) => new MetaMask({ actions, onError })
);
...
export const connectors: [
  MetaMask | WalletConnect | CoinbaseWallet | Network,
  Web3ReactHooks
][] = [
  [injected, injectedHooks],
  [walletConnect, walletConnectHooks],
  [coinbaseWallet, coinbaseWalletHooks],
  [network, networkHooks],
];

export type ConnectorType = typeof connectors;
export { walletConnect, walletConnectHooks };
export { injected, injectedHooks };
export { coinbaseWallet, coinbaseWalletHooks };
export { network, networkHooks };

My _app.tsx file:

function CustomApp({ Component, pageProps }: AppProps) {
  const store = useStore(pageProps.initialReduxState);

  const isWindowVisible = useIsWindowVisible();
  if (!isWindowVisible) return null;

  return (
    <>
      <Head>
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0, viewport-fit=cover"
        />
      </Head>
      <StateProvider store={store}>
        <Suspense fallback={<div>Loading</div>}>
          <AppThemeProvider>
            <Web3ReactProvider connectors={connectors}>
            ....
            </Web3ReactProvider>
        </AppThemeProvider>
      </Suspense>
    </StateProvider>
  </>
  );
}

And when I console.log the chainId and the connector without being login I'm getting this:

  const { chainId, connector } = useWeb3React();
  console.log({ chainId }, { connector });
Capture d’écran 2022-07-12 à 15 01 13

I checked the example-next and also the uniswap/interface setup didn't see any diff with my setup :/

I also tried to defined the Network connector as the first one. I'm getting this :

export const connectors: [
  MetaMask | WalletConnect | CoinbaseWallet | Network,
  Web3ReactHooks
][] = [
  [network, networkHooks],
  [walletConnect, walletConnectHooks],
  [coinbaseWallet, coinbaseWalletHooks],
  [injected, injectedHooks],
];
Capture d’écran 2022-07-12 à 16 17 36

No idea on what going on :/

FabienCoutant commented 2 years ago

Here an update. I'm also able to reproduce the issue with the example-next packages just by doing this:

Just updated the ProviderExample.ts to display also the chainId as bellow:

const connectors: [MetaMask | WalletConnect | CoinbaseWallet | Network, Web3ReactHooks][] = [
    [metaMask, metaMaskHooks],
    [walletConnect, walletConnectHooks],
    [coinbaseWallet, coinbaseWalletHooks],
    [network, networkHooks],
]

function Child() {
    const {connector, chainId} = useWeb3React()
    console.log(`Priority Connector is: ${getName(connector)} on chainId: ${ chainId }`)
    return null
}

export default function ProviderExample() {
    return (
        <Web3ReactProvider connectors={connectors}>
            <Child/>
        </Web3ReactProvider>
    )
}

And in the pages/index.tsx I just comments every component except the Provider as below:


export default function Home() {
  return (
    <>
      <ProviderExample />
         {/*<div style={{ display: 'flex', flexFlow: 'wrap', fontFamily: 'sans-serif' }}>*/}
        {/*<MetaMaskCard />*/}
        {/*<WalletConnectCard />*/}
        {/*<CoinbaseWalletCard />*/}
         {/*<NetworkCard />*/}
        {/*<GnosisSafeCard />*/}
       {/*</div>*/}
    </>
  )

And I'm getting this

Capture d’écran 2022-07-13 à 09 38 05

I need to uncomment the NetworkCard component to get what I expect

FabienCoutant commented 2 years ago

I found the solution to my problem.

I have to override the Web3ReactProvider to a new component that try to eagerly connect the network connector as below :

const connect = async (connector: Connector) => {
  try {
    if (connector.connectEagerly) {
      await connector.connectEagerly();
    } else {
      await connector.activate();
    }
  } catch (error) {
    console.debug(`web3-react eager connection error: ${error}`);
  }
}

export const Web3Provider = ({ children }: { children: ReactNode }) => {
    useEffect(() => {
    connect(network);
  }, []);
  return (
    <Web3ReactProvider connectors={connectors}>{children}</Web3ReactProvider>
  );
};

I think the Network connector should have a eagerConnection props in his constructor

vm commented 2 years ago

Ah yeah, we'll update the examples to explain this. I'm guessing you got the code snippet from the uniswap/interface repo.

0xD4V1NC1 commented 2 years ago

I have a similar issue I think... I'm having trouble trying to persist the WC and Coinbase connectors. Whenever a hard refresh is done, the app wants to use Metamask as the connector instead of remembering the previously used connector

vm commented 2 years ago

@0xD4V1NC1 web3-react won't remember the last used connector, you'll need to handle that logic yourself using some type of cached state.

FabienCoutant commented 2 years ago

Ah yeah, we'll update the examples to explain this. I'm guessing you got the code snippet from the uniswap/interface repo.

Yeah I checked the Uniswap/interface repo. But in my mind this connector should be on eagerConnection to true by default. I don't see any case where you prefer to use the Network connector instead of something else.

I have a similar issue I think... I'm having trouble trying to persist the WC and Coinbase connectors. Whenever a hard refresh is done, the app wants to use Metamask as the connector instead of remembering the previously used connector

You should check how Uniswap/interface handle this: https://github.com/Uniswap/interface/blob/6a1506ade682b35885271dad49b0d1720768cecb/src/components/Web3Provider/index.tsx

vm commented 2 years ago

@FabienCoutant there are cases where users server-side render their app, in which case you need to handle it in an effect. What connectEagerly is doing is suppressing any prompts that appear (example: MetaMask connect window). If the window shows up that means the user hasn't previously authorized and we fail. For network connector there's no prompt, so there's no reason to have a separate connectEagerly function.

This seems to be confusing some people though so I will discuss it with the team how to make it clearer

FabienCoutant commented 2 years ago

Oh ok! I see But yeah if the Provider Example has the useConnectEagerly() function call I think it will be easier to understand. I remember that the V6 example has it and I thought that it's not needed with the v8

vm commented 2 years ago

We are going to work on the docs! Definitely needed

0xD4V1NC1 commented 2 years ago

@0xD4V1NC1 web3-react won't remember the last used connector, you'll need to handle that logic yourself using some type of cached state.

@vm Okay, makes sense. Thank you. I am probably going to use LocalStorage or do you have a better suggestion that I should look into?

vm commented 2 years ago

That's what we've been doing for the interface codebase! I'm going to close this now, and hopefully I answered everything.