Having different wallet addresses by gmail login

In our testing environment (mumbai), there is an issue that different wallet addresses were generated from the same email and login method.

According to our database, the first login was in 2023-10-19, address was 0xbd871cde665f5f34de5f3e74b6d29e0f9a895d60.
In 2023-10-24, the address changed to 0x521635c080c07c950e65af875e62a62e1e7134e9.

We’re using default verifier, and confirm that they are gmail login (not passwordless). The issue happens in testing environment, no client id change/ migration recently.

Here are the web3auth packages we’re using

    "@web3auth/base": "^6.0.1",
    "@web3auth/ethereum-provider": "^6.1.1",
    "@web3auth/metamask-adapter": "^6.0.1",
    "@web3auth/no-modal": "^6.0.1",
    "@web3auth/openlogin-adapter": "^6.1.3",
    "@web3auth/torus-wallet-connector-plugin": "^6.1.3",
    "@web3auth/wallet-connect-v1-adapter": "^6.1.0",
    "@web3auth/wallet-connect-v2-adapter": "^6.1.0",

looking forward to your response, thanks!

@gigichan Welcome Aboard!

Are you able to upgrade all packages to version V7?

Thanks for responding! Yes we can try with it.

May I know if there is any ways that we can verify it’s fixed after upgrading the packages to v7? And which address (0xbd871cde665f5f34de5f3e74b6d29e0f9a895d60/ 0x521635c080c07c950e65af875e62a62e1e7134e9) is expected after the upgrade?

We had a report of a similar issue which was resolved by upgrading. Additionally, please note that the end-of-life for v6 is scheduled to be 31st December 2023 , so we recommend upgrading to V7 first then check the behavior.

The address which was first generated with the account.

After upgrading all packages to 7.0.0, it’s still generating 0x521635c080c07C950e65af875e62a62e1e7134E9 (the second generated address). Is it abnormal in your view?

Can you share your complete implementation code so our Dev team can check? Also, please go through the below document to check why the wallet address changed for any of the possible reasons:

May I share it to you through email?

@gigichan You can share it here.

@vjgee this is the implementation, thanks!

This is the hook to provide all login methods: useWeb3Auth.tsx

/* eslint-disable @typescript-eslint/no-explicit-any */
import { useEffect, useRef, useState } from 'react';
import { Web3AuthNoModal } from '@web3auth/no-modal';
import { WALLET_ADAPTERS, CHAIN_NAMESPACES, SafeEventEmitterProvider } from '@web3auth/base';
import { OpenloginAdapter, OpenloginUserInfo } from '@web3auth/openlogin-adapter';
import { EthereumPrivateKeyProvider } from '@web3auth/ethereum-provider';
import { MetamaskAdapter } from '@web3auth/metamask-adapter';
import {
  WalletConnectV2Adapter,
  getWalletConnectV2Settings,
} from '@web3auth/wallet-connect-v2-adapter';
import { WalletConnectModal } from '@walletconnect/modal';

import {
  openLoginAdapterSettings,
  chainConfig,
  SOCIAL_AUTH_PROVIDER_MAP,
  WC_CHAIN_IDS,
} from '@constants/web3Auth';
import { clientId, wcProjectID, web3AuthNetwork } from '@constants/envVariables';
import { TorusWalletConnectorPlugin } from '@web3auth/torus-wallet-connector-plugin';
import { APIServiceAuthenticationParams } from '@api/type';
import { getAuthInfo, getWalletAddress } from '@utils/web3auth.utils';
import { torusWalletInitOptions } from '@configs/torusWallet';
import useScreenDetector from './useScreenDetector';

const walletConnectModal = new WalletConnectModal({ projectId: wcProjectID });

interface IUserInfo {
  email?: string;
  walletAddress: string;
  loginProvider: string;
  openloginUserInfo?: Partial<OpenloginUserInfo>;
}

export interface IWeb3AuthState {
  web3auth: Web3AuthNoModal | null;
  torusPlugin: TorusWalletConnectorPlugin | null;
  provider: SafeEventEmitterProvider | null;
  authenticateUser: () => Promise<any>;
  logout: () => Promise<void>;
  isConnecting: boolean;
  loginWithGoogle: () => Promise<void>;
  loginWithFacebook: () => Promise<void>;
  loginWithTwitter: () => Promise<void>;
  loginWithDiscord: () => Promise<void>;
  loginWithWCv2: () => Promise<void>;
  loginWithMM: () => Promise<void>;
  error: string | null;
  setError: (error: string | null) => void;
  isLoading: boolean;
  isUserInfoLoading: boolean;
  socialLogin: (provider: string) => Promise<void>;
  userInfo: IUserInfo | null;
  authInfo: APIServiceAuthenticationParams;
}

