`Wallet.generate()` not working when using through an NPM SDK but works fine when used directly in a React app

I am building an NPM SDK in which I need to generate a Fuel account using Wallet.generate() method which comes from fuels SDK.
When I try to generate the account using the same method in a React app, it works fine and account is successfully generated. But when I am trying to do the same through my NPM SDK which I am consuming in a React app, it throws me the following error -

error: TypeError: import_crypto2.default.randomBytes is not a function
at randomBytes (http://localhost:3000/node_modules/.vite/deps/@lyncworld_fuel-tap-sdk.js?v=bfe760eb:5432:67)
at Signer.generatePrivateKey (http://localhost:3000/node_modules/.vite/deps/@lyncworld_fuel-tap-sdk.js?v=bfe760eb:26578:190)
at Wallet.generate (http://localhost:3000/node_modules/.vite/deps/@lyncworld_fuel-tap-sdk.js?v=bfe760eb:29361:35)
at generateNewWallet (http://localhost:3000/node_modules/.vite/deps/@lyncworld_fuel-tap-sdk.js?v=bfe760eb:33013:44)
at http://localhost:3000/node_modules/.vite/deps/@lyncworld_fuel-tap-sdk.js?v=bfe760eb:33143:62 at Generator.next (<anonymous>)
at http://localhost:3000/node_modules/.vite/deps/@lyncworld_fuel-tap-sdk.js?v=bfe760eb:32595:65 at new Promise (<anonymous>)
at __async (http://localhost:3000/node_modules/.vite/deps/@lyncworld_fuel-tap-sdk.js?v=bfe760eb:32579:14)
at initializeLocalStorage (http://localhost:3000/node_modules/.vite/deps/@lyncworld_fuel-tap-sdk.js?v=bfe760eb:33124:44)

message: "import_crypto2.default.randomBytes is not a function"
stack: "TypeError: import_crypto2.default.randomBytes is not a function
at randomBytes (http://localhost:3000/node_modules/.vite/deps/@lyncworld_fuel-tap-sdk.js?v=bfe760eb:5432:67)
at Signer.generatePrivateKey (http://localhost:3000/node_modules/.vite/deps/@lyncworld_fuel-tap-sdk.js?v=bfe760eb:26578:190)
at Wallet.generate (http://localhost:3000/node_modules/.vite/deps/@lyncworld_fuel-tap-sdk.js?v=bfe760eb:29361:35)
at generateNewWallet (http://localhost:3000/node_modules/.vite/deps/@lyncworld_fuel-tap-sdk.js?v=bfe760eb:33013:44)
at http://localhost:3000/node_modules/.vite/deps/@lyncworld_fuel-tap-sdk.js?v=bfe760eb:33143:62
at Generator.next (<anonymous>)
at http://localhost:3000/node_modules/.vite/deps/@lyncworld_fuel-tap-sdk.js?v=bfe760eb:32595:65
at new Promise (<anonymous>)
at __async (http://localhost:3000/node_modules/.vite/deps/@lyncworld_fuel-tap-sdk.js?v=bfe760eb:32579:14)
at initializeLocalStorage (http://localhost:3000/node_modules/.vite/deps/@lyncworld_fuel-tap-sdk.js?v=bfe760eb:33124:44)"

I think this is because Wallet.generate() method uses crypto module to generate random bytes when generating an account and since crypto module works differently in browser environment and node environment, It is giving the error - that randomBytes is not a function.

How can I generate a new wallet using my NPM SDK?

I am using version 0.94.6 of fuels NPM package.

Here is the code snippets that I am using to generate the account -

import { Provider, Wallet, WalletUnlocked } from "fuels";

export const generateNewWallet = (provider: Provider) => {
  if (!provider) throw new Error("Provider is required for generating wallet.");

  const account: WalletUnlocked = Wallet.generate({ provider });
  const publicKey = account.address.toB256();
  const privateKey = account.privateKey;

  return {
    account,
    publicKey,
    privateKey,
  } as const;
};

Here is how I am creating the provider -

import { Provider } from "fuels";

const FUEL_RPC_URL_TESTNET = "https://testnet.fuel.network/v1/graphql";
const provider = await Provider.create(FUEL_RPC_URL_TESTNET);

Hey @bobby-lync :wave:

Our @fuel-ts/crypto package has a dual distribution mechanism (node and browser entrypoints) and to allow for cross platform functionality. Potentially what is happening is that the node distribution is making it’s way into your NPM bundle.

Could you share how you’re building your NPM package?

Hey @p.s

I am using tsup for building my NPM SDK. Here are the configurations for building the package using tsup -

import { defineConfig } from "tsup";

export default defineConfig({
  entry: ["./src/index.ts"],
  outDir: "package",
  format: ["cjs", "esm"],
  dts: true,
  shims: true,
  skipNodeModulesBundle: true,
  clean: true,
});

I am also putting the configurations of my tsconfig.json file -

{
  "compilerOptions": {
    "target": "es2016",
    "module": "commonjs",
    "rootDir": ".",
    "allowJs": false,
    "outDir": "package",
    "removeComments": true,
    "noEmit": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "noImplicitAny": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "skipLibCheck": true,
    "jsx": "react-jsx",
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  },
  "include": ["src"]
}

Could you try using the platform: 'browser' parameter?

This bundle the correct browser shims with the build.

Hey @p.s

The error has been resolved using platform: 'browser' when building the package. But I also added polyfills in the React app to completely resolve the error.

This is how my vite.config.ts file looks like after adding the polyfills -

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
import { nodePolyfills } from 'vite-plugin-node-polyfills';

export default defineConfig({
  plugins: [
    react(),
    nodePolyfills({
      exclude: ['fs'],
    }),
  ],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src/'),
      crypto: 'crypto-browserify',
    },
  },
  optimizeDeps: {
    esbuildOptions: {
      define: {
        global: 'globalThis',
      },
    },
  },
  build: {
    rollupOptions: {
      onwarn: (warning, warn) => {
        if (warning.code === 'SOURCEMAP_ERROR') return;
        warn(warning);
      },
    },
  },
});

To add the polyfills, the following packages should be installed as the dev dependencies - @types/node, crypto-browserify, vite-plugin-node-polyfills

Thanks for the solution.

1 Like

@bobby-lync You’re most welcome!