> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hermis.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Vanilla JavaScript Example

> Complete vanilla JavaScript implementation

## Overview

This example demonstrates how to use Hermis with vanilla JavaScript - no framework required! The guide shows examples for both **web3.js** (legacy) and **@solana/kit** (modern) architectures.

## Architecture Guide

### Web3.js (Legacy)

* Mature and widely supported
* Compatible with all existing Solana tools
* Well-documented ecosystem
* Larger bundle size

### @solana/kit (Modern)

* Type-safe instruction building
* Better tree-shaking for smaller bundles
* Modern API design
* Future-proof architecture

**Good news:** Both architectures work seamlessly with `WalletAdapterManager`! You can even use Kit signers with wallet adapters through `createKitSignersFromAdapter`.

## Project Setup

```bash theme={null}
mkdir solana-wallet-app
cd solana-wallet-app
npm init -y

# For web3.js architecture
npm install @hermis/solana-headless-adapter-base @hermis/errors
npm install @solana/wallet-adapter-phantom @solana/wallet-adapter-solflare @solana/web3.js

# For Kit architecture (optional)
npm install @solana/web-sdk @solana-program/system @solana/kit
```

## HTML Structure

```html index.html theme={null}
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Solana Wallet App</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div class="container">
    <h1>Solana Wallet Integration</h1>

    <!-- Wallet Selection -->
    <div id="wallet-selector" class="wallet-selector"></div>

    <!-- Wallet Info (hidden by default) -->
    <div id="wallet-info" class="wallet-info" style="display: none;">
      <h2>Connected Wallet</h2>
      <p><strong>Wallet:</strong> <span id="wallet-name"></span></p>
      <p><strong>Address:</strong> <span id="wallet-address"></span></p>
      <p><strong>Balance:</strong> <span id="wallet-balance">Loading...</span> SOL</p>
      <button id="disconnect-btn" class="button button-secondary">Disconnect</button>
    </div>
  </div>

  <script type="module" src="app.js"></script>
</body>
</html>
```

## Styling

```css style.css theme={null}
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}

.container {
  background: white;
  padding: 40px;
  border-radius: 20px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
  max-width: 600px;
  width: 100%;
}

h1 {
  color: #333;
  margin-bottom: 30px;
  text-align: center;
}

.wallet-selector {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.wallet-option {
  display: flex;
  align-items: center;
  padding: 15px 20px;
  border: 2px solid #e0e0e0;
  border-radius: 10px;
  background: white;
  cursor: pointer;
  transition: all 0.3s;
}

.wallet-option:hover {
  border-color: #667eea;
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2);
}

.wallet-option.installed {
  border-color: #14F195;
  background: #f0fdf4;
}

.wallet-info {
  text-align: center;
}

.wallet-info p {
  margin: 15px 0;
  color: #666;
}

.wallet-info strong {
  color: #333;
}

.button {
  padding: 12px 24px;
  border: none;
  border-radius: 8px;
  font-size: 16px;
  cursor: pointer;
  transition: all 0.3s;
  margin-top: 20px;
}

.button-secondary {
  background: #ff4757;
  color: white;
}

.button-secondary:hover {
  background: #ff3838;
}

#wallet-address {
  font-family: 'Courier New', monospace;
  font-size: 14px;
}
```

## Wallet Connection

