WalletConnect / web3modal

A single Web3 provider solution for all Wallets
https://web3modal.com
Apache License 2.0
4.8k stars 1.35k forks source link

[bug] Web3Modal does not care whether its modal shows right or wrong #1312

Closed hskang9 closed 1 year ago

hskang9 commented 1 year ago

Link to minimal reproducible example

https://gist.github.com/hskang9/e37ee24589b3824dcaf770111d5b631a

Summary

This is my login page code to open modal once the page is rendered. but the modal opens with empty content. It does not even check if the modal has the right content.

isOpen in useWeb3Modal should be true when Modal is successfully rendered without missing values.

At least it needs to check whether its html element w3m-container has its children element like this example below:

  const check = document.querySelectorAll("w3m-modal")[0].getInnerHTML().includes("w3m-modal-backcard");

this is the code.

import { Web3Button, useWeb3Modal } from "@web3modal/react";
import { useState, useEffect } from "react";
import Or from "components/signin/Or";
import Welcome from "components/signin/Welcome";
import { useRouter } from "next/router";
import { useAccount } from "wagmi";

function IndexPage() {

  // open modal in first connections
  const { open, close, isOpen } = useWeb3Modal();
  const [firstOpened, setFirstOpened] = useState(true);

  // go to app
  const { isConnected, status } = useAccount();
  const router = useRouter();
  useEffect(() => {
    console.log(isConnected, firstOpened, isOpen, status)
    if (isConnected) {
      close();
      router.push("/swap");
    }
    if (!isConnected && isOpen && firstOpened) {
      close();
    }
    if (firstOpened && isOpen) {
      close();
    }
    if (firstOpened && !isOpen && !isConnected) {
      open();
      setFirstOpened(false);
    }
    if (!isConnected && !firstOpened && isOpen ) {
      console.log("close")
      close();
      open();
    }
    if (!isConnected && !firstOpened && !isOpen ) {
      close();
    }
  }, [isConnected, firstOpened, isOpen]);

  const headerStyle = {
    marginLeft: "max(calc((100% - 71.25rem) / 2), 4rem)",
    marginRight: "max(calc((100% - 71.25rem) / 2), 4rem)",
  };

  return (
    <>
      <div className="flex flex-col xs:w-full lg:w-[60%] bg-[#F2F5F84D] h-screen">
        {/* Call To Action */}
        <header className="pt-16" style={headerStyle}>
          <div className="w-full flex flex-row jusify-between align-center">
            <img src="svgs/stnd_logo_black.svg" className="w-32" />
          </div>
          {/* Text */}
          <div className="pt-16 my-4 ">
            <h1 className="leading-normal xs:text-[30px] sm:text-5xl font-semibold">
              Make the most of your crypto trading experiences
            </h1>
            <h1 className="pt-8 xs:text-[20px] sm:text-[28px] break-words">
              Enter the Standard. Please connect your wallet to the blockchain
              network by clicking the button below.
            </h1>
          </div>
        </header>
        {/* Web3 Button */}
        <div className="w-full h-40 px-28 py-8 flex flex-col justify-center items-center">
          <Web3Button />
        </div>

        {/* Or */}
      </div>
      {/* Image panel */}
      <div className="xs:hidden lg:block lg:w-[40%] h-full z-10 object-cover">
        <img
          src={`pngs/login-page/get_started.png`}
          className="absolute xs:hidden lg:block lg:w-[40%] h-full top-0 right-0 object-cover border-l-4 border-black"
        />
      </div>
    </>
  );
}

export default IndexPage;

List of related npm package versions

web3modal : "wagmi": "^0.12.10", "@web3modal/ethereum": "^2.2.0", "@web3modal/react": "^2.2.0",

Screenshot 2023-09-06 at 5 06 27 PM
xzilja commented 1 year ago

This is using old version of packages and I don't see <Web3Modal /> component implemented, could you address these and let me know if issue still persists.

hskang9 commented 1 year ago

I don't understand versioning up into typescript, incompatible newest but deteriorated version will resolve this issue. I cannot even let my old code like useAccount work outside of WagmiConfig DOM element. I thought I just need to find what client-side exception happen in modal component, but why am I asked to update the entire codebase and see if it changes result?

xzilja commented 1 year ago

