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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@rialo/frost-core

v0.1.1

Published

Framework-agnostic wallet integration library for Rialo DApps

Readme

Rialo Frost Core 🧊

Framework-agnostic wallet integration library for Rialo dApps — The foundation that powers @rialo/frost React bindings. Use this package directly when building with Vue, Svelte, Angular, or vanilla JavaScript.

pnpm add @rialo/frost-core

Why Frost Core?

Frost Core provides the low-level wallet integration primitives without any framework dependencies:

  • Zero Framework Lock-in: Works with any JavaScript framework or vanilla JS
  • Wallet Standard Compliant: Built on @wallet-standard
  • Reactive State Management: Powered by @tanstack/store for efficient updates
  • Session Persistence: Automatic session management with configurable TTL
  • Type-Safe: Full TypeScript support with comprehensive type definitions
  • Tree-Shakeable: Only import what you need

Table of Contents


Architecture Overview

┌──────────────────────────────────────────────────────────────┐
│                       Your Application                        │
├──────────────────────────────────────────────────────────────┤
│                        Frost Core                             │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐   │
│  │   Actions   │  │    Store    │  │    WalletRegistry   │   │
│  │  connect()  │  │  TanStack   │  │  Auto-discovery     │   │
│  │  sign*()    │◄─┤   Store     │◄─┤  Event handling     │   │
│  │  send*()    │  │  Reactive   │  │  Wallet-standard    │   │
│  └─────────────┘  └─────────────┘  └─────────────────────┘   │
├──────────────────────────────────────────────────────────────┤
│                    @rialo/wallet-standard                     │
├──────────────────────────────────────────────────────────────┤
│                      Wallet Extensions                        │
└──────────────────────────────────────────────────────────────┘

Key Components:

  • Actions: Pure functions for wallet operations (connect, signMessage, sendTransaction, etc.)
  • Store: Reactive state container with automatic persistence
  • WalletRegistry: Discovers and tracks available Rialo-compatible wallets
  • WalletEventBridge: Handles wallet events (account changes, disconnections)

Installation

Requirements

  • Node.js 18+
  • A Rialo wallet extension (for testing)

Install

# pnpm (recommended)
pnpm add @rialo/frost-core

# npm
npm install @rialo/frost-core

# yarn
yarn add @rialo/frost-core

Quick Start

import {
  createConfig,
  getDefaultRialoClientConfig,
  connect,
  disconnect,
  signMessage,
  sendTransaction,
  WalletRegistry,
  WalletEventBridge,
  initializeConfig,
} from "@rialo/frost-core";

// 1. Create configuration
const config = createConfig({
  clientConfig: getDefaultRialoClientConfig("devnet"),
  autoConnect: true,
});

// 2. Initialize wallet discovery (required for wallet operations)
const registry = new WalletRegistry(config);
const bridge = new WalletEventBridge(config);
initializeConfig(config, registry, bridge);

// 3. Wait for wallet discovery
await registry.ready;

// 4. Connect to a wallet
const { walletName, accountAddress } = await connect(config, {
  walletName: "Rialo",
});
console.log(`Connected: ${accountAddress}`);

// 5. Sign a message
const { signature, signedMessage } = await signMessage(config, {
  message: "Hello from my dApp!",
});

// 6. Send a transaction
const { signature: txSignature } = await sendTransaction(config, {
  transaction: myTransactionBytes,
});

// 7. Cleanup when done
config.destroy();

Configuration

createConfig(options)

Creates a Frost configuration object. Important: Create once and reuse throughout your application.

import { createConfig, getDefaultRialoClientConfig } from "@rialo/frost-core";

const config = createConfig({
  // Required: RPC client configuration
  clientConfig: getDefaultRialoClientConfig("devnet"),

  // Optional settings (shown with defaults)
  autoConnect: true,                    // Reconnect on page load
  sessionTTL: 7 * 24 * 60 * 60 * 1000, // 7 days in milliseconds
  storageKey: "rialo-frost",            // localStorage key prefix
  storage: localStorage,                // Storage implementation (null to disable)
});

Configuration Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | clientConfig | RialoClientConfig | Required | RPC client configuration | | autoConnect | boolean | true | Auto-reconnect on page load | | sessionTTL | number | 604800000 (7 days) | Session expiration in milliseconds | | storageKey | string | "rialo-frost" | Storage key prefix | | storage | Storage \| null | localStorage | Storage implementation |

Custom RPC URL

const config = createConfig({
  clientConfig: {
    chain: {
      id: "rialo:mainnet",
      name: "Mainnet",
      rpcUrl: "https://my-custom-rpc.example.com",
    },
    transport: { timeout: 30000 },
  },
});