<CodeGroup>
  ```javascript app.js (Web3.js) theme={null}
  import { WalletAdapterManager } from '@hermis/solana-headless-adapter-base';
  import { getStandardWalletAdapters } from '@hermis/wallet-standard-base';
  import { WalletAdapterNetwork } from '@hermis/solana-headless-core';
  import { Connection, clusterApiUrl, LAMPORTS_PER_SOL } from '@solana/web3.js';

  // Global state
  let manager;
  let connection;

  // Initialize wallet manager and connection
  async function initWalletManager() {
    connection = new Connection(clusterApiUrl('devnet'));

    // Auto-detect wallet-standard compatible wallets
    const wallets = []; // Add custom wallet adapters here if needed
    const adapters = await getStandardWalletAdapters(wallets, connection.rpcEndpoint, WalletAdapterNetwork.Devnet);

    manager = new WalletAdapterManager(adapters);
    setupEventListeners();
    renderWalletOptions();
  }

  // Setup wallet event listeners
  function setupEventListeners() {
    manager.on('connect', async (publicKey) => {
      console.log('Connected:', publicKey.toBase58());
      showWalletInfo(publicKey.toBase58());
      await updateBalance(publicKey);
    });

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

    manager.on('error', (error) => {
      console.error('Error:', error);
      alert(`Wallet error: ${error.message}`);
    });

    document.getElementById('disconnect-btn').onclick = () => {
      manager.disconnect();
    };
  }

  // Render available wallet options
  function renderWalletOptions() {
    const container = document.getElementById('wallet-selector');
    const adapters = manager.getAdapters();

    adapters.forEach(adapter => {
      const button = document.createElement('button');
      button.className = 'wallet-option';
      button.textContent = adapter.name;

      if (adapter.readyState === 'Installed') {
        button.classList.add('installed');
        const badge = document.createElement('span');
        badge.textContent = ' ✓ Installed';
        badge.style.color = '#14F195';
        badge.style.marginLeft = '10px';
        button.appendChild(badge);
      }

      button.onclick = async () => {
        manager.selectAdapter(adapter.name);
        try {
          await manager.connect();
        } catch (error) {
          console.error('Connection failed:', error);
        }
      };

      container.appendChild(button);
    });
  }

  // Update wallet balance
  async function updateBalance(publicKey) {
    try {
      const balance = await connection.getBalance(publicKey);
      const solBalance = balance / LAMPORTS_PER_SOL;
      document.getElementById('wallet-balance').textContent = solBalance.toFixed(4);
    } catch (error) {
      console.error('Failed to get balance:', error);
      document.getElementById('wallet-balance').textContent = 'Error';
    }
  }

  // Show wallet info UI
  function showWalletInfo(address) {
    document.getElementById('wallet-name').textContent = manager.getSelectedAdapter()?.name || 'Unknown';
    document.getElementById('wallet-address').textContent = address;
    document.getElementById('wallet-info').style.display = 'block';
    document.getElementById('wallet-selector').style.display = 'none';
  }

  // Hide wallet info UI
  function hideWalletInfo() {
    document.getElementById('wallet-info').style.display = 'none';
    document.getElementById('wallet-selector').style.display = 'flex';
  }

  // Initialize on load
  initWalletManager();
  ```

  ```javascript app.js (Kit) theme={null}
  import { WalletAdapterManager, createKitSignersFromAdapter } from '@hermis/solana-headless-adapter-base';
  import { getStandardWalletAdapters } from '@hermis/wallet-standard-base';
  import { WalletAdapterNetwork } from '@hermis/solana-headless-core';
  import { Connection, clusterApiUrl } from '@solana/web3.js';

  // Global state
  let manager;
  let connection;
  let kitSigners;

  // Initialize wallet manager with Kit support
  async function initWalletManager() {
    connection = new Connection(clusterApiUrl('devnet'));

    // Auto-detect wallet-standard compatible wallets
    const wallets = []; // Add custom wallet adapters here if needed
    const adapters = await getStandardWalletAdapters(wallets, connection.rpcEndpoint, WalletAdapterNetwork.Devnet);

    manager = new WalletAdapterManager(adapters);
    setupEventListeners();
    renderWalletOptions();
  }

  // Setup event listeners
  function setupEventListeners() {
    manager.on('connect', (publicKey) => {
      // Create Kit signers from connected adapter
      kitSigners = createKitSignersFromAdapter(
        manager.getSelectedAdapter(),
        connection
      );

      console.log('Kit signers created:');
      console.log('Address:', kitSigners.addressString);
      console.log('Has message signer:', !!kitSigners.messageSigner);
      console.log('Has transaction signer:', !!kitSigners.transactionSigner);

      showKitInfo(publicKey.toBase58());
    });

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

    document.getElementById('disconnect-btn').onclick = () => {
      manager.disconnect();
    };
  }

  // Render wallet options (same as Web3.js example)
  function renderWalletOptions() {
    const container = document.getElementById('wallet-selector');
    const adapters = manager.getAdapters();

    adapters.forEach(adapter => {
      const button = document.createElement('button');
      button.className = 'wallet-option';
      button.textContent = adapter.name;

      if (adapter.readyState === 'Installed') {
        button.classList.add('installed');
      }

      button.onclick = async () => {
        manager.selectAdapter(adapter.name);
        try {
          await manager.connect();
        } catch (error) {
          console.error('Connection failed:', error);
        }
      };

      container.appendChild(button);
    });
  }

  // Show Kit signer info
  function showKitInfo(address) {
    document.getElementById('wallet-name').textContent = manager.getSelectedAdapter()?.name || 'Unknown';
    document.getElementById('wallet-address').textContent = address;
    document.getElementById('wallet-info').style.display = 'block';
    document.getElementById('wallet-selector').style.display = 'none';
  }

  // Hide wallet info UI
  function hideWalletInfo() {
    document.getElementById('wallet-info').style.display = 'none';
    document.getElementById('wallet-selector').style.display = 'flex';
  }

  // Initialize on load
  initWalletManager();
  ```
