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

@rockerone/xprnkit

v0.5.1

Published

> **⚠️ Warning:** Version 0.4.0 introduces breaking changes. Make sure to save your project before migrate.

Downloads

237

Readme

@rockerone/xprnkit

⚠️ Warning: Version 0.4.0 introduces breaking changes. Make sure to save your project before migrate.

React SDK for building dApps on XPR Network. Provides wallet connection, identity proof with pluggable transports, multi-account management, token swap UI, and transaction helpers.

Installation

npm install @rockerone/xprnkit

XPRNProvider

Wrap your application with XPRNProvider to enable wallet and blockchain interactions.

import { XPRNProvider } from '@rockerone/xprnkit';

const config = {
  chainId: "your-chain-id",
  endpoints: ["https://your-endpoint.com"],
  dAppName: "Your dApp Name",
  requesterAccount: "your-account",
  apiMode: "mainnet",
};

function App() {
  return (
    <XPRNProvider config={config}>
      {/* Your app */}
    </XPRNProvider>
  );
}

XPRProviderConfig

| Property | Type | Required | Description | |---|---|---|---| | chainId | string | Yes | Blockchain chain ID | | endpoints | string[] | Yes | RPC endpoints | | dAppName | string | Yes | Display name for your dApp | | requesterAccount | string | Yes | Your dApp's account name | | requesterLogo | string | No | Logo URL for your dApp | | apiMode | "testnet" \| "mainnet" | Yes | Network environment | | restoreSession | boolean | No | Restore last active session on page refresh | | identityProof | XPRNKitIdentityProofConfig | No | Identity proof configuration |


useXPRN

Hook to access the provider context.

import { useXPRN } from '@rockerone/xprnkit';

function MyComponent() {
  const {
    config,
    session,
    link,
    profile,
    identityProof,
    identityProofStatus,
    identityProofError,
    connect,
    disconnect,
    pushTransaction,
    switchToSession,
    requestIdentityProof,
  } = useXPRN();
}

State

| Property | Type | Description | |---|---|---| | config | XPRProviderConfig \| null | Current provider configuration | | session | LinkSession \| null | Active session | | link | ProtonWebLink \| Link \| null | Proton link object | | profile | XPRNKitProfile \| null | Profile for the active session | | identityProof | XPRNKitIdentityProof \| null | Identity proof for the active session | | identityProofStatus | XPRNKitIdentityProofStatus | Status of the identity proof process | | identityProofError | Error \| null | Error from the last failed identity proof attempt |

Methods

| Method | Description | |---|---| | connect(restore?: boolean) | Connect to wallet. Pass true to restore a previous session silently. | | disconnect() | Disconnect the current session | | pushTransaction(actions: any[]) | Push a transaction with the active session | | switchToSession(actor, permission) | Switch to a different stored session | | requestIdentityProof() | Trigger the identity proof flow manually. Can retry after error. |

Transaction Example

const { session, pushTransaction } = useXPRN();

await pushTransaction([
  {
    account: "eosio.token",
    name: "transfer",
    authorization: [session.auth],
    data: {
      from: session.auth.actor.toString(),
      to: "recipient",
      quantity: "1.0000 XPR",
      memo: "Hello!",
    },
  },
]);

Identity Proof

Verify wallet ownership by having users sign a message. Supports HTTP, WebSocket, and custom transports.

Flow

  1. When required is true, identity proof is triggered automatically on wallet connect.
  2. The provider first checks localStorage for an existing proof and validates it.
  3. If no valid proof exists, the user is prompted to sign a message.
  4. The signed message is sent to your backend for verification.
  5. The backend returns a JWT token, stored in localStorage and exposed via useXPRN().identityProof.
  6. On failure, identityProofStatus is set to "error". Call requestIdentityProof() to retry.

XPRNKitIdentityProofConfig