Networks

| Network | Chain ID | Getter | |---------|----------|--------| | Devnet | rialo:devnet | getDefaultRialoClientConfig("devnet") | | Testnet | rialo:testnet | getDefaultRialoClientConfig("testnet") | | Mainnet | rialo:mainnet | getDefaultRialoClientConfig("mainnet") | | Localnet | rialo:localnet | getDefaultRialoClientConfig("localnet") |


Actions Reference

All actions are pure functions that take config as their first argument.

Connection Actions

connect(config, options)

Connects to a wallet.

interface ConnectOptions {
  walletName: string;  // Name of the wallet to connect
  silent?: boolean;    // Attempt without popup (for reconnection)
}

interface ConnectResult {
  walletName: string;
  accountAddress: string;
}

const result = await connect(config, { walletName: "Rialo" });

Throws:

  • WalletNotFoundError — Wallet not discovered
  • UnsupportedFeatureError — Wallet doesn't support connect
  • ConnectionFailedError — User rejected or wallet error

disconnect(config)

Disconnects from the current wallet. Sets userDisconnected flag to prevent auto-reconnect.

await disconnect(config);

reconnect(config)

Attempts silent reconnection to previously connected wallet.

const result = await reconnect(config);
// Returns ConnectResult | null

Returns null if:

  • No previous connection exists
  • User explicitly disconnected
  • Session expired

getActiveConnection(config)

Gets current connection state synchronously.

interface ActiveConnection {
  status: ConnectionStatus;
  chainId: string;
  walletName: string | null;
  accountAddress: string | null;
  connectedAt: number | null;
  lastUsedAt: number;
}

const connection = getActiveConnection(config);

Wallet Actions

getWallets(config)

Gets all discovered wallets.

const wallets: WalletEntity[] = getWallets(config);

getSortedWallets(config)

Gets wallets sorted by priority (higher first), then by last connected time.

const wallets: WalletEntity[] = getSortedWallets(config);

getWallet(config, walletName)

Gets a specific wallet by name.

const wallet: WalletEntity | undefined = getWallet(config, "Rialo");

hasFeature(config, walletName, feature)

Checks if a wallet supports a specific feature.

const canSign = hasFeature(config, "Rialo", "rialo:signMessage");

supportsChain(config, walletName)

Checks if a wallet supports the currently configured chain.

const supported = supportsChain(config, "Rialo");

Account Actions

getAccounts(config)

Gets all known accounts.

const accounts: AccountEntity[] = getAccounts(config);

getActiveAccount(config)

Gets the currently active (connected) account.

const account: AccountEntity | null = getActiveAccount(config);

getAccount(config, address)

Gets a specific account by address.

const account: AccountEntity | undefined = getAccount(config, "7xKXtg...");

Message Signing

signMessage(config, options)

Signs an arbitrary message.

interface SignMessageOptions {
  message: string | Uint8Array;  // String will be UTF-8 encoded
}

interface SignMessageResult {
  signature: Uint8Array;
  signedMessage: Uint8Array;
}

// Sign a string
const result = await signMessage(config, {
  message: "Hello World",
});

// Sign raw bytes
const result = await signMessage(config, {
  message: new Uint8Array([1, 2, 3]),
});

Throws:

  • WalletDisconnectedError — No wallet connected
  • UnsupportedFeatureError — Wallet doesn't support signing
  • WalletError — Wallet operation failed

Transaction Operations

signTransaction(config, options)

Signs a transaction without submitting it.

interface SignTransactionOptions {
  transaction: Uint8Array | { serialize(): Uint8Array };
}

interface SignTransactionResult {
  signedTransaction: Uint8Array;
}

const { signedTransaction } = await signTransaction(config, {
  transaction: myTransaction,
});

sendTransaction(config, options)

Signs and sends a transaction using the wallet's built-in send feature.

interface SendTransactionResult {
  signature: Uint8Array;
}

const { signature } = await sendTransaction(config, {
  transaction: myTransaction,
});

signAndSendTransaction(config, options)

Signs via wallet, sends via Frost's RPC client with confirmation. Useful when you want:

  • Custom RPC URL
  • More control over retry logic
  • To not trust the wallet's send implementation
interface SignAndSendTransactionOptions {
  transaction: Uint8Array | { serialize(): Uint8Array };
  sendOptions?: {
    skipPreflight?: boolean;
    maxRetries?: number;
    retryDelay?: number;
  };
}

interface SignAndSendTransactionResult {
  signature: string;  // Base58 encoded
}

const { signature } = await signAndSendTransaction(config, {
  transaction: myTransaction,
  sendOptions: { maxRetries: 3 },
});

