Invalid ID - WalletConnect V2 Migration

Hello, I have a problem migrating WalletConnect V2.

Because wallet connects V1 is deprecated already I made a new function that triggers the Wallet Connect V2 modal.

const connectWithWalletConnect = async () => {
console.log("Attempting to connect with WalletConnect...");
try {
const projectId = WALLET_CONNECT_PROJECT_ID;
console.log("Project ID:", projectId);

console.log("Calling enableWeb3...");
await enableWeb3({
provider: "walletconnect",
projectId: projectId,
newSession: true,
qrModalOptions: {
themeMode: "dark",
}
});
console.log("enableWeb3 call successful");
console.log("Connected successfully with WalletConnect!");
} catch (error) {
console.error(
"An error occurred while connecting with WalletConnect:",
error
);
}
};

This is the function and it actually works correctly. It prompts the WalletConnect V2 modal and after I scan the QR code with mobile MetaMask it prompts me to connect my wallet.

The problem comes when a gas-free signature should be invoked to verify this wallet is eligible to connect to my dApp. Here is the code with two different methods SEND and REQUEST.

export const $sign = async ({
provider,
account,
message = "Hello, \nProtoStake uses a cryptographic signature to \nauthenticate your wallet address and will never \nrequest access to your funds. The signature request \nis a gas-free transaction.",
}: {
provider: any;
account: string | null;
message?: string;
}) => {
const timestamp = (new Date().getTime() / 1e3).toFixed(0);
console.log("Timestamp:", timestamp);
console.log("Account:", account);
let signature: string | null = null; // Initialize it to null
const payload = [
`${message}\n\nTimestamp: ${timestamp}`,
account,
];

console.log("Payload:", payload);
try {
let result: any;
if (typeof provider.send === 'function') {
console.log("Using provider.send method.");
result = await provider.send("personal_sign", payload);
} else if (typeof provider.request === 'function') {
console.log("Using provider.request method.");
result = await provider.request({
method: "personal_sign",
params: payload
});
} else {
throw new Error('Provider does not support the send or request methods');
}
console.log("Result from provider:", result);
if (typeof result === 'string') {
signature = result;
} else if (result && typeof result === 'object' && 'result' in result) {
signature = result.result as string;
}
} catch (error) {
console.error('Error getting signature:', error);
}
if (!signature) throw Error("Unable to get a valid signature");
console.log("Final signature:", signature);
return { signature, timestamp };
};

After I connect my Wallet I get in the console the following errors:

The problem is that the Client context is returning an invalid ID, which makes no sense.

The strangest part is when I connect my wallet it does not prompt me with a gas-free signature, but when I switch to another page in my dApp it prompts me to sign a gas-free signature in my wallet and it connects perfectly without throwing me the error for InvalidID e.t.c.

Any ideas?

what is the provider here? how do you set it?

Plus something additional to be considered, when I am testing the project locally in the dev environment I am getting the error “Unable to get a valid signature”, which is coming from here:

if (!signature) throw Error("Unable to get a valid signature");

Here it is:

import {
  Chain,
  Token,
  PoolExists,
  Validateable,
  Address,
  FileType,
} from "./types";
import millify from "millify";
import Moralis from "moralis";
import BigNumber from "bignumber.js";

export const $openTab = (url: string) => {
  if (!window) return;
  window.open(url, "_blank");
};

export const $mask = (address: string) => {
  if (!address) return address;
  if (address.length < 6) return `Token ID:   ${address}`;
  let first4 = address.substring(0, 5);
  let last5 = address.substring(address.length - 5);
  let mask = "••••";
  return (address = first4 + mask + last5);
};

export let APP_ENV = "process.env.APP_ENV";