| Property | Type | Required | Description | |---|---|---|---| | required | boolean | Yes | Auto-trigger on connect | | transport | TransportConfig | No | Transport configuration (defaults to HTTP) | | createUrl | string | No | HTTP endpoint for signature verification | | validationUrl | string | No | HTTP endpoint for token validation | | validationBuffer | number | No | Seconds before expiry to trigger revalidation | | headers | Record<string, string> | No | Custom headers for requests | | timeout | number | No | Request timeout in milliseconds | | createIdentityResponseTransformer | (response: any) => any | No | Transform create response | | validationIdentityResponseTransformer | (response: any) => any | No | Transform validation response |

HTTP Transport (Default)

const config = {
  identityProof: {
    required: true,
    createUrl: "/api/auth/authorize",
    validationUrl: "/api/auth/validate",
  },
};

WebSocket Transport

const config = {
  identityProof: {
    required: true,
    transport: {
      type: 'websocket',
      options: {
        url: 'wss://api.example.com/identity',
        timeout: 15000,
        reconnect: true,
        reconnectInterval: 5000,
        maxReconnectAttempts: 5,
        maxReconnectDelay: 30000,
      },
    },
  },
};

WebSocket Message Format

// Client -> Server
{ id: string, action: 'createIdentityProof' | 'validateIdentityProof', data: any }

// Server -> Client
{ id: string, action: string, data: any, error?: string }

Custom Transport

import { IdentityProofTransport, TransportFactory } from '@rockerone/xprnkit';

class GraphQLTransport implements IdentityProofTransport {
  name = 'graphql';

  async createIdentityProof(payload, config, signal) {
    const response = await fetch('/graphql', {
      method: 'POST',
      body: JSON.stringify({
        query: CREATE_IDENTITY_PROOF_MUTATION,
        variables: { input: payload },
      }),
      signal,
    });
    return response.json();
  }

  async validateIdentityProof(proof, config, signal) {
    return true;
  }

  dispose() {}
}

TransportFactory.register('graphql', new GraphQLTransport());

const config = {
  identityProof: {
    required: true,
    transport: { type: 'custom', options: { name: 'graphql' } },
  },
};

Backend Endpoints

Authorization (createUrl)

// Request body
{
  signer: { actor: string, permission: string },
  transaction: any,
  signatures: string[],
  chainId: string,
}

// Response
{
  success: true,
  validated: true,
  actor: string,
  permission: string,
  token: string,
  timestamp: string,
}

Validation (validationUrl)

// Request: Authorization header with "Bearer <token>" or body { token: string }

// Response
{ valid: boolean, actor?: string, expiresAt?: string, error?: string }

XPRNIdentityProofGate

Conditionally render content based on identity proof status.

import { XPRNIdentityProofGate, useXPRN } from '@rockerone/xprnkit';

function ProtectedContent() {
  const { identityProofError, requestIdentityProof } = useXPRN();

  return (
    <XPRNIdentityProofGate
      fallback={<p>Please verify your identity.</p>}
      loading={<p>Verifying...</p>}
      error={
        <div>
          <p>Failed: {identityProofError?.message}</p>
          <button onClick={requestIdentityProof}>Retry</button>
        </div>
      }
      notConnected={<p>Please connect your wallet first.</p>}
    >
      <p>Verified content.</p>
    </XPRNIdentityProofGate>
  );
}

| Prop | Type | Description | |---|---|---| | children | ReactNode | Content when identity proof is obtained | | fallback | ReactNode | Content when identity proof is not obtained | | loading | ReactNode | Content while in progress | | error | ReactNode | Content on failure (falls back to fallback) | | notConnected | ReactNode | Content when no session (falls back to fallback) |

useIdentityProofGate

Hook version for programmatic access.

import { useIdentityProofGate } from '@rockerone/xprnkit';

const {
  isGateActive,
  isConnected,
  isInProgress,
  hasError,
  isVerified,
  shouldRenderChildren,
  needsIdentityProof,
  identityProofStatus,
} = useIdentityProofGate();

Multi-Account Support

Connect multiple wallet accounts. The provider maintains one active session at a time. Use switchToSession(actor, permission) to switch between stored accounts.

const { session, switchToSession } = useXPRN();

await switchToSession("otheraccount", "active");

Components

XPRNConnectButton

import { XPRNConnectButton } from '@rockerone/xprnkit';

