DApp Integration Guide
Complete guide for integrating your decentralized application with Hoosat Wallet Extension.
Quick Start
Detection
First, check if Hoosat Wallet is installed and available:
// Check if Hoosat Wallet is installed
if (window.hoosat) {
console.log('✅ Hoosat Wallet detected!');
} else {
console.log('❌ Hoosat Wallet not found');
// Show installation prompt to user
}
Wait for Provider Initialization
The provider emits an event when it's ready:
// Wait for provider initialization
window.addEventListener('hoosat#initialized', () => {
console.log('🚀 Hoosat provider ready');
// Safe to use window.hoosat now
});
Complete Example
<!DOCTYPE html>
<html>
<head>
<title>My Hoosat DApp</title>
</head>
<body>
<h1>Hoosat DApp Example</h1>
<div id="status">Checking for wallet...</div>
<button id="connect" style="display:none">Connect Wallet</button>
<div id="account" style="display:none"></div>
<script>
async function init() {
// Check if wallet exists
if (!window.hoosat) {
document.getElementById('status').textContent =
'Please install Hoosat Wallet Extension';
return;
}
document.getElementById('status').textContent = 'Wallet found!';
document.getElementById('connect').style.display = 'block';
// Setup connect button
document.getElementById('connect').onclick = async () => {
try {
const accounts = await window.hoosat.requestAccounts();
document.getElementById('connect').style.display = 'none';
document.getElementById('account').style.display = 'block';
document.getElementById('account').textContent =
`Connected: ${accounts[0]}`;
} catch (error) {
console.error('Connection rejected:', error);
}
};
}
// Wait for provider
window.addEventListener('hoosat#initialized', init);
// Or if provider is already loaded
if (window.hoosat) init();
</script>
</body>
</html>
API Reference
Provider Object
The window.hoosat object provides the following interface:
interface HoosatProvider {
// Properties
isHoosat: boolean; // Always true
isConnected: boolean; // Connection status
// Connection
requestAccounts(): Promise<string[]>;
connect(): Promise<string[]>; // Alias for requestAccounts
getAccounts(): Promise<string[]>;
disconnect(): Promise<void>; // Disconnect from wallet
// Blockchain Queries
getBalance(address: string): Promise<string>;
getNetwork(): Promise<string>;
// Transactions & Signing
sendTransaction(params: TransactionParams): Promise<string>;
signMessage(message: string): Promise<string>;
// Events (future)
on(event: string, callback: Function): void;
removeListener(event: string, callback: Function): void;
}
API Methods Summary
| Method | Parameters | Returns | Description |
|---|---|---|---|
requestAccounts() | - | Promise<string[]> | Request connection (shows popup) |
connect() | - | Promise<string[]> | Alias for requestAccounts |
getAccounts() | - | Promise<string[]> | Get connected accounts (no popup) |
disconnect() | - | Promise<void> | Disconnect from wallet (removes from Connected Sites) |
getBalance(address) | address: string | Promise<string> | Get balance in sompi |
sendTransaction(params) | {to, amount, fee?} | Promise<string> | Send transaction, returns TX ID |
signMessage(message) | message: string | Promise<string> | Sign message, returns signature |
getNetwork() | - | Promise<string> | Get current network |
Connection Management
Request Connection
Request user permission to connect to your dApp:
// Request connection (shows approval popup)
try {
const accounts = await window.hoosat.requestAccounts();
console.log('Connected address:', accounts[0]);
// Returns: ["hoosat:qp4ad2eh72xc8..."]
} catch (error) {
console.error('Connection rejected:', error);
// error.code === 4001 (USER_REJECTED)
}
Alternative Method:
// Using connect() alias
const accounts = await window.hoosat.connect();
Check Connection Status
Get connected accounts without showing a popup:
// Get connected accounts (no popup)
const accounts = await window.hoosat.getAccounts();
if (accounts.length > 0) {
console.log('Already connected:', accounts[0]);
} else {
console.log('Not connected');
// Show "Connect Wallet" button
}
Connection Flow
async function connectWallet() {
try {
// Check if already connected
let accounts = await window.hoosat.getAccounts();
if (accounts.length === 0) {
// Not connected - request connection
accounts = await window.hoosat.requestAccounts();
}
// Connected successfully
const address = accounts[0];
console.log('Connected:', address);
// Update UI
updateUI(address);
// Fetch user data
const balance = await window.hoosat.getBalance(address);
console.log('Balance:', balance, 'sompi');
return address;
} catch (error) {
if (error.code === 4001) {
console.log('User rejected connection');
} else {
console.error('Connection error:', error);
}
}
}
Disconnect from Wallet
DApps can programmatically disconnect from the wallet, removing the site from the Connected Sites list:
// Disconnect from wallet
try {
await window.hoosat.disconnect();
console.log('✅ Disconnected successfully');
// Site is removed from "Connected Sites" in the wallet
} catch (error) {
console.error('Disconnect failed:', error);
}
When to use disconnect:
- User logs out from your DApp
- User switches to a different wallet
- Session expires or becomes invalid
- User explicitly requests to disconnect
Benefits:
- Keeps the wallet's Connected Sites list clean and organized
- Better user privacy and security
- Clear connection state management
- Professional UX (similar to MetaMask, WalletConnect)
Complete logout example:
async function logout() {
try {
// 1. Clear local session data
localStorage.removeItem('authToken');
localStorage.removeItem('userAddress');
// 2. Disconnect from wallet
await window.hoosat.disconnect();
// 3. Update UI
updateUIForLoggedOutState();
console.log('✅ Logged out successfully');
} catch (error) {
console.error('Logout failed:', error);
}
}
Querying Blockchain
Get Balance
// Get balance for address (returns sompi)
const balance = await window.hoosat.getBalance(address);
console.log('Balance:', balance, 'sompi');
// Convert sompi to HTN
const balanceHTN = parseInt(balance) / 100000000;
console.log('Balance:', balanceHTN, 'HTN');
Get Network
// Get current network
const network = await window.hoosat.getNetwork();
console.log('Network:', network);
// Returns: "mainnet" or "testnet"
// Use network for validation
if (network !== 'mainnet') {
alert('Please switch to mainnet');
}
Sending Transactions
Basic Transaction
// Send transaction (shows approval popup)
try {
const txId = await window.hoosat.sendTransaction({
to: 'hoosat:qp4ad2eh72xc8...', // Recipient address
amount: 150000000, // Amount in sompi (1.5 HTN)
});
console.log('✅ Transaction sent!');
console.log('TX ID:', txId);
// Returns: "2a3b4c5d6e7f8..."
// Show success message
alert(`Transaction sent! TX ID: ${txId}`);
} catch (error) {
if (error.code === 4001) {
console.log('User rejected transaction');
} else if (error.code === 4100) {
console.log('Not connected - need to connect first');
} else {
console.error('Transaction failed:', error.message);
}
}
Transaction with Custom Fee
// Send with custom fee
const txId = await window.hoosat.sendTransaction({
to: 'hoosat:qp...',
amount: 100000000, // 1 HTN
fee: '5000' // 5000 sompi fee (optional)
});
Transaction Parameters
interface TransactionParams {
to: string; // Recipient address (required)
amount: number | string; // Amount in sompi (required)
fee?: string; // Custom fee in sompi (optional)
}
Amount Conversion:
// HTN to sompi
function htnToSompi(htn) {
return Math.floor(htn * 100000000);
}
// Send 1.5 HTN
await window.hoosat.sendTransaction({
to: recipientAddress,
amount: htnToSompi(1.5) // 150000000 sompi
});
Complete Payment Flow
async function sendPayment(recipient, amountHTN) {
try {
// 1. Check connection
const accounts = await window.hoosat.getAccounts();
if (accounts.length === 0) {
throw new Error('Not connected');
}
// 2. Validate address
if (!recipient.startsWith('hoosat:')) {
throw new Error('Invalid recipient address');
}
// 3. Check balance
const balance = await window.hoosat.getBalance(accounts[0]);
const balanceHTN = parseInt(balance) / 100000000;
const amountSompi = Math.floor(amountHTN * 100000000);
if (balanceHTN < amountHTN) {
throw new Error('Insufficient balance');
}
// 4. Send transaction
const txId = await window.hoosat.sendTransaction({
to: recipient,
amount: amountSompi
});
// 5. Success
console.log('Payment successful!', txId);
return txId;
} catch (error) {
console.error('Payment failed:', error);
throw error;
}
}
Message Signing
Sign Message
// Sign a message
try {
const message = 'Hello World';
const signature = await window.hoosat.signMessage(message);
console.log('✅ Message signed!');
console.log('Signature:', signature);
// Returns: "3045022100ab12cd34ef..." (128 hex chars)
} catch (error) {
if (error.code === 4001) {
console.log('User rejected signing');
}
}
Verify Signature (Backend)
// Backend verification using hoosat-sdk
import { HoosatSigner } from 'hoosat-sdk';
function verifySignature(signature, message, address) {
// Get public key from address (or from user profile)
const publicKey = getPublicKeyForAddress(address);
// Verify signature
const isValid = HoosatSigner.verifyMessage(
signature,
message,
publicKey
);
return isValid;
}
Authentication Patterns
"Sign In with Hoosat"
Complete authentication flow:
// Frontend
async function signInWithHoosat() {
try {
// 1. Connect wallet
const accounts = await window.hoosat.requestAccounts();
const address = accounts[0];
// 2. Request challenge from server
const challengeRes = await fetch('/api/auth/challenge', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ address })
});
const { challenge, nonce } = await challengeRes.json();
// 3. Sign challenge
const signature = await window.hoosat.signMessage(challenge);
// 4. Verify with server
const verifyRes = await fetch('/api/auth/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
address,
signature,
nonce
})
});
const { token, user } = await verifyRes.json();
// 5. Store token
localStorage.setItem('authToken', token);
console.log('✅ Signed in:', user);
return user;
} catch (error) {
console.error('Sign in failed:', error);
}
}
Backend Implementation
// Express.js backend
import { HoosatSigner } from 'hoosat-sdk';
app.post('/api/auth/challenge', (req, res) => {
const { address } = req.body;
// Generate challenge
const nonce = crypto.randomUUID();
const timestamp = Date.now();
const challenge = `Sign in to MyDApp\nNonce: ${nonce}\nTimestamp: ${timestamp}`;
// Store nonce in session/redis
sessions.set(address, { nonce, challenge, timestamp });
res.json({ challenge, nonce });
});
app.post('/api/auth/verify', (req, res) => {
const { address, signature, nonce } = req.body;
// Get stored challenge
const session = sessions.get(address);
if (!session || session.nonce !== nonce) {
return res.status(401).json({ error: 'Invalid nonce' });
}
// Check timestamp (max 5 minutes)
if (Date.now() - session.timestamp > 5 * 60 * 1000) {
return res.status(401).json({ error: 'Challenge expired' });
}
// Get public key for address
const publicKey = getPublicKeyForAddress(address);
// Verify signature
const isValid = HoosatSigner.verifyMessage(
signature,
session.challenge,
publicKey
);
if (!isValid) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Create session/JWT
const token = jwt.sign({ address }, SECRET_KEY);
// Get or create user
const user = findOrCreateUser(address);
res.json({ token, user });
});
Proof of Ownership
async function proveOwnership(address) {
const message = `I own this address: ${address}`;
const signature = await window.hoosat.signMessage(message);
// Submit proof
await fetch('/api/verify-ownership', {
method: 'POST',
body: JSON.stringify({ address, message, signature })
});
}
Error Handling
Error Codes
const ErrorCodes = {
USER_REJECTED: 4001, // User rejected request
UNAUTHORIZED: 4100, // Not connected
UNSUPPORTED_METHOD: 4200, // Method not supported
DISCONNECTED: 4900, // Wallet disconnected
CHAIN_DISCONNECTED: 4901 // Blockchain disconnected
};
Handling Errors
try {
const txId = await window.hoosat.sendTransaction({...});
} catch (error) {
switch (error.code) {
case 4001:
console.log('User rejected the request');
break;
case 4100:
console.log('Not connected - please connect wallet');
// Show connect button
break;
case 4900:
console.log('Wallet disconnected');
// Reconnect
break;
default:
console.error('Transaction failed:', error.message);
}
}
Best Practices
async function safeRequest(fn) {
try {
return await fn();
} catch (error) {
// Log error
console.error('Request failed:', error);
// User-friendly message
let userMessage;
switch (error.code) {
case 4001:
userMessage = 'You rejected the request';
break;
case 4100:
userMessage = 'Please connect your wallet first';
break;
default:
userMessage = 'Something went wrong. Please try again.';
}
// Show to user
alert(userMessage);
// Re-throw for caller
throw error;
}
}
// Usage
await safeRequest(() => window.hoosat.sendTransaction({...}));
Use Cases
1. NFT Marketplace
async function purchaseNFT(nftId, priceHTN) {
// Connect wallet
const accounts = await window.hoosat.connect();
// Get NFT details
const nft = await fetchNFT(nftId);
// Send payment
const txId = await window.hoosat.sendTransaction({
to: nft.sellerAddress,
amount: Math.floor(priceHTN * 100000000)
});
// Transfer NFT to buyer
await transferNFT(nftId, accounts[0], txId);
}
2. DAO Voting
async function castVote(proposalId, vote) {
// Connect wallet
const accounts = await window.hoosat.connect();
// Create vote message
const message = JSON.stringify({
proposalId,
vote,
voter: accounts[0],
timestamp: Date.now()
});
// Sign vote
const signature = await window.hoosat.signMessage(message);
// Submit vote
await fetch('/api/dao/vote', {
method: 'POST',
body: JSON.stringify({ message, signature })
});
}
3. Payment Gateway
async function checkout(cart, total) {
// Connect wallet
const accounts = await window.hoosat.connect();
// Create invoice
const invoice = await createInvoice(cart, accounts[0]);
// Request payment
const txId = await window.hoosat.sendTransaction({
to: invoice.merchantAddress,
amount: invoice.totalSompi
});
// Confirm order
await confirmOrder(invoice.id, txId);
return { invoice, txId };
}
Testing
Test DApp
The extension includes a comprehensive test DApp (test-dapp.html):
# After building extension
# Open test-dapp.html in your browser
# Tests all features:
# - Connection
# - Disconnect
# - Balance queries
# - Transactions
# - Message signing
# - Error handling
Manual Testing
// Test connection
await window.hoosat.requestAccounts();
// Test disconnect
await window.hoosat.disconnect();
// Test balance
await window.hoosat.getBalance('hoosat:qp...');
// Test transaction
await window.hoosat.sendTransaction({
to: 'hoosat:qp...',
amount: 100000000 // 1 HTN
});
// Test message signing
await window.hoosat.signMessage('Test message');
// Test network
await window.hoosat.getNetwork();
Migration from Other Wallets
From MetaMask
Hoosat Wallet uses a similar API to MetaMask:
// MetaMask
await window.ethereum.request({ method: 'eth_requestAccounts' });
// Hoosat Wallet
await window.hoosat.requestAccounts();
Key Differences:
- Different provider object (
window.hoosatvswindow.ethereum) - Different method names (simpler, more direct)
- Amount in sompi (not wei)
- BLAKE3 hashing (not keccak256)
Next Steps
- User Guide - Guide for wallet users
- Browser SDK - Build your own wallet
Support
- GitHub Issues: Report a bug
- Examples: Test DApp
- Community: Discord | Telegram