Facing issues with Ethereum Key Provider with web sfa sdk

When asking for help in this category, please make sure to provide the following details:

  • SDK Version: 8.0.2
  • Platform: web

I have a component where i am trying to do custom authentication login just like with the example at web3auth-core-kit-examples/single-factor-auth-web/sfa-web-custom-jwt-example at main · Web3Auth/web3auth-core-kit-examples · GitHub

here is my component

import React, { useState, useEffect } from "react";
import axios from "axios";
import { useNavigate } from "react-router-dom";
import { tKey } from "../tkey";
import { generatePrivateKeyFromUserId } from "../utils/Helpers";
import { AxirCore } from "axr-erc4337-sdk";
import { ethers } from "ethers";
import { Web3Auth, decodeToken } from "@web3auth/single-factor-auth";
// import { EthereumPrivateKeyProvider } from "@web3auth/ethereum-provider";
import { CHAIN_NAMESPACES, WEB3AUTH_NETWORK } from "@web3auth/base";

import * as jose from "jose";

import Loader from "../components/Loader";
import { encryptKey } from "../utils/Helpers";
import "./TelegramLogin.css";
import { EthereumPrivateKeyProvider } from "@web3auth/ethereum-provider";

const verifier = "w3a-jwt-for-sfa-web";

const clientId =
  "BEglQSgt4cUWcj6SKRdu5QkOXTsePmMcusG5EAoyjyOYKlVRjIF1iCNnMOTfpzCiunHRrMui8TIwQPXdkQ8Yxuk"; // get from https://dashboard.web3auth.io

const chainConfig = {
  chainId: "0x1",
  displayName: "Ethereum Mainnet",
  chainNamespace: CHAIN_NAMESPACES.EIP155,
  tickerName: "Ethereum",
  ticker: "ETH",
  decimals: 18,
  rpcTarget: "https://rpc.ankr.com/eth",
  blockExplorerUrl: "https://etherscan.io",
};

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

// // Initialising Web3Auth Single Factor Auth SDK
// const web3authSfa = new Web3Auth({
//   clientId, // Get your Client ID from Web3Auth Dashboard
//   web3AuthNetwork: WEB3AUTH_NETWORK.TESTNET, // ["cyan", "testnet"]
//   usePnPKey: false, // Setting this to true returns the same key as PnP Web SDK, By default, this SDK returns CoreKitKey.
//   privateKeyProvider,
// });

