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.
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
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
<! 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
* {
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 ( 135 deg , #667eea 0 % , #764ba2 100 % );
min-height : 100 vh ;
display : flex ;
align-items : center ;
justify-content : center ;
padding : 20 px ;
}
.container {
background : white ;
padding : 40 px ;
border-radius : 20 px ;
box-shadow : 0 20 px 60 px rgba ( 0 , 0 , 0 , 0.3 );
max-width : 600 px ;
width : 100 % ;
}
h1 {
color : #333 ;
margin-bottom : 30 px ;
text-align : center ;
}
.wallet-selector {
display : flex ;
flex-direction : column ;
gap : 10 px ;
}
.wallet-option {
display : flex ;
align-items : center ;
padding : 15 px 20 px ;
border : 2 px solid #e0e0e0 ;
border-radius : 10 px ;
background : white ;
cursor : pointer ;
transition : all 0.3 s ;
}
.wallet-option:hover {
border-color : #667eea ;
transform : translateY ( -2 px );
box-shadow : 0 4 px 12 px rgba ( 102 , 126 , 234 , 0.2 );
}
.wallet-option.installed {
border-color : #14F195 ;
background : #f0fdf4 ;
}
.wallet-info {
text-align : center ;
}
.wallet-info p {
margin : 15 px 0 ;
color : #666 ;
}
.wallet-info strong {
color : #333 ;
}
.button {
padding : 12 px 24 px ;
border : none ;
border-radius : 8 px ;
font-size : 16 px ;
cursor : pointer ;
transition : all 0.3 s ;
margin-top : 20 px ;
}
.button-secondary {
background : #ff4757 ;
color : white ;
}
.button-secondary:hover {
background : #ff3838 ;
}
#wallet-address {
font-family : 'Courier New' , monospace ;
font-size : 14 px ;
}
Wallet Connection
app.js (Web3.js)
app.js (Kit)
import { WalletAdapterManager , getStandardWalletAdapters } from '@hermis/solana-headless-adapter-base' ;
import { Connection , clusterApiUrl , LAMPORTS_PER_SOL } from '@solana/web3.js' ;
// Global state
let manager ;
let connection ;
// Initialize wallet manager and connection
function initWalletManager () {
connection = new Connection ( clusterApiUrl ( 'devnet' ));
// Auto-detect wallet-standard compatible wallets
const wallets = []; // Add custom wallet adapters here if needed
const adapters = getStandardWalletAdapters ( wallets );
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 ();
Send Transaction
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 } ` ;
}
};
HTML for transaction UI:
<!-- 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
// 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 } ` ;
}
};
HTML for message signing:
<!-- 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
{
"name" : "solana-wallet-app" ,
"type" : "module" ,
"scripts" : {
"dev" : "vite" ,
"build" : "vite build"
},
"devDependencies" : {
"vite" : "^5.0.0"
}
}
Running the Example
Open http://localhost:5173 in your browser!
Features Demonstrated
Web3.js Architecture
Wallet connection with WalletAdapterManager
Transaction sending (SOL transfers)
Event-driven wallet state
Kit Architecture
Kit signer creation from adapters
Kit transaction building with pipe pattern
Kit message signing with signBytes
Type-safe instruction building
Functional composition with pipe
Both Architectures
Same WalletAdapterManager for connection
Seamless bridge via createKitSignersFromAdapter
Event-driven architecture