export const ALLOWED_CHAINS: Chain[] =
  APP_ENV == "mainnet"
    ? [
      {
          name: "Arbitrum",
          networkId: "0xa4b1",
          explorerUrl: "https://arbiscan.io/",
          shortName: "ARB",
          icon: {
            url: "https://cdn.protoverse.ai/assets/ProtoVerse/arb2.svg",
          },
          content: "Choose this option if you want to mint on Arbitrum",
        },
        {
          name: "Ethereum",
          networkId: "0x1",
          explorerUrl: "https://etherscan.io",
          shortName: "ETH",
          icon: {
            url: "https://cdn.protoverse.ai/assets/theme/eth-icon.svg",
          },
          content: "Choose this option if you want to mint on Ethereum",
        },
        {
          name: "Binance Smart Chain",
          networkId: "0x38",
          explorerUrl: "https://bscscan.com",
          shortName: "BSC",
          icon: {
            url: "https://cdn.protoverse.ai/assets/theme/bsc-icon.svg",
          },
          content: "Choose this option if you want to mint on BSC",
        },
        {
          name: "Polygon",
          networkId: "0x89",
          explorerUrl: "https://polygonscan.com",
          shortName: "MATIC",
          icon: {
            url: "https://cdn.protoverse.ai/assets/theme/poly-icon.svg",
          },
          content: "Choose this option if you want to mint on Polygon",
        },
        {
          name: "Proof Of Memes",
          networkId: "0x46ef",
          icon: { url: "https://cdn.protoverse.ai/assets/ProtoVerse/pom.svg" },
          shortName: "POM",
          explorerUrl: "https://explorer.memescan.io/",
          content:
            "Choose this option if you want to deploy the pool on Proof of memes.",
        },
      ]
    : [
        {
          name: "Polygon Testnet",
          networkId: "0x13881",
          explorerUrl: "https://mumbai.polygonscan.com",
          shortName: "Matic",
          icon: {
            url: "https://cdn.protoverse.ai/assets/theme/poly-icon.svg",
          },
          content: "Choose this option if you want to mint on Polygon",
        },
        {
          name: "Binance Testnet",
          networkId: "0x61",
          icon: {
            url: "https://cdn.protoverse.ai/assets/theme/bsc-icon.svg",
          },
          shortName: "BNB",
          explorerUrl: "https://testnet.bscscan.com",
          content: "Choose this option if you want to mint on BSC",
        },

        {
          name: "Ethereum Testnet",
          networkId: "0x5",
          icon: { url: "https://cdn.protoverse.ai/assets/theme/eth-icon.svg" },
          shortName: "ETH",
          explorerUrl: "https://goerli.etherscan.io/",
          content: "Choose this option if you want to mint on Ethereum",
        },
        {
          name: "Proof Of Memes Testnet",
          networkId: "0xd649",
          icon: { url: "https://cdn.protoverse.ai/assets/ProtoVerse/pom.svg" },
          shortName: "POM",
          explorerUrl: "https://testnet-explorer.memescan.io/",
          content:
            "Choose this option if you want to deploy the pool on Proof of memes.",
        },
        {
          name: "Arbitrum Testnet",
          networkId: "0x66eed",
          icon: { url: "https://cdn.protoverse.ai/assets/ProtoVerse/arb2.svg" },
          shortName: "ARBT",
          explorerUrl: "https://goerli.arbiscan.io",
          content:
            "Choose this option if you want to deploy the pool on Arbitrum Testnet.",
        },
      ];