function TelegramLogin() {
  const navigate = useNavigate();
  const [loading, setLoading] = useState(true);
  const [telegramPrivateKey, setTelegramPrivateKey] = useState("");
  const [loginMethod, setLoginMethod] = useState("telegram");
  const [privateKey, setPrivateKey] = useState("");
  const [walletAddress, setWalletAddress] = useState("");
  const [privateKeyError, setPrivateKeyError] = useState("");
  const [walletAddressError, setWalletAddressError] = useState("");
  const [web3authSfa, setWeb3authSfa] = useEffect(null);

  useEffect(() => {
    const init = async () => {
      try {
        const privateKeyProvider = new EthereumPrivateKeyProvider({
          config: {
            chainConfig: {
              chainId: process.env.REACT_APP_WEB3AUTH_CHAIN_ID,
              rpcTarget: process.env.REACT_APP_RPC_URL,
            },
          },
        });

        // Initialising Web3Auth Single Factor Auth SDK
        const web3authSfa = new Web3Auth({
          clientId, // Get your Client ID from Web3Auth Dashboard
          web3AuthNetwork: WEB3AUTH_NETWORK.TESTNET, // ["cyan", "testnet"]
          usePnPKey: false, // Setting this to true returns the same key as PnP Web SDK, By default, this SDK returns CoreKitKey.
          privateKeyProvider,
        });

        setWeb3authSfa(web3authSfa);

        web3authSfa.init({});
      } catch (error) {
        console.error(error);
      }
    };

    init();
  }, []);

  useEffect(() => {
    const params = new URLSearchParams(window.location.search);
    const authData = {};
    for (let param of params) {
      authData[param[0]] = param[1];
    }

    if (authData.id && authData.hash) {
      validateTelegramAuth(authData);
    } else {
      navigate("/");
    }
  }, [navigate]);

  const getIdToken = async (id) => {
    // Get ID Token from server
    const res = await fetch("http://localhost:5000/api/token", {
      method: "POST",
      body: {
        id,
      },
      headers: {
        "Content-Type": "application/json",
      },
    });
    const data = await res.json();
    return data?.token;
  };

  const validateTelegramAuth = async (authData) => {
    try {
      const response = await axios.get(
        process.env.REACT_APP_VALIDATE_TELEGRAM_AUTH + "/telegram-login",
        {
          params: authData,
        }
      );

      if (response.status === 200) {
        handleTelegramAuthSuccess(authData?.id);
      } else {
        console.error("Telegram authentication failed");
        navigate("/");
      }
    } catch (error) {
      console.error("Error validating Telegram auth data:", error);
      navigate("/");
    }
  };

  // async function createJWT(userId) {
  //   // Assume the key is already imported or generated
  //   // For example, you might have imported it like this:
  //   // const privateKey = await jose.importJWK(yourJWK, 'RS256');

  //   const tokenPayload = {
  //     sub: `${userId}`, // must be unique to each user
  //     // name: newUser.username,
  //     // email: showOnlyVerified ? newUser.getEmailVerified() : newUser.email,
  //     // phoneNumber: showOnlyVerified ? newUser.getPhoneVerified() : newUser.phone_number,
  //     exp: Math.floor(Date.now() / 1000) + 86400, // expires in 1 day
  //     iat: Math.floor(Date.now() / 1000),
  //     iss: process.env.WEB3AUTH_ISS,
  //     aud: "axr-wallet",
  //   };

  //   const jwt = await new jose.SignJWT(tokenPayload)
  //     .setProtectedHeader({ alg: "RS256", typ: "JWT" })
  //     .sign(privateKey);

  //   return jwt;
  // }

  const generatePrivateKeyFromUserId = async (userId) => {
    // trying logging in with the Single Factor Auth SDK
    try {
      if (!web3authSfa) {
        console.log("**@ Web3Auth Single Factor Auth SDK not initialized yet");
        return;
      }
      const idTokenResult = await getIdToken(userId);

      const { payload } = decodeToken(idTokenResult);
      await web3authSfa.connect({
        verifier,
        verifierId: payload.sub,
        idToken: idTokenResult,
      });
    } catch (err) {
      // Single Factor Auth SDK throws an error if the user has already enabled MFA
      // One can use the Web3AuthNoModal SDK to handle this case
      console.error(err);
    }
  };

  const generatePrivateKeyFromUserIdOld = async (userId) => {
    // try {
    //   const verifierId = userId;
    //   const rpcUrl = process.env.REACT_APP_RPC_URL;
    //   const idToken = await createJWT(userId);
    //   console.log("**@ generated idToken from createJWT is , ", idToken);
    //   //this code if for "@web3auth/node-sdk": "^3.1.0"
    //   const web3auth = new Web3Auth({
    //     clientId: process.env.REACT_APP_WEB3AUTH_CLIENT_ID,
    //     web3AuthNetwork: process.env.REACT_APP_WEB3AUTH_NETWORK,
    //   });
    //   const ethereumProvider = new EthereumPrivateKeyProvider({
    //     config: {
    //       chainConfig: {
    //         // chainNamespace: "eip155",
    //         chainId: process.env.REACT_APP_WEB3AUTH_CHAIN_ID,
    //         rpcTarget: rpcUrl,
    //       },
    //     },
    //   });
    //   web3auth.init({ provider: ethereumProvider });
    //   const provider = await web3auth.connect({
    //     verifier: process.env.REACT_APP_WEB3AUTH_VERIFIER_NAME,
    //     verifierId: verifierId,
    //     idToken: idToken,
    //   });
    //   // logger.info(`provider: ${provider.toString()}`);
    //   let socialLoginShare = await provider.request({
    //     method: "eth_private_key",
    //   });
    //   console.log("**@ GENERATED SOCIAL Login share", socialLoginShare);
    //   return socialLoginShare;
    // } catch (err) {
    //   console.log(`**@ Error in getSocialLoginShare: ${JSON.stringify(err)}`);
    //   return null;
    // }
  };

  const handleTelegramAuthSuccess = async (userId) => {
    try {
      const privateKey = await generatePrivateKeyFromUserId(userId);
      console.log("**@ generatePrivateKeyFromUserId is , ", privateKey);
      setTelegramPrivateKey(privateKey);
      setLoading(false);
    } catch (error) {
      console.error("Error handling Telegram auth success:", error);
      navigate("/");
    }
  };

  const handlePrivateKeyChange = (e) => {
    const value = e.target.value;
    setPrivateKey(value);
    if (value && !/^0x[a-fA-F0-9]{64}$/.test(value)) {
      setPrivateKeyError("Invalid private key format");
    } else {
      setPrivateKeyError("");
    }
  };

  const handleWalletAddressChange = (e) => {
    const value = e.target.value;
    setWalletAddress(value);
    if (value && !/^0x[a-fA-F0-9]{40}$/.test(value)) {
      setWalletAddressError("Invalid wallet address");
    } else {
      setWalletAddressError("");
    }
  };

  const checkOwnerShip = async (smartWalletAccount, owner) => {
    try {
      const isOwner = await smartWalletAccount.isK1Owner(owner);
      return isOwner;
    } catch (e) {
      return false;
    }
  };

  const handleLogin = async () => {
    if (loginMethod === "telegram") {
      const encryptedPrivateKey = encryptKey(telegramPrivateKey);

      navigate("/dashboard/home", {
        state: { privateKey: encryptedPrivateKey, hideLogoutButton: true },
      });
    } else {
      if (
        !privateKeyError &&
        !walletAddressError &&
        privateKey &&
        walletAddress
      ) {
        const smartWalletAccount = new AxirCore(
          privateKey,
          process.env.REACT_APP_RPC_URL,
          process.env.REACT_APP_BUNDLER_URL,
          0,
          {
            walletAddress: walletAddress,
          }
        );
        const walletOwner = new ethers.Wallet(privateKey);
        const walletOwnerAddress = walletOwner.address;

        const isOwner = await checkOwnerShip(
          smartWalletAccount,
          walletOwnerAddress
        );

        if (isOwner) {
          const encryptedPrivateKey = encryptKey(privateKey);

          navigate("/dashboard/home", {
            state: {
              privateKey: encryptedPrivateKey,
              hideLogoutButton: true,
              walletAddress: walletAddress,
            },
          });
        } else {
          setWalletAddressError(
            "Private key is not the owner of the given wallet address"
          );
        }
      }
    }
  };

  if (loading) {
    return (
      <div className="telegram-login">
        <h1 className="telegram-login__title">AXR Wallet</h1>
        <p className="telegram-login__message">
          Authenticating with Telegram...
        </p>
        <div className="telegram-login__loader"></div>
      </div>
    );
  }

  return (
    <div className="login-screen">
      <div className="login-container">
        <h1 className="login-screen__title">AXR Wallet</h1>
        <div className="login-screen__tabs">
          <button
            className={`login-screen__tab ${
              loginMethod === "telegram" ? "active" : ""
            }`}
            onClick={() => setLoginMethod("telegram")}
          >
            Login with Telegram
          </button>
          <button
            className={`login-screen__tab ${
              loginMethod === "privateKey" ? "active" : ""
            }`}
            onClick={() => setLoginMethod("privateKey")}
          >
            Login with Private Key
          </button>
        </div>
        <div className="login-screen__form-container">
          {loginMethod === "telegram" ? (
            <button className="login-screen__button" onClick={handleLogin}>
              Login with Telegram
            </button>
          ) : (
            <div className="login-screen__form">
              <input
                type="password"
                placeholder="Private Key(starting with 0x)"
                value={privateKey}
                onChange={handlePrivateKeyChange}
                className="login-screen__input"
              />
              {privateKeyError && (
                <p className="login-screen__error">{privateKeyError}</p>
              )}
              <input
                type="text"
                placeholder="Wallet Address"
                value={walletAddress}
                onChange={handleWalletAddressChange}
                className="login-screen__input"
              />
              {walletAddressError && (
                <p className="login-screen__error">{walletAddressError}</p>
              )}
              <button
                className="login-screen__button login-screen__button--private-key"
                onClick={handleLogin}
                disabled={
                  !privateKey ||
                  !walletAddress ||
                  privateKeyError ||
                  walletAddressError
                }
              >
                Login
              </button>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

export default TelegramLogin;

The app fails to load and i get this error which i am unable to debug.

bool.ts:13 Uncaught TypeError: (0 , superstruct_1.boolean) is not a function
    at ./node_modules/@metamask/abi-utils/dist/parsers/bool.js (bool.ts:13:1)
    at options.factory (react refresh:6:1)
    at __webpack_require__ (bootstrap:22:1)
    at fn (hot module replacement:61:1)
    at ./node_modules/@metamask/abi-utils/dist/parsers/index.js (index.ts:3:1)
    at options.factory (react refresh:6:1)
    at __webpack_require__ (bootstrap:22:1)
    at fn (hot module replacement:61:1)
    at ./node_modules/@metamask/abi-utils/dist/packer.js (packer.ts:10:1)
    at options.factory (react refresh:6:1)

When i remove the ethereumKeyProvider part and try to run without it , it runs but fails otherwise. Its a create-react-app that i have. How do i fix this issue ?

Hi Abhishek,

Thanks for sharing the details. It helps a lot!

It looks like you’re encountering an issue with the EthereumPrivateKeyProvider and how it’s integrated. The error you’re seeing:

bool.ts:13 Uncaught TypeError: (0 , superstruct_1.boolean) is not a function
  ...

indicates there’s a problem with the @metamask/abi-utils package, which is likely a dependency of @web3auth/ethereum-provider.

To help the support team address this efficiently, could you provide the following additional details?

  • Code snippets: Specifically, focus on how you’re initializing and using EthereumPrivateKeyProvider with Web3Auth.
  • Browser console screenshots: Sometimes, additional context in the console can help identify the root cause.

However, I have a preliminary suggestion to help you debug this:

Possible Solution

  1. Check Dependencies:

    • Ensure all your dependencies are up-to-date. Sometimes, dependency mismatches cause issues.
    npm update
    
  2. Isolation Test:

    • Remove/comment out the lines involving EthereumPrivateKeyProvider and see if your app runs. Reintroduce it gradually to pinpoint where it breaks.
  3. Dependency Override:

    • Specifically, try to directly add the superstruct package to ensure it’s the correct version.
    npm install superstruct
    
  4. Verification in Development Environment:

    • Make sure your NODE_ENV variables in .env files are properly set for local development as they might influence the behaviour.

Example Code in Context

Here’s the relevant snippet from your provided code with some corrections and comments for clarity:

useEffect(() => {
  const init = async () => {
    try {
      // If you're directly using environment variables, ensure they're correctly defined in your .env file
      const chainConfig = {
        chainId: process.env.REACT_APP_WEB3AUTH_CHAIN_ID,
        rpcTarget: process.env.REACT_APP_RPC_URL,
      };

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

      // Initializing Web3Auth Single Factor Auth SDK
      const web3authSfa = new Web3Auth({
        clientId, 
        web3AuthNetwork: WEB3AUTH_NETWORK.TESTNET, 
        usePnPKey: false, 
        privateKeyProvider
      });

      setWeb3authSfa(web3authSfa);
      await web3authSfa.init({});
      
    } catch (error) {
      console.error(error);
    }
  };

  init();
}, []);

Hopefully, these steps and this example provide a clearer path forward for resolving the error.

This response was generated by the Web3Auth Bot, and a person from the Web3Auth team will respond to your query as well. Please stay tuned for further assistance.

Thanks for replying @w3abot , i ran npm run update and its still giving me the same issue.

running npm install superstruct gives me this error

npm ERR! code ERESOLVE
npm ERR! ERESOLVE could not resolve
npm ERR! 
npm ERR! While resolving: react-scripts@5.0.1     
npm ERR! Found: typescript@5.5.4
npm ERR! node_modules/typescript
npm ERR!   peerOptional typescript@">=5.0.4" from 
abitype@1.0.5
npm ERR!   node_modules/abitype
npm ERR!     abitype@"1.0.5" from viem@2.17.11    
npm ERR!     node_modules/viem
npm ERR!       viem@"^2.16.3" from axr-erc4337-sdk@0.1.31
npm ERR!       node_modules/axr-erc4337-sdk       
npm ERR!         axr-erc4337-sdk@"^0.1.31" from the root project
npm ERR!       1 more (permissionless)
npm ERR!   peer typescript@">= 2.7" from fork-ts-checker-webpack-plugin@6.5.3
npm ERR!   node_modules/fork-ts-checker-webpack-plugin
npm ERR!     fork-ts-checker-webpack-plugin@"^6.5.0" from react-dev-utils@12.0.1
npm ERR!     node_modules/react-dev-utils
npm ERR!       react-dev-utils@"^12.0.1" from react-scripts@5.0.1
npm ERR!       node_modules/react-scripts
npm ERR!         peer react-scripts@">=2.1.3" from react-app-rewired@2.2.1
npm ERR!         node_modules/react-app-rewired   
npm ERR!         1 more (the root project)        
npm ERR!   2 more (tsutils, viem)
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peerOptional typescript@"^3.2.1 || ^4" from react-scripts@5.0.1
npm ERR! node_modules/react-scripts
npm ERR!   peer react-scripts@">=2.1.3" from react-app-rewired@2.2.1
npm ERR!   node_modules/react-app-rewired
npm ERR!     dev react-app-rewired@"^2.2.1" from the root project
npm ERR!   react-scripts@"5.0.1" from the root project
npm ERR! 
npm ERR! Conflicting peer dependency: typescript@4.9.5
npm ERR! node_modules/typescript
npm ERR!   peerOptional typescript@"^3.2.1 || ^4" 
from react-scripts@5.0.1
npm ERR!   node_modules/react-scripts
npm ERR!     peer react-scripts@">=2.1.3" from react-app-rewired@2.2.1
npm ERR!     node_modules/react-app-rewired       
npm ERR!       dev react-app-rewired@"^2.2.1" from the root project
npm ERR!     react-scripts@"5.0.1" from the root project
npm ERR!
npm ERR! Fix the upstream dependency conflict, or 
retry
npm ERR! this command with --force or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR!
npm ERR!
npm ERR! For a full report see:
npm ERR! C:\Users\abhis\AppData\Local\npm-cache\_logs\2024-08-30T06_50_40_267Z-eresolve-report.txt  

npm ERR! A complete log of this run can be found in: C:\Users\abhis\AppData\Local\npm-cache\_logs\2024-08-30T06_50_40_267Z-debug-0.log

And yes , removing ethereumKeyProvider does run the app without any issue

Hey Abhishek,

As I looked into this issue, this is related to the latest changes done by the package we are depending upon, ie. @metamask/abi-utils.

Since Create React App is deprecated, most of the libraries have stopped support for it. You can migrate to Vite, since that will be ultimate the solution for this issue.
Have a look at this blog: How to Migrate from Create React App to Vite

Once migrated to Vite, this issue will be resolved.

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