export const useWeb3Auth = (): IWeb3AuthState => {
  const { screenType, SCREEN_TYPE_MAP } = useScreenDetector();
  const popUpClosedByUser = useRef<boolean>(false);

  const [web3auth, setWeb3auth] = useState<Web3AuthNoModal | null>(null);
  const [torusPlugin, setTorusPlugin] = useState<TorusWalletConnectorPlugin | null>(null);
  const [provider, setProvider] = useState<SafeEventEmitterProvider | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [isUserInfoLoading, setIsUserInfoLoading] = useState(false);
  const [userInfo, setUserInfo] = useState<IUserInfo | null>(null);
  const [isConnecting, setIsConnecting] = useState<boolean>(false);

  const [authInfo, setAuthInfo] = useState<APIServiceAuthenticationParams>({});
  const isLoading = useRef(false);

  useEffect(() => {
    const init = async () => {
      try {
        if (isLoading.current || screenType == SCREEN_TYPE_MAP.INIT) return;
        isLoading.current = true;

        const web3auth = new Web3AuthNoModal({
          clientId,
          chainConfig,
          web3AuthNetwork,
        });

        const privateKeyProvider = new EthereumPrivateKeyProvider({
          config: { chainConfig },
        });

        // Open login adapter (for social logins)
        const openloginAdapter = new OpenloginAdapter({
          adapterSettings: {
            ...openLoginAdapterSettings,
            uxMode: screenType === SCREEN_TYPE_MAP.MOBILE ? 'redirect' : 'popup',
          },
          privateKeyProvider,
        });

        web3auth.configureAdapter(openloginAdapter);

        // Wallet connect v2 adapter
        const defaultWcSettings = await getWalletConnectV2Settings(
          CHAIN_NAMESPACES.EIP155,
          WC_CHAIN_IDS,
          wcProjectID,
        );

        const walletConnectV2Adapter = new WalletConnectV2Adapter({
          adapterSettings: {
            qrcodeModal: walletConnectModal,
            ...defaultWcSettings.adapterSettings,
          },
          loginSettings: { ...defaultWcSettings.loginSettings },
          web3AuthNetwork,
        });
        web3auth.configureAdapter(walletConnectV2Adapter);

        // Metamask adapter
        const metamaskAdapter = new MetamaskAdapter({});
        web3auth.configureAdapter(metamaskAdapter);

        web3auth.on('errored', (e) => {
          if (
            e?.message?.toLowerCase?.().includes?.('closed by the user') ||
            e?.message?.toLowerCase?.().includes?.('user rejected the request')
          ) {
            popUpClosedByUser.current = true;
            setError(null);
          }
          console.error(e);
        });
        setWeb3auth(web3auth);

        // Initialize TORUS EVM Wallet Plugin
        const torusPlugin = new TorusWalletConnectorPlugin({
          torusWalletOpts: {},
          walletInitOptions: torusWalletInitOptions,
        });
        await web3auth.addPlugin(torusPlugin);
        await web3auth.init();

        if (web3auth.connected) {
          setProvider(web3auth.provider);
        } else {
          setProvider(null);
        }
        setTorusPlugin(torusPlugin);
      } catch (error: unknown) {
        setError(error?.toString?.() || 'Internal Error');
        console.error(error);
      } finally {
        isLoading.current = false;
      }
    };

    init();
  }, [screenType]);

  // Get user info from web3auth
  useEffect(() => {
    if (!provider || !web3auth?.connected) {
      return;
    }
    const fetchUserInfo = async () => {
      setIsUserInfoLoading(true);
      try {
        const walletAddress = await getWalletAddress(provider);

        const { email, appPubKey, authToken, loginProvider, userInfoRaw } = await getAuthInfo(
          web3auth,
        );

        if (appPubKey && authToken) {
          setAuthInfo({
            appPubKey,
            authToken,
          });
        }
        setUserInfo({
          email,
          walletAddress,
          loginProvider,
          openloginUserInfo: userInfoRaw,
        });
      } finally {
        setIsUserInfoLoading(false);
      }
    };

    fetchUserInfo();
  }, [provider, web3auth?.connected]);

  const socialLogin = async (provider: string) => {
    popUpClosedByUser.current = false;
    if (!web3auth) {
      uiConsole('web3auth not initialized yet');
      return;
    }

    if (!provider) {
      uiConsole('web3auth not initialized yet');
      return;
    }

    setIsConnecting(true);
    try {
      const web3authProvider = await web3auth.connectTo(WALLET_ADAPTERS.OPENLOGIN, {
        loginProvider: provider,
      });
      setProvider(web3authProvider);
      setError('');
    } catch (e: unknown) {
      if ((e as any)?.message.includes('Wallet is not ready yet, Already connecting')) {
        console.error('already connecting error: ', e);
      } else if (screenType === SCREEN_TYPE_MAP.WEB_BROWSER && !popUpClosedByUser.current)
        // NOTE: we use redirect instead of popup in mobile
        setError(e?.toString() || 'Internal error');
      console.error(e);
    } finally {
      setIsConnecting(false);
    }
  };

  const loginWithGoogle = async () => {
    socialLogin(SOCIAL_AUTH_PROVIDER_MAP.GOOGLE);
  };

  const loginWithFacebook = async () => {
    socialLogin(SOCIAL_AUTH_PROVIDER_MAP.FACEBOOK);
  };

  const loginWithTwitter = async () => {
    socialLogin(SOCIAL_AUTH_PROVIDER_MAP.TWITTER);
  };

  const loginWithDiscord = async () => {
    socialLogin(SOCIAL_AUTH_PROVIDER_MAP.DISCORD);
  };

  const loginWithWCv2 = async () => {
    if (!web3auth) {
      uiConsole('web3auth not initialized yet');
      return;
    }
    try {
      const web3authProvider = await web3auth.connectTo(WALLET_ADAPTERS.WALLET_CONNECT_V2);
      setProvider(web3authProvider);
    } catch (e) {
      console.error(e);
      walletConnectModal.closeModal();
      if (!e?.toString?.()?.includes?.('User closed modal')) {
        setError(e?.toString?.() || 'Internal error');
      }
    }
  };

  const loginWithMM = async () => {
    popUpClosedByUser.current = false;
    // eslint-disable-next-line @typescript-eslint/ban-ts-comment
    // @ts-ignore
    if (window && typeof window.ethereum == 'undefined') {
      console.error('MetaMask is not installed!');
      setError('MetaMask is not installed!');
      return;
    }

    if (!web3auth) {
      uiConsole('web3auth not initialized yet');
      return;
    }

    setIsConnecting(true);

    try {
      const web3authProvider = await web3auth.connectTo(WALLET_ADAPTERS.METAMASK);
      setProvider(web3authProvider);
    } catch (e) {
      console.error(e);
      if ((e as any)?.message.includes('Failed to connect with wallet. Already connected')) {
        setError((e as any)?.message);
      }
      if (!popUpClosedByUser.current) setError(e?.toString?.() || 'Internal error');
    } finally {
      setIsConnecting(false);
    }
  };

  const authenticateUser = async () => {
    if (!web3auth) {
      uiConsole('web3auth not initialized yet');
      return;
    }
    const idToken = await web3auth.authenticateUser();

    return idToken;
  };

  const logout = async () => {
    if (!web3auth) {
      uiConsole('web3auth not initialized yet');
      return;
    }
    // @TODO
    await web3auth.logout();
    setProvider(null);
    setUserInfo(null);
    setAuthInfo({});
  };

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  function uiConsole(...args: any[]): void {
    const message = JSON.stringify(args || {}, null, 2);
    console.log(message);
  }

  return {
    web3auth,
    torusPlugin,
    provider,
    authenticateUser,
    logout,
    isConnecting,
    loginWithGoogle,
    loginWithFacebook,
    loginWithTwitter,
    loginWithDiscord,
    loginWithWCv2,
    loginWithMM,
    error,
    setError,
    isLoading: isLoading.current,
    socialLogin,
    userInfo,
    authInfo,
    isUserInfoLoading,
  };
};