</CodeGroup>

## Send Transaction

<CodeGroup>
  ```javascript Web3.js theme={null}
  import { Transaction, SystemProgram, PublicKey, LAMPORTS_PER_SOL } from '@solana/web3.js';

  // Send SOL transaction
  async function sendSOL(recipientAddress, amountInSOL) {
    const publicKey = manager.getAdapter()?.publicKey;
    if (!publicKey) {
      throw new Error('Wallet not connected');
    }

    try {
      const transaction = new Transaction();
      transaction.add(
        SystemProgram.transfer({
          fromPubkey: publicKey,
          toPubkey: new PublicKey(recipientAddress),
          lamports: amountInSOL * LAMPORTS_PER_SOL
        })
      );

      const { blockhash } = await connection.getLatestBlockhash();
      transaction.recentBlockhash = blockhash;
      transaction.feePayer = publicKey;

      const signature = await manager.signAndSendTransaction(connection, transaction);

      if (!signature) {
        throw new Error('Transaction failed');
      }

      console.log('✅ Transaction signature:', signature);

      // Wait for confirmation
      await connection.confirmTransaction(signature, 'confirmed');
      console.log('✅ Transaction confirmed!');

      return signature;
    } catch (error) {
      console.error('❌ Transaction error:', error);
      throw error;
    }
  }

  // Wire up send button
  document.getElementById('send-btn').onclick = async () => {
    const recipient = document.getElementById('recipient').value;
    const amount = parseFloat(document.getElementById('amount').value);
    const statusEl = document.getElementById('tx-status');

    if (!recipient || !amount) {
      statusEl.textContent = 'Please enter recipient and amount';
      return;
    }

    try {
      statusEl.textContent = 'Sending transaction...';
      const signature = await sendSOL(recipient, amount);
      statusEl.textContent = `Success! Signature: ${signature.substring(0, 20)}...`;
    } catch (error) {
      statusEl.textContent = `Error: ${error.message}`;
    }
  };
  ```

  ```javascript Kit theme={null}
  import {
    pipe,
    createTransactionMessage,
    setTransactionMessageFeePayer,
    setTransactionMessageLifetimeUsingBlockhash,
    appendTransactionMessageInstruction,
    address,
    lamports
  } from '@solana/kit';
  import { createSolanaRpc, devnet } from '@solana/kit';
  import { getTransferSolInstruction } from '@solana-program/system';

  // Send SOL using Kit pipe pattern
  async function sendSOLKit(recipientAddress, amountInSOL) {
    const publicKey = manager.getAdapter()?.publicKey;
    if (!publicKey) {
      throw new Error('Wallet not connected');
    }

    try {
      // Create Kit RPC
      const rpc = createSolanaRpc(devnet('https://api.devnet.solana.com'));

      // Get latest blockhash
      const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();

      // Build transaction using Kit's functional pipe pattern
      const message = pipe(
        createTransactionMessage({ version: 0 }),
        m => setTransactionMessageFeePayer(address(publicKey.toBase58()), m),
        m => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m),
        m => appendTransactionMessageInstruction(
          getTransferSolInstruction({
            source: address(publicKey.toBase58()),
            destination: address(recipientAddress),
            amount: lamports(BigInt(Math.floor(amountInSOL * 1_000_000_000)))
          }),
          m
        )
      );

      // Sign and send using manager
      const signature = await manager.signAndSendTransaction(rpc, message);

      console.log('✅ Kit transaction signature:', signature);
      return signature;
    } catch (error) {
      console.error('❌ Kit transaction error:', error);
      throw error;
    }
  }

  // Wire up send button
  document.getElementById('send-btn').onclick = async () => {
    const recipient = document.getElementById('recipient').value;
    const amount = parseFloat(document.getElementById('amount').value);
    const statusEl = document.getElementById('tx-status');

    if (!recipient || !amount) {
      statusEl.textContent = 'Please enter recipient and amount';
      return;
    }

    try {
      statusEl.textContent = 'Sending transaction...';
      const signature = await sendSOLKit(recipient, amount);
      statusEl.textContent = `Success! Signature: ${signature.substring(0, 20)}...`;
    } catch (error) {
      statusEl.textContent = `Error: ${error.message}`;
    }
  };
  ```
</CodeGroup>

**HTML for transaction UI:**