export const ALLOWED_TOKENS: Token[] =
  APP_ENV == "mainnet"
    ? [
        // BSC
        {
          isNative: true,
          name: "Binance",
          symbol: "BNB",
          decimals: 18,
          address: "0x0000000000000000000000000000000000000000",
          chainId: "0x38",
          icon: {
            url: "https://cdn.protoverse.ai/assets/theme/bsc-icon.svg",
          },
        },
        {
          name: "BUSD",
          icon: {
            url: "https://cdn.protoverse.ai/assets/theme/network-busd-icon.svg",
          },
          address: "0xe9e7cea3dedca5984780bafc599bd69add087d56",
          symbol: "BUSD",
          decimals: 18,
          chainId: "0x38",
        },
        {
          name: "DAI",
          icon: {
            url: "https://cdn.protoverse.ai/assets/theme/network-dai-icon.svg",
          },
          address: "0x1af3f329e8be154074d8769d1ffa4ee058b1dbc3",
          symbol: "DAI",
          decimals: 18,
          chainId: "0x38",
        },
        {
          name: "USDC",
          icon: {
            url: "https://cdn.protoverse.ai/assets/theme/network-usdc-icon.svg",
          },
          address: "0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d",
          symbol: "USDC",
          decimals: 18,
          chainId: "0x38",
        },
        {
          name: "USDT",
          icon: {
            url: "https://cdn.protoverse.ai/assets/theme/network-usdt-icon.svg",
          },
          address: "0x55d398326f99059ff775485246999027b3197955",
          symbol: "USDT",
          decimals: 18,
          chainId: "0x38",
        },

        // Polygon
        {
          isNative: true,
          name: "Matic",
          symbol: "MATIC",
          decimals: 18,
          address: "0x0000000000000000000000000000000000000000",
          chainId: "0x89",
          icon: {
            url: "https://cdn.protoverse.ai/assets/theme/poly-chain-black-bg.svg",
          },
        },
        {
          name: "BUSD",
          icon: {
            url: "https://cdn.protoverse.ai/assets/theme/network-busd-icon.svg",
          },
          address: "0xdab529f40e671a1d4bf91361c21bf9f0c9712ab7",
          symbol: "BUSD",
          decimals: 18,
          chainId: "0x89",
        },
        {
          name: "DAI",
          icon: {
            url: "https://cdn.protoverse.ai/assets/theme/network-dai-icon.svg",
          },
          address: "0x8f3cf7ad23cd3cadbd9735aff958023239c6a063",
          symbol: "DAI",
          decimals: 18,
          chainId: "0x89",
        },
        {
          name: "USDC",
          icon: {
            url: "https://cdn.protoverse.ai/assets/theme/network-usdc-icon.svg",
          },
          address: "0x2791bca1f2de4661ed88a30c99a7a9449aa84174",
          symbol: "USDC",
          decimals: 6,
          chainId: "0x89",
        },
        {
          name: "USDT",
          icon: {
            url: "https://cdn.protoverse.ai/assets/theme/network-usdt-icon.svg",
          },
          address: "0xc2132d05d31c914a87c6611c10748aeb04b58e8f",
          symbol: "USDT",
          decimals: 6,
          chainId: "0x89",
        },
// ETH

{
  isNative: true,
  name: "ETH",
  symbol: "ETH",
  decimals: 18,
  address: "0x0000000000000000000000000000000000000000",
  chainId: "0xa4b1", 
  icon: {
    url: "https://cdn.protoverse.ai/assets/theme/eth-icon.svg",
  },
},
        {
          isNative: true,
          name: "ETH",
          symbol: "ETH",
          decimals: 18,
          address: "0x0000000000000000000000000000000000000000",
          chainId: "0x1", 
          icon: {
            url: "https://cdn.protoverse.ai/assets/theme/eth-icon.svg",
          },
        },
        // ETH
        {
          isNative: true,
          name: "ETH",
          symbol: "ETH",
          decimals: 18,
          address: "0x0000000000000000000000000000000000000000",
          chainId: "0x1", 
          icon: {
            url: "https://cdn.protoverse.ai/assets/theme/eth-icon.svg",
          },
        },

        {
          name: "BUSD",
          icon: {
            url: "https://cdn.protoverse.ai/assets/theme/network-busd-icon.svg",
          },
          address: "0x4Fabb145d64652a948d72533023f6E7A623C7C53",
          symbol: "BUSD",
          decimals: 18,
          chainId: "0x1",
        },

        // {
        //   name: "USDT",
        //   icon: {
        //     url: "https://cdn.protoverse.ai/assets/theme/network-usdt-icon.svg",
        //   },
        //   address: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
        //   symbol: "USDT",
        //   decimals: 6,
        //   chainId: "0x1",
        // },

        {
          name: "USDC",
          icon: {
            url: "https://cdn.protoverse.ai/assets/theme/network-usdc-icon.svg",
          },
          address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
          symbol: "USDC",
          decimals: 6,
          chainId: "0x1",
        },
      ]
    : [
        {
          isNative: true,
          name: "Matic",
          symbol: "MATIC",
          decimals: 18,
          address: "0x0000000000000000000000000000000000000000",
          chainId: "0x13881",
          icon: {
            url: "https://cdn.protoverse.ai/assets/theme/poly-chain-black-bg.svg",
          },
        },
        {
          isNative: true,
          name: "Binance",
          symbol: "BNB",
          decimals: 18,
          address: "0x0000000000000000000000000000000000000000",
          chainId: "0x61",
          icon: {
            url: "https://cdn.protoverse.ai/assets/theme/bsc-icon.svg",
          },
        },
        {
          isNative: true,
          name: "Ethereum",
          symbol: "ETH",
          decimals: 18,
          address: "0x0000000000000000000000000000000000000000",
          chainId: "0x3",
          icon: {
            url: "https://cdn.protoverse.ai/assets/theme/eth-icon.svg",
          },
        },
        {
          name: "Staker Token",
          symbol: "Staker",
          decimals: 18,
          address: "0xc55c1c75997d763660515ce31b003B77b5E914B3",
          chainId: "0x13881",
          icon: {
            url: "https://cdn.protoverse.ai/assets/ProtoVerse/stake-icon.svg",
          },
        },
        {
          name: "Rewarder Token",
          symbol: "Rewarder",
          decimals: 18,
          address: "0xFb2c09c9D172a2f18f8dd4de137b3A1b2A153dC1",
          chainId: "0x13881",
          icon: {
            url: "https://cdn.protoverse.ai/assets/ProtoVerse/stake-icon.svg",
          },
        },
      ];

