Facing issues in xrp integration

Am trying to integrate xrp pnp sdk into our webapp.

here’s the link from docs and I got two major concerns

  1. Hosted Xrp examples present on above docs page are actually throwing errors so wanted to know are they the latest one?
  2. I tried following exact commands from xrp integration github readme but got node_modules corrupt error so wanted to understand if that’s the latest repo and the correct one?

Though by making some changes I was able to make the auth work but can’t do further actions like getting user acc details or signing txns.
I think its related to rpc somewhere
sorry if am doing anything wrong so pls can someone guide me in how to resolve it?
all errors and steps mentioned in below
App.tsx

import { useEffect, useState } from "react";
import { Web3Auth, Web3AuthOptions } from "@web3auth/modal";
import {
  CHAIN_NAMESPACES,
  IProvider,
  UX_MODE,
  WEB3AUTH_NETWORK,
} from "@web3auth/base";
import { AuthAdapter } from "@web3auth/auth-adapter";
import { XrplPrivateKeyProvider } from "@web3auth/xrpl-provider";
import RPC from "./xrplRPC";
import { Web3AuthNoModal } from "@web3auth/no-modal";
import "./App.css";

const clientId =
  ""; 

const chainConfig = {
  chainNamespace: CHAIN_NAMESPACES.XRPL,
  chainId: "0x2",
  rpcTarget: "https://testnet.xrpl-labs.com/", // needed to change rpcs mentioned in docs becoz they were throwing errors connecting to them
  wsTarget: "wss://testnet.xrpl-labs.com/",
  ticker: "XRP",
  tickerName: "XRPL",
  displayName: "xrpl testnet",
  blockExplorerUrl: "https://livenet.xrpl.org",
};

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

const web3AuthOptions: Web3AuthOptions = {
  clientId,
  web3AuthNetwork: WEB3AUTH_NETWORK.SAPPHIRE_DEVNET,
  privateKeyProvider,
};
const web3auth = new Web3Auth(web3AuthOptions);