Wagmi hooks have to be used within their config component as it creates context's for them. I'm still not clear on what your issue is, have you included actual Web3Modal component? From screenshot above, all warnings come from Intercom?

hskang9 commented 1 year ago

This is the _app.js I included for <Web3Modal/>

import { React, useState, useEffect } from "react";
import Head from "next/head";
import "styles/global.css";
import Navbar from "../components/navbar/Navbar";
import "utils/analytics";
import Chat from "components/misc/Chat";
import WrappedTokens from "inputs/wrapped_tokens.json";
import {
  createClient,
  configureChains,
  WagmiConfig,
  useAccount,
  useNetwork,
} from "wagmi";
import {
  EthereumClient,
  w3mConnectors,
  w3mProvider,
} from "@web3modal/ethereum";
import { Web3Modal, useWeb3Modal } from "@web3modal/react";
import {
  arbitrum,
  mainnet,
  optimism,
  evmos,
  zkSync,
  polygon,
} from "wagmi/chains";
import {
  mantleTest,
  baseGoerli,
  KucoinCommunityChain,
  LineaTestnet,
} from "consts/customChains";
import localFont from "next/font/local";
import { useRouter } from "next/router";
import { HeadTitle } from "enums";
import PageLoader from "components/loaders/PageLoader";
import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";

const favorit = localFont({
  src: "./fonts/Favorit/ABCFavoritVariable.woff2",
  variable: "--font-favorit",
  display: "swap",
});

const loginPaths = ["/signin", "/get-started", "/unsupported", "/"];
const nonLoginPaths = [
  "/legal/terms-of-service",
  "/legal/privacy-policy",
  "/buy-sell-crypto",
];

// 1. Get projectID at https://cloud.walletconnect.com
if (!process.env.NEXT_PUBLIC_PROJECT_ID) {
  throw new Error("You need to provide NEXT_PUBLIC_PROJECT_ID env variable");
}

export const projectId = process.env.NEXT_PUBLIC_PROJECT_ID;

// 2. Configure wagmi client
const chains = [
  //mainnet,
  //optimism,
  //arbitrum,
  //evmos,
  //polygon,
  //zkSync,
  //baseGoerli,
  //KucoinCommunityChain,
  LineaTestnet,
];
const { provider } = configureChains(chains, [w3mProvider({ projectId })]);
export const wagmiClient = createClient({
  autoConnect: true,
  connectors: w3mConnectors({ version: 1, chains, projectId }),
  provider,
});

// 3. Configure modal ethereum client
export const ethereumClient = new EthereumClient(wagmiClient, chains);