<XPRNConnectButton
  onSession={(session, link) => {}}
  onProfile={(profile) => {}}
  onIdentityProof={(proof) => {}}
>
  Connect Wallet
</XPRNConnectButton>

Shows "Connect" when disconnected, "Log out (actor)" when connected. Accepts standard button props, variant, size, asChild, and className.

XPRNIdentity

Dropdown showing user identity with avatar and session info.

import { XPRNIdentity } from '@rockerone/xprnkit';

<XPRNIdentity showLogout avatarClassName="custom-avatar">
  <div>Dropdown content</div>
</XPRNIdentity>

| Prop | Type | Description | |---|---|---| | showLogout | boolean | Show logout link | | activeSessionClassName | string | CSS for active session container | | dropdownClassName | string | CSS for dropdown | | avatarClassName | string | CSS for avatar | | matchDropdownWidth | boolean | Match dropdown width to trigger (default: true) | | closeOnSelect | boolean | Close dropdown on click (default: true) | | onSession | (session, link) => void | Session callback | | onProfile | (profile) => void | Profile callback | | onIdentityProof | (proof) => void | Identity proof callback |

Sub-components: XPRNAvatar, XPRNSessionActor, XPRNSessionName.

XPRNAccountList

Lists stored accounts with switch, add, and remove functionality.

import { XPRNIdentity, XPRNAccountList } from '@rockerone/xprnkit';

<XPRNIdentity showLogout>
  <XPRNAccountList
    showAddAccount
    showRemove
    onSelect={(entry) => {}}
  />
</XPRNIdentity>

| Prop | Type | Description | |---|---|---| | showRemove | boolean | Show remove button per account | | showAddAccount | boolean | Show "Add Account" button | | addAccountLabel | string | Custom add account label | | renderItem | (props: AccountItemRenderProps) => ReactNode | Custom item renderer | | onSelect | (entry: XPRNKitProfileStorageEntry) => void | Selection callback | | onRemove | (entry: XPRNKitProfileStorageEntry) => void | Removal callback | | onAddAccount | () => void | Add account callback |

XRPNContainer / XPRNLogged / XPRNUnlogged

Conditional rendering based on session state.

import { XRPNContainer, XPRNLogged, XPRNUnlogged } from '@rockerone/xprnkit';

<XRPNContainer noSessionState={<p>Please log in.</p>}>
  <p>Welcome!</p>
</XRPNContainer>

<XPRNLogged>
  <p>Visible when connected.</p>
</XPRNLogged>

<XPRNUnlogged>
  <p>Visible when disconnected.</p>
</XPRNUnlogged>

XPRNTransaction

Button that pushes a blockchain transaction.

import { XPRNTransaction } from '@rockerone/xprnkit';

<XPRNTransaction
  actions={actions}
  onTransactionSuccess={(res) => {}}
  onTransactionFail={(err) => {}}
>
  Swap
</XPRNTransaction>

Shows "Connect" when no session. Accepts actions, onTransactionStart, onTransactionSuccess, onTransactionFail, variant, size, asChild, and className.


Swap Components

XPRNSwap

Complete swap interface out of the box.

import { XPRNSwap } from '@rockerone/xprnkit';

<XPRNSwap />

<XPRNSwap
  filters={{ quoteSymbol: "XUSDC" }}
  sides={['buy', 'sell']}
/>

Custom Swap Layout

import {
  XPRNSwapProvider,
  XPRNPairsSelector,
  XPRNSwapFieldsGroup,
  XPRNSwapField,
  XPRNSwapSideButton,
  XPRNTransaction,
} from '@rockerone/xprnkit';

<XPRNSwapProvider config={{ filters: { quoteSymbol: "SNIPS", baseSymbol: "XUSDC" } }}>
  <XPRNPairsSelector />
  <XPRNSwapFieldsGroup horizontal>
    <XPRNSwapField />
    <XPRNSwapSideButton horizontal>Swap</XPRNSwapSideButton>
    <XPRNSwapField />
  </XPRNSwapFieldsGroup>
  <XPRNTransaction>Swap</XPRNTransaction>