Throws:

  • WalletDisconnectedError — No wallet connected
  • UnsupportedFeatureError — Wallet doesn't support signing
  • TransactionFailedError — Transaction confirmed but execution failed on-chain

State Management

Frost Core uses @tanstack/store for reactive state management.

FrostAppState

interface FrostAppState {
  status: ConnectionStatus;           // "disconnected" | "connecting" | "connected" | "reconnecting"
  chainId: string;                    // Current chain ID
  walletName: string | null;          // Connected wallet name
  accountAddress: string | null;      // Active account address
  connectedAt: number | null;         // Connection timestamp
  sessionExpiresAt: number | null;    // Session expiration timestamp
  lastUsedAt: number;                 // Last activity timestamp
  userDisconnected: boolean;          // Prevents auto-reconnect
  wallets: Map<string, WalletEntity>; // Discovered wallets
  accounts: Map<string, AccountEntity>; // Known accounts
}

Subscribing to State Changes

// Subscribe to all state changes
const unsubscribe = config.store.subscribe(({ currentVal, prevVal }) => {
  if (currentVal.status !== prevVal.status) {
    console.log(`Status changed: ${prevVal.status} -> ${currentVal.status}`);
  }
});

// Read current state
const state = config.store.state;

// Unsubscribe when done
unsubscribe();

Entity Types

WalletEntity

interface WalletEntity {
  name: string;                        // Display name
  icon?: string;                       // Base64 data URI
  chains: readonly string[];           // Supported chains
  features: readonly string[];         // Feature identifiers
  installedVersion?: string;           // Wallet version
  priority?: number;                   // UI sort priority
  lastConnectedAt?: number;            // Last successful connection
}

AccountEntity

interface AccountEntity {
  address: string;                     // Base58-encoded address
  publicKey: Uint8Array;               // Raw public key bytes
  walletName: string;                  // Parent wallet name
  chains: readonly string[];           // Valid chains
  label?: string;                      // Optional user label
}

Error Handling

Frost provides typed errors for precise handling.

Error Type Guard

import { isFrostError, type FrostError } from "@rialo/frost-core";

try {
  await connect(config, { walletName: "Unknown" });
} catch (error) {
  if (isFrostError(error)) {
    handleFrostError(error);
  } else {
    console.error("Unknown error:", error);
  }
}

Error Types

| Code | Class | When | |------|-------|------| | WALLET_NOT_FOUND | WalletNotFoundError | Wallet extension not discovered | | WALLET_DISCONNECTED | WalletDisconnectedError | Operation requires connected wallet | | UNSUPPORTED_CHAIN | UnsupportedChainError | Wallet doesn't support current chain | | UNSUPPORTED_FEATURE | UnsupportedFeatureError | Missing wallet capability | | CONNECTION_FAILED | ConnectionFailedError | User rejected or wallet error | | SESSION_EXPIRED | SessionExpiredError | Persisted session too old | | TRANSACTION_FAILED | TransactionFailedError | Transaction confirmed but failed on-chain | | WALLET_ERROR | WalletError | Generic wallet error wrapper |

Error Properties

All errors extend FrostError and include:

class FrostError extends Error {
  code: string;           // Error code (e.g., "WALLET_NOT_FOUND")
  walletName?: string;    // Associated wallet (if applicable)
}

Specific errors have additional properties:

// UnsupportedChainError
error.chainId;           // Requested chain
error.supportedChains;   // Array of supported chains

// UnsupportedFeatureError
error.feature;           // Required feature identifier

// ConnectionFailedError
error.reason;            // Failure reason string

// SessionExpiredError
error.expiredAt;         // Expiration timestamp

// TransactionFailedError
error.signature;         // Transaction signature
error.reason;            // On-chain failure reason

// WalletError
error.originalError;     // Original error from wallet

Handling Pattern

import {
  isFrostError,
  WalletNotFoundError,
  WalletDisconnectedError,
  UnsupportedChainError,
  ConnectionFailedError,
} from "@rialo/frost-core";

function handleError(error: unknown) {
  if (!isFrostError(error)) {
    console.error("Unknown error:", error);
    return;
  }

  switch (error.code) {
    case "WALLET_NOT_FOUND":
      alert(`Install ${error.walletName} to continue`);
      break;
    case "WALLET_DISCONNECTED":
      alert("Please connect your wallet first");
      break;
    case "UNSUPPORTED_CHAIN":
      const chainError = error as UnsupportedChainError;
      alert(`Switch to: ${chainError.supportedChains.join(", ")}`);
      break;
    case "CONNECTION_FAILED":
      alert("Connection rejected or failed");
      break;
    case "TRANSACTION_FAILED":
      alert(`Transaction failed: ${error.message}`);
      break;
    default:
      alert(error.message);
  }
}