// 4. Wrap your app with WagmiProvider and add <Web3Modal /> component
function MyApp({ Component, pageProps }) {
  const [ready, setReady] = useState(false);
  const { isConnected } = useAccount();
  const { chain } = useNetwork();
  const [wasConnectedChain, setWasConnectedChain] = useState(undefined);
  const router = useRouter();

  // Web3.0 hooks and effects
  useEffect(() => {
    setReady(true);
  }, []);

  useEffect(() => {
    if (!router.isReady) return;

    const handleUnsupportedChain = () => {
      if (chain && WrappedTokens[chain.name] === undefined) {
        localStorage.setItem("path", router.asPath);
        router.push({
          pathname: "/unsupported",
        });
      }
    };

    const handleLoginRedirects = () => {
      const path = localStorage.getItem("path");

      if (isConnected) {
        if (path) {
          localStorage.removeItem("path");
          router.push(path);
        } else if (chain && chain.unsupported) {
          localStorage.setItem("path", router.asPath);
          router.push({
            pathname: "/unsupported",
          });
        } else if (!wasConnectedChain && loginPaths.includes(router.pathname)) {
          setWasConnectedChain(chain);
          const prevLink = localStorage.getItem("path");
          router.push(prevLink ?? "/swap");
        }
      } else {
        localStorage.setItem("path", router.asPath);
        if (!loginPaths.includes(router.pathname)) {
          router.push({
            pathname: wasConnectedChain ? "/signin" : "/",
          });
        }
      }
    };

    const handleNetworkChange = () => {
      if (wasConnectedChain && chain && wasConnectedChain.id !== chain.id) {
        window.location.reload();
      }
    };

    handleUnsupportedChain();
    handleLoginRedirects();
    handleNetworkChange();
  }, [chain, router.isReady, isConnected]);

  // Reload when network changes
  useEffect(() => {
    if (wasConnectedChain && chain && wasConnectedChain.id !== chain.id) {
      window.location.reload();
    }
  }, [chain]);

  function getTitleForPath(path) {
    for (const key in HeadTitle) {
      if (path.includes(key)) {
        return HeadTitle[key];
      }
    }
    return null; // Return null if no match is found
  }

  const renderHead = () => {
    if (router.pathname.includes("/trade")) {
      return (
        // Make new Head in trade page
        <></>
      );
    } else {
      return (
        <Head>
          <title>{`${getTitleForPath(
            router.asPath
          )} | Standard - All in one app for Bitcoin, Ethereum & Altcoins`}</title>
          <meta name="apple-mobile-web-app-capable" content="yes" />
          <meta name="apple-mobile-web-app-status-bar-style" content="black" />
          <meta name="apple-mobile-web-app-title" content="Standard" />
          <meta name="view-transition cursor-pointer" content="same-origin" />
        </Head>
      );
    }
  };

  const renderFooter = () => {
    if (router.pathname.includes("/trade")) {
      return (
        // Make new Footer in trade page
        <></>
      );
    } else {
      return <Footer />;
    }
  };

  const renderPage = () => {
    if (loginPaths.includes(router.pathname)) {
      return (
        <>
          <div className="w-screen h-screen flex-row justify-between contents">
            <>
              {/* show loader if you are connected */}
              {router.pathname !== "/unsupported" && isConnected && !nonLoginPaths.includes(router.asPath) && (
                <PageLoader
                 id="login"
                  isOpen={isConnected && !nonLoginPaths.includes(router.asPath)}
                />
              )}
              <Chat />
              <main
                className={`${favorit.variable} font-sans w-full h-full bg-[#F2F5F84D] `}
              >
                <Component {...pageProps} />
              </main>
            </>
          </div>
        </>
      );
    } else {
      return (
        <div className="w-screen h-screen flex flex-col justify-between overflow-auto">
          <>
            {/* show loader if you are disconnected */}
            {!nonLoginPaths.includes(router.asPath) &&
              !isConnected &&
              !loginPaths.includes(router.asPath) && (
                <PageLoader
                id="logout"
                  isOpen={
                    !nonLoginPaths.includes(router.asPath) &&
                    !isConnected &&
                    !loginPaths.includes(router.asPath)
                  }
                />
              )}
            <Chat />
            <main className={`${favorit.variable} font-sans w-full`}>
              <Navbar
                networkName={chain ? chain.name : "Ethereum"}
                pathname={router.pathname}
              />
              {nonLoginPaths.includes(router.asPath) ? (
                <Component {...pageProps} className="h-full" />
              ) : (
                isConnected && <Component {...pageProps} className="h-full" />
              )}
            </main>
            <ToastContainer />
          </>
        </div>
      );
    }
  };

  return (
    <>
      {ready ? (
        <WagmiConfig client={wagmiClient}>
          {renderHead()}
          {renderPage()}
        </WagmiConfig>
      ) : null}
      <Web3Modal
        projectId={projectId}
        ethereumClient={ethereumClient}
        termsOfServiceUrl=""
        privacyPolicyUrl=""
      />
    </>
  );
}