</XPRNSwapProvider>

useXPRNSwap

| Property/Method | Type | Description | |---|---|---| | marketProviders | XPRNMarketProvider[] | Available market providers | | currentMarketProvider | XPRNMarketProvider \| null | Selected market provider | | setCurrentMarketProvider | (mp: XPRNMarketProvider) => void | Set market provider | | refreshMarketPairs | () => void | Refresh pairs | | currentMarketPairs | XPRNMarketProviderResult[] | Current pairs | | refreshStatus | ServiceStatus | Refresh status | | currentSwapPair | XPRNMarketProviderResult \| null | Selected pair | | setCurrentSwapPair | (pair: XPRNMarketProviderResult) => void | Set pair | | swapSide | XPRNSwapSide | Current side ("buy" or "sell") | | setSwapSide | (side: XPRNSwapSide) => void | Set side | | swapVolume | number | Current volume | | setSwapVolume | (volume: number) => void | Set volume | | swapValues | XPRNSwapValues | Current swap values | | updateSwapValues | (base, quote, lastMutated) => void | Update values | | swapTransaction | any[] | Current transaction actions |

Sub-components

| Component | Description | |---|---| | XPRNSwapProvider | Context provider for swap state. Props: config?: XPRNSwapProviderConfig | | XPRNSwapFieldsGroup | Groups swap fields, flips on side change. Props: horizontal?: boolean | | XPRNSwapField | Token amount input. Props: type?: "base" \| "quote" | | XPRNSwapSideButton | Toggles buy/sell side. Props: horizontal?: boolean | | XPRNSwapMarketsSelector | Market provider dropdown (renders only if >1 provider) | | XPRNPairsSelector | Trading pair dropdown. Props: contentClassName, itemsClassName |


Types Reference

type XPRProviderConfig = {
  chainId: string;
  endpoints: string[];
  dAppName: string;
  requesterAccount: string;
  requesterLogo?: string;
  apiMode: "testnet" | "mainnet";
  restoreSession?: boolean;
  identityProof?: XPRNKitIdentityProofConfig;
};

type XPRNKitIdentityProofConfig = {
  required: boolean;
  transport?: TransportConfig;
  createUrl?: string;
  validationUrl?: string;
  createIdentityResponseTransformer?: (response: any) => any;
  validationIdentityResponseTransformer?: (response: any) => any;
  validationBuffer?: number;
  headers?: Record<string, string>;
  timeout?: number;
};

type TransportConfig = {
  type: 'http' | 'websocket' | 'custom';
  transport?: IdentityProofTransport;
  options?: Record<string, any>;
};

type WebSocketOptions = {
  url: string;
  timeout?: number;
  reconnect?: boolean;
  reconnectInterval?: number;
  maxReconnectAttempts?: number;
  maxReconnectDelay?: number;
};

type XPRNKitIdentityProof = {
  auth: { actor: string; permission: string };
  token: string;
  metadata: { [key: string]: any };
};

type XPRNKitIdentityProofPayload = {
  signer: { actor: string; permission: string };
  transaction: any;
  signatures: string[];
  chainId: string;
};

type XPRNKitIdentityProofStatus =
  | "idle" | "signing" | "verifying"
  | "validating" | "success" | "expired" | "error";

type XPRNKitProfile = {
  displayName: string;
  avatar?: string;
  isKyc: boolean;
};

type XPRNKitProfileStorageEntry = {
  auth: { actor: string; permission: string };
  chainId: string;
  profile: XPRNKitProfile;
};

type ServiceStatus = 'idle' | 'pending' | 'success' | 'fail';

Utilities

toPrecision

Format a number to a given precision.

function toPrecision(
  value: number,
  precision: number,
  mode?: "ceil" | "floor" | "round" | "none",
  forceDecimal?: boolean
): string;
toPrecision(3.14159, 2)              // "3.15"
toPrecision(3.14159, 2, "floor")     // "3.14"
toPrecision(3, 2, "none", true)      // "3.00"
toPrecision(3, 2, "ceil", false)     // "3"