npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@verified-network/verified-custody

v0.5.9

Published

React submodule for Custody of digital assets

Readme

🛡️ Verified Custody

Verified Custody is a React submodule designed for custody of digital assets. This React module enables users to create blockchain wallets where private keys are sharded and stored on-chain in encrypted form. Users do not need to remember mnemonics or back up private keys.

Verified Custody requires wallet users to add co-signers, who can be requested to re-generate the sharded and stored keys, adding additional security and recovery options.

Objectives

  • Provide an easy-to-integrate React module for digital asset custody.
  • Support user onboarding (FTU) and returning user workflows.
  • Enable secure co-signer management and transaction signing.
  • Allow flexible communication with co-signers via customizable helper functions.
  • Support wallet recovery and transaction signing workflows.

⚙️ Installation

npm install @verified-network/verified-custody

💻 Usage Example

import React from "react";
import { VerifiedCustody } from "@verified-network/verified-custody";

function App() {
  return (
    <div>
      <VerifiedCustody />
    </div>
  );
}

🛠️ Build

This module is built with TypeScript and works for both JavaScript and TypeScript React/Vite projects. All components are compiled and available under the dist/ directory. The module exports React components and helper functions useful for changing UI based on user state or custom business logic.

🔄 Main Components & Workflows

The complete custody workflow component that handles new and existing users through multiple steps.

<VerifiedCustody />

🆕 First Time Users (FTU)

<FTUPage /> Full workflow for first-time users, consisting of:

<ContactPage /> Starting point where users enter phone number or email to receive an OTP (One-Time Password).

<OTPPage />  Where new and existing users verify or resend OTP.

<CreatePinPage />  Users either:

Create a new WebAuth Passkey to secure their account.

Authenticate with existing Passkey and get redirected accordingly.

Reset/Update their existing passkey

New user accounts are created here after verifying phone/email.

<AddCoSignersPage /> New users add co-signers and define signing rules:
Up to 5 co-signers can be added with a minimum of 2 required signers.

🔄 Returning Users

<EnterPinPage /> Existing users enter their PIN to:

Connect wallet/account to an application.

Accept or reject co-signer invitations.

Recover lost account/wallet keys.

Sign account recovery transactions.

Sign regular transactions, etc.

📋 Usage Details & Component Props

Component Props Types

export interface VerifiedCustodyProps {
  action?: VerifiedWalletAction;
  actionText?: string;
  origin?: string;
  title?: string;
  chainId?: number;
  vaultData?: VerifiedWalletVault;
  txData?: VerifiedWalletTx;
  reqVaultData?: VerifiedWalletVault;
  delayPageElement?: React.JSX.Element;
  delayPageStyle?: React.CSSProperties;
  helperFunctions?: VerifiedCustodyHelpers;
}

export type VerifiedWalletAction =
  | "connect_wallet"
  | "getPk"
  | "invitation"
  | "signRecovery"
  | "completeRecovery"
  | "eth_sendTransaction";

export type VerifiedWalletVault = {
  vaultId: string,
  hashedVaultId?: string,
  hashedVaultPin?: string,
  CoSignerId?: string,
  vId?: string,
  vPh?: string,
  cId?: string,
  address?: string,
  channel?: "sms" | "email",
  pk?: string,
};

export type VerifiedWalletTx = {
  id: string,
};

export interface VerifiedCustodyHelpers {
  sendCoSignerInvitation: (
    channel: "sms" | "email",
    coSignerId: string,
    creatorId: string,
    hashedCreatorPin: string
  ) => Promise<boolean>;

  sendCreatorConfirmation: (
    channel: "sms" | "email",
    creatorId: string,
    coSignersList: [],
    requiredSigners: number
  ) => Promise<boolean>;

  sendCreatorInitiation: (
    channel: "sms" | "email",
    creatorId: string,
    hashedCreatorPin: string,
    txId: number,
    requiredSigners: number
  ) => Promise<boolean>;