function App() {
  
  const [provider, setProvider] = useState<IProvider | null>(null);
  const [loggedIn, setLoggedIn] = useState(false);

  useEffect(() => {
    const init = async () => {
      try {
        // IMP START - SDK Initialization
        await web3auth.initModal();
        // IMP END - SDK Initialization
        console.log("iniii", web3auth.provider);
        setProvider(web3auth.provider);

        if (web3auth.connected) {
          setLoggedIn(true);
        }
      } catch (error) {
        console.error(error);
      }
    };

    init();
  }, []);

  const login = async () => {
    if (!web3auth) {
      uiConsole("web3auth not initialized yet");
      return;
    }
    const web3authProvider = await web3auth.connect();
    console.log("authh", web3authProvider);
    setProvider(web3authProvider!);
    setLoggedIn(true);
  };

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

  const getUserInfo = async () => {
    if (!web3auth) {
      uiConsole("web3auth not initialized yet");
      return;
    }
    const user = await web3auth.getUserInfo();
    uiConsole(user);
  };

  const logout = async () => {
    if (!web3auth) {
      uiConsole("web3auth not initialized yet");
      return;
    }
    await web3auth.logout();
    setProvider(null);
    setLoggedIn(false);
  };

  const getAccounts = async () => {
    const user = await web3auth.getUserInfo();
    console.log(user);

    try {
     // I tried using xrplRPC file function here as well but not working so I pasted exact code here for your reference
     //NOTE: unable to call .request() as its not provided in provider

     //ERROR : App.tsx:562 Error JsonRpcError: Cannot read properties of undefined (reading 'call')  at getJsonRpcError 
      const accounts = await privateKeyProvider.request({
        method: "xrpl_getAccounts",
      });

      console.log(accounts);

      if (accounts) {
        const accInfo = (await privateKeyProvider.request({
          method: "account_info",
          params: [
            {
              account: accounts[0],
              strict: true,
              ledger_index: "current",
              queue: true,
            },
          ],
        })) as Record<string, Record<string, string>>;
        console.log("XRPL account info", accInfo);
        // XRPL Account
        const account = accInfo?.account_data?.Account;
        // Balance
        const balance = accInfo?.account_data?.Balance;
      } else {
        console.log("No accounts found, please report this issue.");
      }
    } catch (error) {
      console.error("Error", error);
    }
  };

  const getBalance = async () => {
    if (!provider) {
      uiConsole("provider not initialized yet");
      return;
    }
    const rpc = new RPC(provider);
    const balance = await rpc.getBalance();
    uiConsole("Balance", balance);
  };

  const sendTransaction = async () => {
    if (!provider) {
      uiConsole("provider not initialized yet");
      return;
    }
    const rpc = new RPC(provider);
    const result = await rpc.signAndSendTransaction();
    uiConsole(result);
  };

  const signMessage = async () => {
    if (!provider) {
      uiConsole("provider not initialized yet");
      return;
    }
    const rpc = new RPC(provider);
    const result = await rpc.signMessage();
    uiConsole(result);
  };

  function uiConsole(...args: any[]): void {
    const el = document.querySelector("#console>p");
    if (el) {
      el.innerHTML = JSON.stringify(args || {}, null, 2);
    }
  }

  const loggedInView = (
    <>
      <div className="flex-container">
        <div>
          <button onClick={getUserInfo} className="card">
            Get User Info
          </button>
        </div>
        <div>
          <button onClick={authenticateUser} className="card">
            Get ID Token
          </button>
        </div>
        <div>
          <button onClick={getAccounts} className="card">
            Get Accounts
          </button>
        </div>
        <div>
          <button onClick={getBalance} className="card">
            Get Balance
          </button>
        </div>
        <div>
          <button onClick={signMessage} className="card">
            Sign Message
          </button>
        </div>
        <div>
          <button onClick={sendTransaction} className="card">
            Send Transaction
          </button>
        </div>
        <div>
          <button onClick={logout} className="card">
            Log Out
          </button>
        </div>
      </div>
      <div id="console" style={{ whiteSpace: "pre-line" }}>
        <p style={{ whiteSpace: "pre-line" }}></p>
      </div>
    </>
  );

  const unloggedInView = (
    <button onClick={login} className="card">
      Login
    </button>
  );

  return (
    <div className="container">
      <h1 className="title">
        <a
          target="_blank"
          href="https://web3auth.io/docs/sdk/pnp/web/modal"
          rel="noreferrer"
        >
          Web3Auth{" "}
        </a>
        & React XRPL Example
      </h1>

      <div className="grid">{loggedIn ? loggedInView : unloggedInView}</div>

      <footer className="footer">
        <a
          href="https://github.com/Web3Auth/web3auth-pnp-examples/tree/main/web-modal-sdk/blockchain-connection-examples/xrpl-modal-example"
          target="_blank"
          rel="noopener noreferrer"
        >
          Source code
        </a>
        <a href="https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FWeb3Auth%2Fweb3auth-pnp-examples%2Ftree%2Fmain%2Fweb-modal-sdk%2Fblockchain-connection-examples%2Fxrpl-modal-example&project-name=w3a-xrpl-modal&repository-name=w3a-xrpl-modal">
          <img src="https://vercel.com/button" alt="Deploy with Vercel" />
        </a>
      </footer>
    </div>
  );
}

export default App;

xrplRPC.ts

import { IProvider } from "@web3auth/base";
import { convertStringToHex, Payment, xrpToDrops } from "xrpl";

export default class XrplRPC {
  private provider: IProvider;

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

  getAccounts = async (): Promise<any> => {
    try {
      const accounts = await this.provider.request<never, string[]>({
        method: "xrpl_getAccounts",
      });
      if (accounts) {
        const accInfo = await this.provider.request({
          method: "account_info",
          params: [
            {
              account: accounts[0],
              strict: true,
              ledger_index: "current",
              queue: true,
            },
          ],
        });
        return accInfo;
      } else {
        return "No accounts found, please report this issue.";
      }
    } catch (error) {
      console.error("Error", error);
      return error;
    }
  };

  getBalance = async (): Promise<any> => {
    try {
      const accounts = await this.provider.request<string[], never>({
        method: "xrpl_getAccounts",
      });

      if (accounts) {
        const accInfo = (await this.provider.request({
          method: "account_info",
          params: [
            {
              account: accounts[0],
              strict: true,
              ledger_index: "current",
              queue: true,
            },
          ],
        })) as Record<string, Record<string, string>>;
        return accInfo.account_data?.Balance;
      } else {
        return "No accounts found, please report this issue.";
      }
    } catch (error) {
      console.error("Error", error);
      return error;
    }
  };

  signMessage = async (): Promise<any> => {
    try {
      const msg = "Hello world";
      const hexMsg = convertStringToHex(msg);
      const txSign = await this.provider.request<{ signature: string }, never>({
        method: "xrpl_signMessage",
        params: {
          signature: hexMsg,
        },
      });
      return txSign;
    } catch (error) {
      console.log("error", error);
      return error;
    }
  };