Testing

Frost Core includes comprehensive testing utilities via the /testing export.

Installation

import {
  createMockConfig,
  createMockWallet,
  createMockAccount,
  createMockClient,
  createMockStorage,
  waitFor,
  waitForState,
} from "@rialo/frost-core/testing";

Creating a Mock Config

const { config, mockClient, mockStorage } = createMockConfig({
  chainId: "rialo:devnet",
  autoConnect: false,
  initialState: {
    status: "disconnected",
  },
});

Creating Mock Wallets

const mockWallet = createMockWallet({
  name: "TestWallet",
  autoApprove: true,           // Auto-approve connections
  connectDelay: 100,           // Simulate network delay
  failConnect: false,          // Simulate connection failure
  supportsEvents: true,        // Include standard:events
  supportsSignMessage: true,   // Include rialo:signMessage
  supportsSignTransaction: true,
  supportsSignAndSend: true,
});

// Register the mock wallet
config.store.setState(state => ({
  ...state,
  wallets: new Map([[mockWallet.name, createMockWalletEntity({ name: mockWallet.name })]]),
  _walletRefs: new Map([[mockWallet.name, mockWallet]]),
}));

Mock Wallet Test Helpers

// Trigger account change event
mockWallet._emitAccountChange([newAccount]);

// Check call counts
console.log(mockWallet._connectCallCount);
console.log(mockWallet._signMessageCallCount);

// Access last operations
console.log(mockWallet._lastSignedMessage);
console.log(mockWallet._lastSignedTransaction);

// Reset state between tests
mockWallet._reset();

Mock RPC Client

const mockClient = createMockClient({
  defaultBalance: 1_000_000_000n,  // 1 SOL equivalent
  requestDelay: 50,                 // Simulate latency
  failRequests: false,              // Simulate RPC errors
});

// Set specific balances
mockClient._setBalance("7xKXtg...", 5_000_000_000n);

// Simulate transaction failures
mockClient._setTransactionFails(true, "Insufficient funds");

// Check operations
console.log(mockClient._sendTransactionCallCount);
console.log(mockClient._lastSentTransaction);

Wait Utilities

// Wait for a condition
await waitFor(() => config.store.state.status === "connected", {
  timeout: 5000,
  interval: 50,
});

// Wait for specific state
const state = await waitForState(
  config,
  (s) => s.wallets.size > 0,
  { timeout: 3000 }
);

Example Test

import { describe, it, expect, beforeEach } from "vitest";
import { connect, disconnect } from "@rialo/frost-core";
import {
  createMockConfig,
  createMockWallet,
  createMockWalletEntity,
  waitForState,
} from "@rialo/frost-core/testing";

describe("connect", () => {
  let config;
  let mockWallet;

  beforeEach(() => {
    const { config: c } = createMockConfig();
    config = c;
    mockWallet = createMockWallet({ name: "TestWallet" });

    // Register mock wallet
    config.store.setState(state => ({
      ...state,
      wallets: new Map([["TestWallet", createMockWalletEntity({ name: "TestWallet" })]]),
      _walletRefs: new Map([["TestWallet", mockWallet]]),
    }));
  });

  it("should connect to wallet", async () => {
    const result = await connect(config, { walletName: "TestWallet" });

    expect(result.walletName).toBe("TestWallet");
    expect(config.store.state.status).toBe("connected");
    expect(mockWallet._connectCallCount).toBe(1);
  });

  it("should throw on unknown wallet", async () => {
    await expect(
      connect(config, { walletName: "Unknown" })
    ).rejects.toThrow("WALLET_NOT_FOUND");
  });
});

Advanced Usage

Initializing Without Auto-Discovery

For custom wallet registration or SSR environments:

import {
  createConfig,
  getDefaultRialoClientConfig,
  WalletRegistry,
  WalletEventBridge,
  initializeConfig,
} from "@rialo/frost-core";

const config = createConfig({
  clientConfig: getDefaultRialoClientConfig("devnet"),
  storage: null,  // Disable persistence for SSR
});

// Initialize only in browser
if (typeof window !== "undefined") {
  const registry = new WalletRegistry(config);
  const bridge = new WalletEventBridge(config);
  initializeConfig(config, registry, bridge);
}

Switching Chains

import { getDefaultRialoClientConfig } from "@rialo/frost-core";

// Switch to mainnet
config.switchChain(getDefaultRialoClientConfig("mainnet"));