  sendCreatorSigned: (
    channel: "sms" | "email",
    creatorId: string,
    coSignerId: string
  ) => Promise<boolean>;

  sendCreatorCompleted: (
    channel: "sms" | "email",
    creatorId: string,
    coSignerId: string,
    vaultPinHashed: string,
    txId: number
  ) => Promise<boolean>;

  sendCreatorAcceptance: (
    channel: "sms" | "email",
    creatorId: string,
    coSignerId: string
  ) => Promise<boolean>;

  sendCreatorRejection: (
    channel: "sms" | "email",
    creatorId: string,
    coSignerId: string
  ) => Promise<boolean>;
}

Prop Descriptions

action:
Optional. Can be one of:
"connect_wallet" | "getPk" | "invitation" | "signRecovery" | "completeRecovery" | "eth_requestAccounts" | "eth_sendTransaction"
If omitted, the component handles account creation or login for new/existing users.

helperFunctions:
An object containing functions that handle messaging and notifications (email/SMS) for co-signers and creators.

Example:
sendCoSignerInvitation must take four parameters:
channel: "sms" | "email"
coSignerId: string (email or phone number of the co-signer)
phone number must be in format +{countryCode}{10DigitNumber} e.g. +1098765432
creatorId: string (email or phone number of the wallet creator)
hashedCreatorPin: string
It returns a Promise resolving to true on successful message delivery, otherwise false.

🚀 Full Workflow Example

import React from "react";
import { VerifiedCustody } from "@verified-network/verified-custody";

function App() {
  const myCustomSendCoSignerInvitation = async (
    channel: "sms" | "email",
    coSignerId: string,
    creatorId: string,
    hashedCreatorPin: string
  ): Promise<boolean> => {
    try {
      if (channel === "sms") {
        // send SMS to coSignerId with creatorId and hashedCreatorPin
        return true;
      } else if (channel === "email") {
        // send email to coSignerId with creatorId and hashedCreatorPin
        return true;
      }
      return false;
    } catch (err) {
      return false;
    }
  };

  const myCustomSendCreatorConfirmation = async (
    channel: "sms" | "email",
    creatorId: string,
    coSignersList: [],
    requiredSigners: number
  ): Promise<boolean> => {
    try {
      if (channel === "sms") {
        // send SMS showing coSigners and signing rules to creatorId
        return true;
      } else if (channel === "email") {
        // send email showing coSigners and signing rules to creatorId
        return true;
      }
      return false;
    } catch (err) {
      // If reverted for any reason return false
      return false;
    }
  };

  // Similarly define other helper functions: myCustomsendCreatorInitiation, myCustomSendCreatorSigned, etc.

  return (
    <div>
      <VerifiedCustody
        helperFunctions={{
          sendCoSignerInvitation: myCustomSendCoSignerInvitation,
          sendCreatorConfirmation: myCustomSendCreatorConfirmation,
          sendCreatorInitiation: myCustomsendCreatorInitiation,
          sendCreatorSigned: myCustomSendCreatorSigned,
          sendCreatorCompleted: myCustomSendCreatorCompleted,
          sendCreatorAcceptance: myCustomSendCreatorAcceptance,
          sendCreatorRejection: myCustomSendCreatorRejection,
        }}
      />
    </div>
  );

  // You can also set action prop for different user flows, e.g. action="connect_wallet"
}

🆕 First Time Users

import React from "react";
import { FTUPage } from "@verified-network/verified-custody";