```html theme={null}
<!-- Add to wallet-info div -->
<div class="transaction-form">
  <h3>Send SOL</h3>
  <input type="text" id="recipient" placeholder="Recipient Address" />
  <input type="number" id="amount" placeholder="Amount (SOL)" step="0.01" min="0" />
  <button id="send-btn" class="button button-primary">Send Transaction</button>
  <p id="tx-status"></p>
</div>
```

## Sign Message

<CodeGroup>
  ```javascript Web3.js theme={null}
  // Sign message with wallet
  async function signMessage(message) {
    const publicKey = manager.getAdapter()?.publicKey;
    if (!publicKey) {
      throw new Error('Wallet not connected');
    }

    try {
      // Encode message to Uint8Array
      const encodedMessage = new TextEncoder().encode(message);

      // Sign using manager
      const signature = await manager.signMessage(encodedMessage);

      if (!signature) {
        throw new Error('Message signing failed');
      }

      console.log('✅ Signature:', signature);
      return signature;
    } catch (error) {
      console.error('❌ Signing error:', error);
      throw error;
    }
  }

  // Wire up sign button
  document.getElementById('sign-btn').onclick = async () => {
    const message = document.getElementById('message-input').value;
    const outputEl = document.getElementById('signature-output');

    if (!message) {
      outputEl.textContent = 'Please enter a message';
      return;
    }

    try {
      outputEl.textContent = 'Signing message...';
      const signature = await signMessage(message);
      outputEl.textContent = `Signature: ${Array.from(signature).slice(0, 10).join(', ')}...`;
    } catch (error) {
      outputEl.textContent = `Error: ${error.message}`;
    }
  };
  ```

  ```javascript Kit theme={null}
  import { signBytes } from '@solana/kit';

  // Sign message using Kit
  async function signMessageKit(message) {
    const publicKey = manager.getAdapter()?.publicKey;
    if (!publicKey || !kitSigners?.messageSigner) {
      throw new Error('Wallet not connected or message signing not supported');
    }

    try {
      // Encode message to Uint8Array
      const messageBytes = new TextEncoder().encode(message);

      // Sign using Kit message signer
      const signatures = await signBytes(kitSigners.messageSigner, messageBytes);
      const signature = signatures[kitSigners.messageSigner.address];

      console.log('✅ Kit signature:', signature);
      return signature;
    } catch (error) {
      console.error('❌ Kit signing error:', error);
      throw error;
    }
  }

  // Wire up sign button
  document.getElementById('sign-btn').onclick = async () => {
    const message = document.getElementById('message-input').value;
    const outputEl = document.getElementById('signature-output');

    if (!message) {
      outputEl.textContent = 'Please enter a message';
      return;
    }

    try {
      outputEl.textContent = 'Signing message...';
      const signature = await signMessageKit(message);
      outputEl.textContent = `Signature: ${Array.from(signature).slice(0, 10).join(', ')}...`;
    } catch (error) {
      outputEl.textContent = `Error: ${error.message}`;
    }
  };
  ```
</CodeGroup>

**HTML for message signing:**

```html theme={null}
<!-- Add to wallet-info div -->
<div class="message-form">
  <h3>Sign Message</h3>
  <textarea id="message-input" placeholder="Enter message to sign"></textarea>
  <button id="sign-btn" class="button button-primary">Sign Message</button>
  <p id="signature-output"></p>
</div>
```

## Build Setup

```json package.json theme={null}
{
  "name": "solana-wallet-app",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build"
  },
  "devDependencies": {
    "vite": "^5.0.0"
  }
}
```

## Running the Example

```bash theme={null}
npm run dev
```

Open [http://localhost:5173](http://localhost:5173) in your browser!

## Features Demonstrated

### Web3.js Architecture

<Check>Wallet connection with WalletAdapterManager</Check>
<Check>SOL balance fetching</Check>
<Check>Transaction sending (SOL transfers)</Check>
<Check>Message signing</Check>
<Check>Event-driven wallet state</Check>
<Check>Error handling</Check>

### Kit Architecture

<Check>Kit signer creation from adapters</Check>
<Check>Kit transaction building with pipe pattern</Check>
<Check>Kit message signing with signBytes</Check>
<Check>Type-safe instruction building</Check>
<Check>Functional composition with pipe</Check>
<Check>Modern RPC interface</Check>

### Both Architectures

<Check>Same WalletAdapterManager for connection</Check>
<Check>Seamless bridge via createKitSignersFromAdapter</Check>
<Check>Multi-wallet support</Check>
<Check>Event-driven architecture</Check>