Related file used in the hook: constants/web3Auth

import { CHAIN_NAMESPACES } from '@web3auth/base';

import { OPENLOGIN_NETWORK, LANGUAGE_TYPE } from '@web3auth/openlogin-adapter';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const config: Record<string, any> = {
  [OPENLOGIN_NETWORK.TESTNET]: {
    chainNamespace: CHAIN_NAMESPACES.EIP155,
    chainId: '0x13881',
    rpcTarget: 'https://rpc-mumbai.maticvigil.com',
    displayName: 'Polygon Mumbai Testnet',
    blockExplorer: 'https://mumbai.polygonscan.com/',
    ticker: 'MATIC',
    tickerName: 'Polygon Mumbai',
  },
  [OPENLOGIN_NETWORK.MAINNET]: {
    chainNamespace: CHAIN_NAMESPACES.EIP155,
    chainId: '0x89',
    rpcTarget: 'https://rpc-mainnet.maticvigil.com',
    displayName: 'Polygon Mainnet',
    blockExplorer: 'https://polygonscan.com/',
    ticker: 'MATIC',
    tickerName: 'Polygon Mainnet',
  },
};

export const chainConfig =
  config[process.env.REACT_APP_CHAIN_CONFIG_NETWORK || OPENLOGIN_NETWORK.TESTNET];

