Skip to main content

Overview

Wallet management is at the heart of any Solana dApp. Hermis provides a comprehensive system for managing wallet connections, state, and interactions across all supported wallets.

Wallet Lifecycle

States

Unconnected

No wallet is connected. User needs to select and connect a wallet.

Connecting

Connection in progress. Waiting for user approval in wallet.

Connected

Wallet successfully connected. Can perform transactions.

Disconnecting

Disconnection in progress. Cleaning up resources.

Supported Wallets

Hermis supports any wallet that implements the Wallet Standard. This includes browser extension wallets, mobile wallets, and hardware wallets that conform to the standard.

Wallet Detection

The SDK automatically detects installed wallets and groups them by ready state:
import { useWalletAdapters } from '@hermis/solana-headless-react';

function WalletList() {
  const { installed, loadable, notDetected, all } = useWalletAdapters();

  // installed: Wallets detected in browser
  // loadable: Wallets that can be loaded on demand
  // notDetected: Wallets not installed
  // all: All adapters sorted by priority

  return (
    <>
      {installed.map(adapter => (
        <div key={adapter.name}>{adapter.name} (Installed)</div>
      ))}
    </>
  );
}

Ready States

Installed
ReadyState
Wallet is installed and detected in the user’s browser
Loadable
ReadyState
Wallet can be loaded on demand (e.g., WalletConnect)
NotDetected
ReadyState
Wallet is not installed or detected
Unsupported
ReadyState
Wallet is not supported in current environment

Connection Management

Manual Connection

// React
import { useWallet } from '@hermis/solana-headless-react';

function ConnectButton() {
  const { connect, select } = useWallet();

  const handleConnect = async () => {
    select('Phantom'); // Select wallet
    await connect();    // Connect to wallet
  };

  return <button onClick={handleConnect}>Connect</button>;
}
// Vanilla JS
import { WalletAdapterManager } from '@hermis/solana-headless-adapter-base';

const manager = new WalletAdapterManager(adapters);

manager.selectAdapter('Phantom');
await manager.connect();

Auto-Connect

Automatically reconnect to the last used wallet on page load:
<HermisProvider autoConnect={true}>
  {children}
</HermisProvider>
This provides a seamless experience for returning users.

Wallet Selection

Building a Wallet Selector

import { useWallet, useWalletAdapters } from '@hermis/solana-headless-react';

function WalletSelector() {
  const { select, connect } = useWallet();
  const { installed, notDetected } = useWalletAdapters();

  return (
    <div>
      <h3>Installed Wallets</h3>
      {installed.map(adapter => (
        <button
          key={adapter.name}
          onClick={() => {
            select(adapter.name);
            connect();
          }}
        >
          {adapter.icon && <img src={adapter.icon} alt={adapter.name} />}
          {adapter.name}
        </button>
      ))}

      <h3>Other Wallets</h3>
      {notDetected.map(adapter => (
        <button
          key={adapter.name}
          onClick={() => window.open(adapter.url, '_blank')}
        >
          {adapter.name} (Not Installed)
        </button>
      ))}
    </div>
  );
}

Persistence

Local Storage

Wallet selection is automatically persisted to local storage:
<HermisProvider
  storageKey="my-app-wallet" // Custom storage key
>
  {children}
</HermisProvider>

Custom Storage

Implement custom storage (e.g., IndexedDB):
import { createIndexedDBStorageFactory } from '@hermis/solana-headless-react';

const storage = createIndexedDBStorageFactory('db-name', 'store-name');

<HermisProvider storageFactory={storage}>
  {children}
</HermisProvider>

Multi-Wallet Support

Switching Wallets

Users can switch between wallets without refreshing:
const { wallet, select, disconnect, connect } = useWallet();

const switchWallet = async (newWalletName: string) => {
  await disconnect();  // Disconnect current wallet
  select(newWalletName); // Select new wallet
  await connect();      // Connect to new wallet
};

Wallet Events

React

Use hooks to react to wallet changes:
const { wallet, connected, publicKey } = useWallet();

useEffect(() => {
  if (connected && publicKey) {
    console.log('Wallet connected:', publicKey.toBase58());
  }
}, [connected, publicKey]);

Vanilla JS

Subscribe to wallet events:
manager.on('connect', (publicKey) => {
  console.log('Connected:', publicKey.toBase58());
});

manager.on('disconnect', () => {
  console.log('Disconnected');
});

manager.on('error', (error) => {
  console.error('Error:', error);
});

manager.on('walletChanged', (wallet) => {
  console.log('Wallet changed to:', wallet?.name);
});

Error Handling

Hermis uses HermisError for type-safe error handling:
import { isHermisError, HERMIS_ERROR__WALLET_CONNECTION__FAILED } from '@hermis/errors';

try {
  await connect();
} catch (error) {
  if (isHermisError(error, HERMIS_ERROR__WALLET_CONNECTION__FAILED)) {
    console.log(`Failed to connect: ${error.context.walletName}`);
  } else if (isHermisError(error)) {
    console.log(`Error [${error.code}]: ${error.message}`);
  }
}

Common Error Patterns

Handle wallet connection failures with context data:
import { isHermisError, HERMIS_ERROR__WALLET_CONNECTION__FAILED } from '@hermis/errors';

if (isHermisError(error, HERMIS_ERROR__WALLET_CONNECTION__FAILED)) {
  // Access typed context: walletName, reason, originalError
  showToast(`Failed to connect to ${error.context.walletName}`);
}
Check if wallet is not connected before operations:
import { isHermisError, HERMIS_ERROR__WALLET_CONNECTION__NOT_CONNECTED } from '@hermis/errors';

if (isHermisError(error, HERMIS_ERROR__WALLET_CONNECTION__NOT_CONNECTED)) {
  showToast('Please connect your wallet first');
}
Handle any HermisError with automatic message formatting:
import { isHermisError } from '@hermis/errors';

if (isHermisError(error)) {
  // HermisError provides formatted messages automatically
  showToast(error.message);
}

Global Error Handler

import { isHermisError } from '@hermis/errors';

<HermisProvider
  onError={(error, adapter) => {
    if (isHermisError(error)) {
      // HermisError provides formatted messages and typed context
      console.error(`[${error.code}] ${error.message}`);
      showToast(error.message);
    } else {
      // Handle non-Hermis errors
      console.error(`Error with ${adapter?.name}:`, error);
      showToast(`Error: ${error.message}`);
    }
  }}
>
  {children}
</HermisProvider>

Best Practices

Users can disconnect from their wallet extension at any time. Always handle this gracefully:
useEffect(() => {
  if (!connected) {
    // Clear user data, redirect, etc.
  }
}, [connected]);
Ensure the wallet is connected to the correct network:
const { network } = useConnection();
if (network !== 'mainnet-beta') {
  showWarning('Please switch to mainnet');
}
Show clear states for connecting, connected, and errors:
if (connecting) return <Spinner />;
if (connected) return <ConnectedUI />;
return <ConnectButton />;
Hermis automatically handles mobile wallet detection and deep linking. Simply ensure the Mobile Wallet Adapter is included in your adapters, and users can seamlessly connect from mobile devices.For best performance, remind users to disable battery/power saving mode on their devices to ensure reliable connections and smooth deep linking between your dApp and wallet apps.

What’s Next?