// Get current chain
const chainId = config.getChainId(); // "rialo:mainnet"

Direct Store Access

// Read state
const { status, walletName, accountAddress } = config.store.state;

// Subscribe to changes
const unsubscribe = config.store.subscribe(({ currentVal }) => {
  console.log("New status:", currentVal.status);
});

// Update state (internal use only)
config.store.setState(state => ({
  ...state,
  lastUsedAt: Date.now(),
}));

Custom Persistence

// In-memory storage
const memoryStorage = new Map<string, string>();

const customStorage: Storage = {
  getItem: (key) => memoryStorage.get(key) ?? null,
  setItem: (key, value) => memoryStorage.set(key, value),
  removeItem: (key) => memoryStorage.delete(key),
  clear: () => memoryStorage.clear(),
  get length() { return memoryStorage.size; },
  key: (index) => Array.from(memoryStorage.keys())[index] ?? null,
};

const config = createConfig({
  clientConfig: getDefaultRialoClientConfig("devnet"),
  storage: customStorage,
});

Direct RPC Client Access

// Access the RialoClient for direct RPC calls
const client = config.client;

// Example: Get balance
const balance = await client.getBalance(publicKey);

// Example: Get latest blockhash
const { blockhash, lastValidBlockHeight } = await client.getLatestBlockhash();

Cleanup

Always destroy the config when unmounting your app:

// Cleanup all subscriptions and resources
config.destroy();

Troubleshooting

No wallets found

  1. Install a Rialo wallet extension
  2. Make sure the extension is enabled for your site
  3. Wait for registry.ready before checking wallets
  4. Refresh the page

Connection keeps failing

  1. Unlock your wallet
  2. Check if the wallet supports your network (devnet/mainnet)
  3. Try disabling and re-enabling the extension
  4. Clear localStorage and refresh

Session expired on page load

Expected behavior. The default session TTL is 7 days. Configure with sessionTTL option:

const config = createConfig({
  clientConfig: getDefaultRialoClientConfig("devnet"),
  sessionTTL: 30 * 24 * 60 * 60 * 1000, // 30 days
});

State not updating

Ensure you're subscribing to the store correctly:

// Wrong: Reading state once
const status = config.store.state.status;

// Right: Subscribing to changes
config.store.subscribe(({ currentVal }) => {
  updateUI(currentVal.status);
});

Auto-reconnect not working

Check if userDisconnected flag is set:

const { userDisconnected } = config.store.state;
if (userDisconnected) {
  // User explicitly disconnected, won't auto-reconnect
  // Must call connect() again
}

API Reference

Exports

// Configuration
export { createConfig, initializeConfig } from "./config";
export type { CreateConfigOptions, FrostConfig } from "./config";

// Chain definitions (re-exported from @rialo/ts-cdk)
export {
  getDefaultRialoClientConfig,
  RIALO_DEVNET_CHAIN,
  RIALO_TESTNET_CHAIN,
  RIALO_MAINNET_CHAIN,
  RIALO_LOCALNET_CHAIN,
} from "@rialo/ts-cdk";

// Actions
export {
  connect,
  disconnect,
  reconnect,
  getActiveConnection,
  getWallets,
  getSortedWallets,
  getWallet,
  hasFeature,
  supportsChain,
  getAccounts,
  getActiveAccount,
  getAccount,
  signMessage,
  signTransaction,
  sendTransaction,
  signAndSendTransaction,
} from "./actions";

// Types
export type {
  ConnectionStatus,
  FrostAppState,
  WalletEntity,
  AccountEntity,
  ConnectOptions,
  ConnectResult,
  ActiveConnection,
  SignMessageOptions,
  SignMessageResult,
  SignTransactionOptions,
  SignTransactionResult,
  SendTransactionResult,
  SignAndSendTransactionOptions,
  SignAndSendTransactionResult,
} from "./types";

// Errors
export {
  FrostError,
  isFrostError,
  WalletNotFoundError,
  WalletDisconnectedError,
  UnsupportedChainError,
  UnsupportedFeatureError,
  ConnectionFailedError,
  SessionExpiredError,
  TransactionFailedError,
  WalletError,
} from "./errors";

// Internal (for framework bindings)
export { WalletRegistry } from "./registry/WalletRegistry";
export { WalletEventBridge } from "./bridge/WalletEventBridge";

Testing Exports (@rialo/frost-core/testing)

export {
  createMockConfig,
  createMockWallet,
  createMockAccount,
  createMockClient,
  createMockStorage,
  createMockWalletEntity,
  createMockAccountEntity,
  waitFor,
  waitForState,
} from "./testing";

License

Apache-2.0