function App() {
  const myCustomSendCoSignerInvitation = async (
    channel: "sms" | "email",
    coSignerId: string,
    creatorId: string,
    hashedCreatorPin: string
  ): Promise<boolean> => {
    // Implementation as above
  };

  const myCustomSendCreatorConfirmation = async (
    channel: "sms" | "email",
    creatorId: string,
    coSignersList: [],
    requiredSigners: number
  ): Promise<boolean> => {
    // Implementation as above
  };

  return (
    <div>
      <FTUPage
        helperFunctions={{
          sendCoSignerInvitation: myCustomSendCoSignerInvitation,
          sendCreatorConfirmation: myCustomSendCreatorConfirmation,
          sendCreatorInitiation: myCustomsendCreatorInitiation,
          sendCreatorSigned: myCustomSendCreatorSigned,
          sendCreatorCompleted: myCustomSendCreatorCompleted,
          sendCreatorAcceptance: myCustomSendCreatorAcceptance,
          sendCreatorRejection: myCustomSendCreatorRejection,
        }}
      />
    </div>
  );
}

🔄 Existing/Returning Users

import React from "react";
import { EnterPinPage } from "@verified-network/verified-custody";

function App() {
  const myCustomSendCoSignerInvitation = async (
    channel: "sms" | "email",
    coSignerId: string,
    creatorId: string,
    hashedCreatorPin: string
  ): Promise<boolean> => {
    // Implementation as above
  };

  const myCustomSendCreatorConfirmation = async (
    channel: "sms" | "email",
    creatorId: string,
    coSignersList: [],
    requiredSigners: number
  ): Promise<boolean> => {
    // Implementation as above
  };

  return (
    <div>
      <EnterPinPage
        helperFunctions={{
          sendCoSignerInvitation: myCustomSendCoSignerInvitation,
          sendCreatorConfirmation: myCustomSendCreatorConfirmation,
          sendCreatorInitiation: myCustomsendCreatorInitiation,
          sendCreatorSigned: myCustomSendCreatorSigned,
          sendCreatorCompleted: myCustomSendCreatorCompleted,
          sendCreatorAcceptance: myCustomSendCreatorAcceptance,
          sendCreatorRejection: myCustomSendCreatorRejection,
        }}
      />
    </div>
  );
}

🔄 React(Create React App) Setup and Fixes

✅ 1. Initialize the Project

npx create-react-app my-app
cd my-app

✅ 2. Install react-app-rewired

npm install react-app-rewired --save-dev
OR
yarn add --dev react-app-rewired

✅ 3. Install other dependencies for node polyfils

npm install --save-dev crypto-browserify stream-browserify assert stream-http https-browserify os-browserify url path-browserify vm-browserify buffer process
OR
yarn add crypto-browserify stream-browserify assert stream-http https-browserify os-browserify url path-browserify vm-browserify buffer process

note: the above dependencies are needed dependencies to make node related packages work, more dependencies can be added to handle any other nodeJs polyfills error.

✅ 4. Override create-react-app webpack configuration In the root folder of your project, create a new file named 'config-overrides.js', and add:

//config-overrides.js

const webpack = require("webpack");
module.exports = function override(config) {
  const fallback = config.resolve.fallback || {};
  Object.assign(fallback, {
    crypto: require.resolve("crypto-browserify"),
    stream: require.resolve("stream-browserify"),
    assert: require.resolve("assert"),
    http: require.resolve("stream-http"),
    https: require.resolve("https-browserify"),
    os: require.resolve("os-browserify"),
    url: require.resolve("url"),
    path: require.resolve("path-browserify"),
    vm: require.resolve("vm-browserify"),
    fs: false,
  });
  config.plugins.push(
    new webpack.IgnorePlugin({
      resourceRegExp:
        /^react-native$|^expo-font$|^@react-native-async-storage\/async-storage$/,
    })
  );

  config.plugins = (config.plugins || []).concat([
    new webpack.ProvidePlugin({
      process: "process/browser",
      Buffer: ["buffer", "Buffer"],
    }),
  ]);
  config.module.rules = [
    ...config.module.rules,
    {
      test: /\.m?js/,
      resolve: {
        fullySpecified: false,
      },
    },
  ];
  config.resolve.fallback = fallback;
  config.ignoreWarnings = [/Failed to parse source map/];
  return config;
};