const Footer = () => {
  return (
    <footer
      className={`w-screen ${favorit.variable} font-sans text-[#FEFBEB]`}
      style={{ backgroundColor: "#121920" }}
    >
      <div className="w-full px-screen-5 py-20 flex xs:flex-col md:flex-row xs:space-x-0 space-x-4 xs:space-y-8">
        <div className="xs:w-full w-1/2 flex flex-col justify-start items-start">
          <img
            src="/svgs/stnd_logo_cream.svg"
            alt="logo"
            className="w-[400px] h-[63px]"
          />
          <span className="mt-5 font-normal text-center">
            2023 Standard Labs. - All rights reserved
          </span>
        </div>
        <div className="xs:w-full w-1/2 flex flex-row justify-between items-start">
          <div className="flex flex-col justify-start space-y-3">
            <span className="font-semibold">Products</span>
            <span className="text-sm font-normal">App</span>
            <span className="text-sm">Docs</span>
            <span className="text-sm">Support</span>
          </div>
          <div className="flex flex-col justify-start space-y-3">
            <span className="font-semibold">Explore</span>
            <span className="text-sm">Buy Crypto</span>
            <span className="text-sm">
              <a href="/market">Swap</a>
            </span>
            <span className="text-sm">Trade</span>
            <span className="text-sm">Learn</span>
          </div>
          <div className="flex flex-col justify-start space-y-3">
            <span className="font-semibold">Connect</span>
            <span className="text-sm">Twitter</span>
            <span className="text-sm">Telegram</span>
            <span className="text-sm">Discord</span>
            <span className="text-sm">Linkedin</span>
          </div>
        </div>
      </div>
    </footer>
  );
};

export default MyApp;

The issue is useWeb3Modal hook's isOpen variable shows true even when web3modal is not rendered fully.

hskang9 commented 1 year ago

Creating context does not matter, it is just Web3Modal having client side error not being able to handle its context given by Wagmi properly. Usually, it should be something where you did not check undefined variable, and prepare for alternative rendering.

DSapielkin commented 1 year ago

I have very interisting error

Снимок экрана 2023-09-08 в 12 27 30
DSapielkin commented 1 year ago

When i try open web3Modal i have this error^

Снимок экрана 2023-09-08 в 12 28 16 Снимок экрана 2023-09-08 в 12 28 30
DSapielkin commented 1 year ago

it worked correctly before

DSapielkin commented 1 year ago
Снимок экрана 2023-09-08 в 12 30 13
xzilja commented 1 year ago

Using <w3m-qrcode> on it's own is a more advanced use case, it expects uri for qrcode to be passed as an argument. You would need to get it from other sdk's like @walletconnect/ethereum-provider or even @walletconnect/sign-client. To simply use modal as intended (normal use case) you can follow example implementations in docs.

hskang9 commented 1 year ago

at least make this useWeb3modal hook check if that html element is rendered and set isOpen to true. This is all I want. I don't need these new wagmi v1 with useless type gymnastics while missing out original features which was in v0. Typescript was intended to prevent undefined property, but this code cannot even use it right.

hskang9 commented 1 year ago

Seriously, why in the world would you use Typescript when you cannot even handle undefined property or variables? It is made because of it.

xzilja commented 1 year ago

I'm still confused about what exactly your issue is, mind putting together a codesandbox? What exactly is undefined? You mentioned

I don't understand versioning up into typescript, incompatible newest but deteriorated version will resolve this issue.

Does this mean upgrading ts version resolves the issue or were you just complaining about ts?

hskang9 commented 1 year ago

You should be confused because you cannot find what is undefined and fails to render web3 modal.

I am complaining that you cannot even find that in your code when you are using typescript, because it is the tool for you to find if you have strongly defined types which are used in your web3modal. Instead of versioning up typescript or package and praying to your computer, I highly suggest you look at property which web3modal uses and check if there is any case it becomes undefined or strongly define types that are input to render the modal. From @DSapielkin’s error, it seems evident there is an missing property in some state of web3modal. You should update isOpen state to false corresponding to that situation.

hskang9 commented 1 year ago

Does this mean upgrading ts version resolves the issue or were you just complaining about ts?

🤦‍♂️… My suggestion in typescript configuration is this; I think you should include “strict” : true property in tsconfig.json on web3modal. I think this will make editor filled with red lines, but that should happen because ok undefined properties. Otherwise, I would highly recommend to use JSDoc to define types so that codes can be run, and replace original js code with typescript code. I would never use typescript code in the first place unless there are types that are strongly defined not to change.

xzilja commented 1 year ago

Will be closing this now, as discussion is now unproductive. To leave you with few solutions / explanations (based on my best guess of what issue you are facing):

  1. You could be facing an issue that was resolved in later versions of web3modal packages that you use, try upgrading
  2. isOpen state is what modal component reacts to, not vice versa
  3. You suggestion to use strict mode is already the case i.e .https://github.com/WalletConnect/web3modal/blob/V2/tsconfig.json#L8
  4. This project can be used inside js project without need to rely on TypeScript