const CHAIN_ID = Number(chainConfig.chainId);
export const WC_CHAIN_IDS = [CHAIN_ID];

export const openLoginAdapterSettings = {
  whiteLabel: {
    defaultLanguage: 'en' as LANGUAGE_TYPE,
    mode: 'dark',
    theme: {
      primary: '#00D1B2',
    },
  },
};

export const SOCIAL_AUTH_PROVIDER_MAP = {
  FACEBOOK: 'facebook',
  GOOGLE: 'google',
  DISCORD: 'discord',
  TWITTER: 'twitter',
} as const;

export const SOCIAL_AUTH_PROVIDERS = Object.values(SOCIAL_AUTH_PROVIDER_MAP);

Related file used in the hook: @utils/web3auth.utils

import { getPublicCompressed } from '@toruslabs/eccrypto';
import { SafeEventEmitterProvider } from '@web3auth/base';
import { Web3AuthNoModal } from '@web3auth/no-modal';
import { LOGIN_PROVIDER_TYPE, OpenloginUserInfo } from '@web3auth/openlogin-adapter';
import RPC from '@services/web3RPC.service';
import { SOCIAL_AUTH_PROVIDERS } from '@/constants/web3Auth';

export const getAppPrivKey = async (provider: SafeEventEmitterProvider) => {
  return provider.request({
    method: 'eth_private_key', // use "private_key" for other non-evm chains
  }) as Promise<string>;
};
export const getAppPubKey = async (provider: SafeEventEmitterProvider) => {
  const app_scoped_privkey = await getAppPrivKey(provider);
  return getPublicCompressed(Buffer.from(app_scoped_privkey.padStart(64, '0'), 'hex')).toString(
    'hex',
  );
};

export const getWalletAddress = async (provider: SafeEventEmitterProvider) => {
  const rpc = new RPC(provider);
  return rpc.getAccounts() as Promise<string>;
};

export const getLoginProvider = async (
  web3auth: Web3AuthNoModal,
  userInfo?: Partial<OpenloginUserInfo>,
) => {
  const userInfoRaw = userInfo ?? (await web3auth.getUserInfo());
  const loginProvider = userInfoRaw.typeOfLogin ?? web3auth?.connectedAdapterName ?? '';
  return loginProvider as LOGIN_PROVIDER_TYPE;
};

export const getAuthInfo = async (
  web3auth: Web3AuthNoModal,
  userInfo?: Partial<OpenloginUserInfo>,
) => {
  let appPubKey = '',
    authToken = '',
    email = '',
    loginProvider = '';

  const userInfoRaw = userInfo ?? (await web3auth.getUserInfo());
  loginProvider = await getLoginProvider(web3auth, userInfoRaw);
  email = userInfoRaw?.email ?? '';

  // only return pubkey and authToken for social login
  if (SOCIAL_AUTH_PROVIDERS.includes(loginProvider as any)) {
    appPubKey = await getAppPubKey(web3auth.provider!);
    authToken = userInfoRaw?.idToken ?? '';
  }
  return {
    appPubKey,
    authToken,
    email,
    loginProvider,
    userInfoRaw,
  };
};

Related implementation in @services/web3RPC.service

/* eslint-disable @typescript-eslint/no-explicit-any */
import type { SafeEventEmitterProvider } from '@web3auth/base';
import { ethers } from 'ethers';

export default class EthereumRpc {
  private provider: SafeEventEmitterProvider;

  constructor(provider: SafeEventEmitterProvider) {
    this.provider = provider;
  }

  async getAccounts(): Promise<any> {
    try {
      const ethersProvider = new ethers.providers.Web3Provider(this.provider);
      const signer = ethersProvider.getSigner();

      // Get user's Ethereum public address
      const address = await signer.getAddress();

      return address;
    } catch (error) {
      return error;
    }
  }
}

related packages

    "@web3auth/base": "^7.0.0",
    "@web3auth/ethereum-provider": "^7.0.0",
    "@web3auth/metamask-adapter": "^7.0.0",
    "@web3auth/no-modal": "^7.0.0",
    "@web3auth/openlogin-adapter": "^7.0.0",
    "@web3auth/torus-wallet-connector-plugin": "^7.0.0",
    "@web3auth/wallet-connect-v2-adapter": "^7.0.0",
    "ethers": "^5.7.2",
    "@walletconnect/modal": "^2.6.2",
    "@toruslabs/eccrypto": "^4.0.0",

Hi @gigichan , can you plz also share user’s email to check in our logs?

@himanshu sure, email is jackychengb5@gmail.com

Thanks for the reply, we are debugging this, please expect a reply with fix in a couple of days, please be rest assured that user will be able to get old address.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.

Hi @himanshu is there any updates?