config-overrides.js' code snippet instructs webpack how to resolve node js related dependencies that may not be available on browser side but are required. It also instruct webpack to ignore react-native related dependencies that may break our react app.

✅ 5. Modify package.json Scripts In your package.json, replace this:

"scripts": {
  "start": "react-scripts start",
  "build": "react-scripts build",
  "test": "react-scripts test",
  "eject": "react-scripts eject"
}

With this:

"scripts": {
  "start": "react-app-rewired start",
  "build": "react-app-rewired build",
  "test": "react-app-rewired test"
}

note: the above scripts changes ensure your app is handled by react-app-rewired scripts instead of react-scripts this way the webpack configuration will be acknowledged.

✅ 6. Import and Initialize Verified-Custody-Module and Start the application Using the instruction on Verified Custody Module Usage initialize the module to suit your app logic, You can start the application using:

npm start

React(Create React App) Folder Structure

my-app/
├── node_modules/
├── public/
├── src/
│   ├── App.js
│   └── index.js
├── config-overrides.js
├── package.json
└── README.md

🔄 React Native(Using Expo) Setup and Fixes

✅ 1. Initialize the Project

npx create-expo-app my-rn-app
cd my-rn-app

✅ 2. Install required dependencies

npm react-native-get-random-values expo-font @react-native-async-storage/async-storage
OR
yarn add react-native-get-random-values expo-font @react-native-async-storage/async-storage

✅ 3. Install other dependencies for node polyfils

npm install --save-dev crypto-browserify stream-browserify assert stream-http https-browserify os-browserify url path-browserify vm-browserify buffer process
OR
yarn add crypto-browserify stream-browserify assert stream-http https-browserify os-browserify url path-browserify vm-browserify buffer process

note: the above dependencies are required as external/peer dependencies for the module to work on react-native.

✅ 4. Modify the (tabs) index.js or index.ts In the app/(tabs) folder, open the index.js or index.ts file and add:

//index.js or index.ts

import "react-native-get-random-values";
OR
require("react-native-get-random-values")
};

note the react-native-get-random-values import needs to be on the first line of the file so this is import early before the custody module is used. The react-native-get-random-values is very important for node packages like crypto and various hashing/encryption functions to work.

✅ 6. Import and Initialize Verified-Custody-Module and Start the application Using the instruction on Verified Custody Module Usage initialize the module to suit your app logic, You can start the application using:

npm start(if your script is setup)
OR
npx expo start
OR
npx expo run:android (to start the application on android)
OR
npx expo run:ios (to start the application on ios)

React Native(With Expo) Folder Structure

my-rn-app/
├── node_modules/
├── public/
├── app/
│   ├── (tabs)
│         └── index.ts or index.js
├── package.json
└── README.md

🔄 Web Extension Setup and Fixes

Web Extension has various setup procedures, any of the setup you decide to use will work fine with Verify Custody Module. It is important to know that Web extension are to be built like react app and therefore during build react native related dependencies should be ignored or set as external. This can be done using plugin on webpack. For Example:

plugins: [
  {
    name: "node-and-react-native-polyfills",
    setup(build) {
      // treat react-native as external don't build
      build.onResolve({ filter: /^react-native$/ }, () => {
        return { external: true };
      });
      build.onResolve({ filter: /^expo-font$/ }, () => {
        return { external: true };
      });
      build.onResolve(
        { filter: /^@react-native-async-storage\/async-storage$/ },
        () => {
          return { external: true };
        }
      );

      ...Other polyfills
    },
  }
]

Summary

  • Secure blockchain wallet custody with private key sharding and on-chain encrypted storage.
  • Eliminates the need for mnemonic phrases or private key backups.
  • Supports multi-signature wallets via co-signers.
  • Provides complete workflows for First Time Users (FTU) and Returning Users (RU).
  • Fully customizable via React component props and helper functions.