const dAppChainIds: any = {
  ProtoStake: [
    "0x89",
    "0x13881",
    "0x1",
    "0x38",
    "0x89",
    "0x61",
    "0x3",
    "0x5",
    "0x46ef",
    "0xd649",
    "0xa4b1",
    "0x66eed",
  ],
  ProtoMint: ["0x89", "0x13881", "0x1", "0x38", "0x89", "0x61", "0x3", "0x5", "0xa4b1", "0x66eed",],
  ProtoRace: ["0x89", "0x13881", "0x1", "0x38", "0x89", "0x61", "0x3", "0x5", "0xa4b1", "0x66eed",],
  Cryptopia: ["0x89", "0x13881", "0x1", "0x38", "0x89", "0x61", "0x3", "0x5", "0xa4b1", "0x66eed",],
  Mint8: ["0x89", "0x13881", "0x1", "0x38", "0x89", "0x61", "0x3", "0x5", "0xa4b1", "0x66eed",],
  Generic: [
    "0x89",
    "0x13881",
    "0x1",
    "0x38",
    "0x89",
    "0x61",
    "0x3",
    "0x5",
    "0x46ef",
    "0xd649",
    "0xa4b1",
    "0x66eed",
  ],
};

export const $chain = (chainId: string, dAppId: string) => {
  return ALLOWED_CHAINS.find(
    (chain) =>
      dAppChainIds[dAppId].includes(chain.networkId) &&
      chain.networkId == chainId
  );
};

export const $token = (address: string, chainId: string, dAppId: string) => {
  return ALLOWED_TOKENS.find(
    (token) =>
      dAppChainIds[dAppId].includes(token.chainId) &&
      token.chainId == chainId &&
      token.address.toLowerCase() === address?.toLowerCase()
  );
};

export const $quickMillify = (value: any) => {
  return millify(value, {
    precision: 2,
    decimalSeparator: ".",
  });
};

export const $fromWei = (
  weiAmount: any,
  decimal?: number,
  outDecimal?: number
) => {
  return Number(Moralis.Units.FromWei(weiAmount, decimal || 18)).toFixed(
    outDecimal || 4
  );
};