  signAndSendTransaction = async (): Promise<any> => {
    try {
      const accounts = await this.provider.request<never, string[]>({
        method: "xrpl_getAccounts",
      });

      if (accounts && accounts.length > 0) {
        const tx: Payment = {
          TransactionType: "Payment",
          Account: accounts[0] as string,
          Amount: xrpToDrops(50),
          Destination: "rM9uB4xzDadhBTNG17KHmn3DLdenZmJwTy",
        };
        const txSign = await this.provider.request({
          method: "xrpl_submitTransaction",
          params: {
            transaction: tx,
          },
        });
        return txSign;
      } else {
        return "failed to fetch accounts";
      }
    } catch (error) {
      console.log("error", error);
      return error;
    }
  };
}

Hi @prabhpreetsingh.sing,
Just fixed the XRPL examples. The signMessage method had the wrong parameter name (now using ‘message’ instead of ‘signature’), and we’ve updated the RPC endpoints to testnet.xrpl-labs.com. Pull the latest code from GitHub and you should be good to go.
Thank you for mentioning this.

Hey @maharshi thanks for the update, I tried running the latest code and acc creation, balance fetching is working fine but rather on signAndSendTransaction func am getting error
error JsonRpcError: Cannot read properties of null (reading ‘createHash’)
at getJsonRpcError (chunk-DIK7U36M.js?v=be638401:22784:10)
at Object.internal (chunk-DIK7U36M.js?v=be638401:22825:22)
at provider.sendAsync (chunk-DIK7U36M.js?v=be638401:23459:23)
at async provider.request (chunk-DIK7U36M.js?v=be638401:23479:17)
at async XrplRPC.signAndSendTransaction (xrplRPC.ts:97:24)
at async sendTransaction (App.tsx:173:20)

updated fn is

signAndSendTransaction = async (): Promise<any> => {
    try {
      const accounts = await this.provider.request<never, string[]>({
        method: "xrpl_getAccounts",
      });
      console.log(accounts)
      if (accounts && accounts.length > 0) {
        const tx: Payment = {
          TransactionType: "Payment",
          Account: accounts[0] as string,
          Amount: xrpToDrops(50),
          Destination: "r9kiyqEux5WH56GvUKVskwtxaQDiK9nZMA",
        };
        const txSign = await this.provider.request({
          method: "xrpl_submitTransaction",
          params: {
            transaction: tx,
          },
        });
        return txSign;
      } else {
        return "failed to fetch accounts";
      }
    } catch (error) {
      console.log("error", error);
      return error;
    }
  };
}

Hey @prabhpreetsingh.sing
Can you please share the output for get accounts method?

Here you go

[
"Account info: ",
{
"account_data": {
"Account": "rDNqDX3Bp3Ht28R6jr3jLc57wde6NgrwEk",
"Balance": "10000000",
"Flags": 0,
"LedgerEntryType": "AccountRoot",
"OwnerCount": 0,
"PreviousTxnID": "E4FEC7D05B7AA2E0CEC4C8998B0BA7EDC6BE47A92748DDEC77A71F57DF79114B",
"PreviousTxnLgrSeq": 5704096,
"Sequence": 5704096,
"index": "2DDE1923F0866B1A474CBDD27F3EF29D684BC2AB6B7D27802ADD4EDBE21029FA"
},
"account_flags": {
"allowTrustLineClawback": false,
"defaultRipple": false,
"depositAuth": false,
"disableMasterKey": false,
"disallowIncomingCheck": false,
"disallowIncomingNFTokenOffer": false,
"disallowIncomingPayChan": false,
"disallowIncomingTrustline": false,
"disallowIncomingXRP": false,
"globalFreeze": false,
"noFreeze": false,
"passwordSpent": false,
"requireAuthorization": false,
"requireDestinationTag": false
},
"ledger_current_index": 5705247,
"queue_data": {
"txn_count": 0
},
"status": "success",
"validated": false
}
]

Hey @prabhpreetsingh.sing
I’ll be adding it to our examples as well but in the mean time please add crypto-browserify to the aliases in vite.config.ts to resolve this issue.
The resolve section in the vite.config.ts should look like this post the recommended changes:

resolve: {
    alias: {
      crypto: "crypto-browserify",
      assert: "assert",
      http: "empty-module",
      https: "empty-module",
      os: "empty-module",
      url: "empty-module",
      zlib: "empty-module",
      stream: "stream-browserify",
    },
  },

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