export const $toWei = (tokenAmount: any, decimal?: number) => {
  return Moralis.Units.Token(tokenAmount, decimal || 18);
};
export const $quickMillifyWei = (weiAmount: any, decimal: number) => {
  return $quickMillify($fromWei(weiAmount, decimal || 18));
};

export const $sign = async ({
  provider,
  account,
  message = "Hello, \nProtoStake uses a cryptographic signature to \nauthenticate your wallet address and will never \nrequest access to your funds. The signature request \nis a gas-free transaction.",
}: {
  provider: any;
  account: string | null;
  message?: string;
}) => {
  const timestamp = (new Date().getTime() / 1e3).toFixed(0);

  let signature: string | null = null; // Initialize it to null

  try {
    const payload = [
      `${message}\n\nTimestamp: ${timestamp}`,
      account,
    ];
    
    let result: any;

    if (typeof provider.send === 'function') {
      result = await provider.send("personal_sign", payload);
    } else if (typeof provider.request === 'function') {
      result = await provider.request({
        method: "personal_sign",
        params: payload
      });
    } else {
      throw new Error('Provider does not support the send or request methods');
    }

    if (typeof result === 'string') {
      signature = result;
    } else if (result && typeof result === 'object' && 'result' in result) {
      signature = result.result as string;
    }
  } catch (error) {
    console.error('Error getting signature:', error);
  }

  if (!signature) throw Error("Unable to get a valid signature");

  return { signature, timestamp };
};



export const $addHours = (date: any, h: any) => {
  date.setTime(date.getTime() + h * 60 * 60 * 1000);
  return date.getTime();
};

export const $apy = (
  rewardSupplyWei: string,
  totalActiveWei: string,
  factor: string = "1"
) => {
  if (new BigNumber(totalActiveWei).isZero()) return 0;
  return Number(
    Number(
      new BigNumber(rewardSupplyWei)
        .div(new BigNumber(totalActiveWei))
        .times(100)
        .times(factor)
        .toString()
    ).toFixed(4)
  );
};

export const $estimateEarnings = (
  stakeWei: string,
  earnedWei: string,
  totalStakedWei: string,
  poolExists: PoolExists,
  currentBlockNumber?: number
) => {
  const someZero = [stakeWei, totalStakedWei, currentBlockNumber || "0"].some(
    (wei) => new BigNumber(wei).isZero()
  );
  if (!currentBlockNumber || someZero) return $fromWei(earnedWei);
  const stakedPercent = new BigNumber(stakeWei)
    .times(100)
    .div(new BigNumber(totalStakedWei));
  const totalBlocks = Number(poolExists.endsAt) - Number(poolExists.startsAt);
  const remainingBlocks = Number(poolExists.endsAt) - currentBlockNumber;
  const rewardRate = new BigNumber(poolExists.rewardSupply).div(totalBlocks);
  const rewardsForRemainingBlocks = new BigNumber(rewardRate).times(
    remainingBlocks
  );
  if (rewardsForRemainingBlocks.isZero() || stakedPercent.isZero())
    return $fromWei(earnedWei);
  const estimatedEarnings = new BigNumber(rewardsForRemainingBlocks)
    .times(new BigNumber(stakedPercent))
    .div(100);
  return $fromWei(
    estimatedEarnings.plus(new BigNumber(earnedWei)).toFixed(0) || "0"
  );
};

export const defaultValidatableProps: Validateable<any> = {
  onValid: () => {},
  validate: (_: any) => true,
  invalidate: () => {},
};

// Validations
// export const $isValidChainAddress = (address: string) => {
//   return isAddress(address);
// };

export const $isValidPassword = (password: string) => {
  return /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/.test(password);
};

export const isValidURL = (url: string) => {
  var pattern = new RegExp(
    "^(https?:\\/\\/)?" + // protocol
      "((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|" + // domain name
      "((\\d{1,3}\\.){3}\\d{1,3}))" + // OR ip (v4) address
      "(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*" + // port and path
      "(\\?[;&a-z\\d%_.~+=-]*)?" + // query string
      "(\\#[-a-z\\d_]*)?$",
    "i"
  ); // fragment locator
  return pattern.test(url);
};

export const $fileType = (fileName: string) => {
  const imageTypes = [
    ".jpg",
    ".jpeg",
    ".jfif",
    ".pjpeg",
    ".pjp",
    ".png",
    ".svg",
    ".webp",
    ".apng",
    ".avif",
  ];
  const videoTypes = [
    ".mpg",
    ".mp2",
    ".mpeg",
    ".mpe",
    ".mpv",
    ".ogg",
    ".mp4",
    ".m4p",
    ".m4v",
    ".avi",
    ".wmv",
    ".mov",
    ".qt",
    ".flv",
    ".swf",
  ];
  if (imageTypes.some((ext) => fileName.toLowerCase().endsWith(ext)))
    return FileType.IMAGE;
  if (videoTypes.some((ext) => fileName.toLowerCase().endsWith(ext)))
    return FileType.VIDEO;
  return FileType.OTHER;
};

export const findMintTx = (tx: any) => {
  const isTransfer = tx.logs.find(
    (log: any) =>
      log.topic0 ==
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" &&
      log.topic1
        ?.toLowerCase()
        .includes(
          "0x0000000000000000000000000000000000000000000000000000000000000000"
        )
  );
  if (isTransfer)
    return {
      tokenId: new BigNumber(isTransfer.topic3).toString() as string,
      address: isTransfer.address as Address,
    };
  const isTransferSingle = tx.logs.find(
    (log: any) =>
      log.topic0 ==
        "0xc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62" &&
      log.topic2
        ?.toLowerCase()
        .includes(
          "0x0000000000000000000000000000000000000000000000000000000000000000"
        )
  );

  if (isTransferSingle) {
    const [tokenId, amount] = Moralis.web3Library.utils.defaultAbiCoder.decode(
      ["uint256", "uint256"],
      isTransferSingle.data
    );
    return {
      tokenId: tokenId.toString() as string,
      address: isTransferSingle.address as Address,
    };
  }
};

.........................................

I will try to replicate this and see if i can make an example

Okay, let me know if I can help you with something additional.

I was able to sign a message in the following way. We are using ethers and enableWeb3 will return a JsonRpcSigner back, I will share the full code so you can get an idea of what i did

<!DOCTYPE html>
<html>
    <head>
        <title>Vanilla Boilerplate</title>
        <script src="https://cdn.jsdelivr.net/npm/@walletconnect/[email protected]/dist/index.umd.js"></script>
        <script>
            window.WalletConnectProvider = window["@walletconnect/ethereum-provider"];
        </script>
        <script src="https://unpkg.com/moralis-v1/dist/moralis.js"></script>
    </head>

    <body>
        <h1>Moralis Hello World!</h1>

        <button id="btn-login">Web 3 Login</button>
        <button id="btn-sign">Sign Message</button>
        <span>
            <pre id="user"></pre>
        </span>

        <script>
            /* Moralis init code */
            const serverUrl = "https://0wf.......erver";
            const appId = "Cv0LD5nuu........suTbpsWm";
            Moralis.start({ serverUrl, appId });

            /* Authentication code */
            let provider = null;
            let signer = null;
            async function handleAuth(provider) {
                provider = await Moralis.enableWeb3({
                    projectId: "6850fb8.......c679a460a6",
                    provider,
                    newSession: false,
                    rpcMap: { 1: "https://eth-ma........f1qoHgDzk" }, // OPTIONAL
                });
                signer = await provider.getSigner();

                console.log(signer);
            }

            // sign message

            async function signMessage() {
                const message = "Hello from Moralis!";
                // Sign the message
                const signature = await signer.signMessage(message);
                console.log("signature", signature);
            }

            document.getElementById("btn-login").onclick = () => handleAuth("walletconnect");
            document.getElementById("btn-sign").onclick = () => signMessage();
        </script>
    </body>
</html>

And this is how it looks like im my metamsk