Web Browser APIs (W3C)
Revolutionary client integration patterns using HTMX, Cross-Origin iFrames, and Browser APIs to solve crypto UX problems across maximum devices with seamless ease of control
Web Browser APIs Integration
Scope
This document covers Sonr's client integration patterns using standard web technologies: HTMX for dynamic interfaces, Cross-Origin iFrames for secure isolation, and native Browser APIs for device capabilities. It includes implementation patterns, security models, and device compatibility. This document does not cover low-level protocol details—see the DWN module reference for Vault specifications.
Audience
Web developers building crypto-enabled applications. Frontend engineers integrating blockchain features. System architects designing secure client architectures. Prerequisites: HTML/CSS/JavaScript knowledge, understanding of web security concepts (CORS, CSP), and basic familiarity with browser APIs.
Summary
Sonr enables crypto integration using standard web technologies instead of blockchain SDKs. Developers use HTMX for server-driven UI, Cross-Origin iFrames for wallet isolation, and Browser APIs for authentication and storage. Applications work on any device with a web browser—no downloads, extensions, or blockchain knowledge required. The system maintains security through browser sandboxing and UCAN authorization.
Architecture Overview
Traditional vs. Sonr Approach
Traditional Web3 requires complex client-side libraries:
// Traditional: Complex wallet integration
import { ethers } from "ethers";
import { WalletConnectConnector } from "@web3-react/walletconnect";
// Different code for each wallet type
if (window.ethereum?.isMetaMask) {
// MetaMask specific logic
} else if (window.ethereum?.isCoinbaseWallet) {
// Coinbase specific logic
}
Sonr uses standard web patterns:
<!-- Sonr: Simple HTML form -->
<form hx-post="/api/sonr/payment" hx-target="#result">
<input type="email" name="recipient" placeholder="alice@sonr.id" />
<input type="number" name="amount" placeholder="25.00" />
<button type="submit">Send Payment</button>
</form>
<div id="result"></div>
Component Architecture
HTMX Integration
Server-Driven Interfaces
HTMX enables dynamic crypto interfaces without JavaScript frameworks:
<!-- Payment form with HTMX -->
<form
hx-post="/api/sonr/payment"
hx-target="#payment-result"
hx-indicator="#loading"
>
<input type="email" name="recipient" required />
<input type="number" name="amount" step="0.01" required />
<select name="currency">
<option value="USDC">USDC</option>
<option value="ATOM">ATOM</option>
</select>
<button type="submit">
Send Payment
<span id="loading" class="htmx-indicator">⏳</span>
</button>
</form>
<div id="payment-result"></div>
Server returns HTML responses:
<!-- Success response -->
<div class="success">
<h3>✅ Payment Sent</h3>
<p>Sent $25.00 USDC to alice@sonr.id</p>
<p>Transaction: <code>tx_1a2b3c4d</code></p>
</div>
Real-Time Updates
Live blockchain data without WebSockets:
<!-- Auto-updating portfolio -->
<div hx-get="/api/sonr/portfolio" hx-trigger="every 30s" hx-swap="innerHTML">
<h2>Portfolio: $12,543.21</h2>
<!-- Server renders current balances -->
</div>
<!-- Live transaction feed -->
<div
hx-get="/api/sonr/transactions"
hx-trigger="load, every 10s"
hx-target="#tx-list"
>
<div id="tx-list">
<!-- Server renders transaction history -->
</div>
</div>
Progressive Enhancement
Forms work without JavaScript, enhance with HTMX:
<!-- Basic form -->
<form action="/api/sonr/stake" method="POST">
<input type="number" name="amount" required />
<button type="submit">Stake ATOM</button>
</form>
<!-- Enhanced with HTMX -->
<form
hx-post="/api/sonr/stake"
hx-target="#result"
hx-confirm="Stake this amount?"
>
<input
type="number"
name="amount"
hx-get="/api/validate-stake"
hx-trigger="keyup changed delay:500ms"
hx-target="#validation"
/>
<div id="validation"></div>
<button type="submit">Stake ATOM</button>
</form>
<div id="result"></div>
Cross-Origin iFrames
Secure Wallet Isolation
Vaults run in isolated iFrames for security:
<!-- Host application (your-app.com) -->
<div class="app-content">
<h1>Your DeFi App</h1>
<!-- Sonr Vault iFrame -->
<iframe
id="sonr-vault"
src="https://vault.sonr.id/embed"
sandbox="allow-scripts allow-same-origin"
style="width: 300px; height: 400px;"
>
</iframe>
</div>
<script>
// Secure communication
const vault = document.getElementById("sonr-vault");
function requestPayment(amount, recipient) {
vault.contentWindow.postMessage(
{
type: "REQUEST_CAPABILITY",
capability: "payment:send",
params: { amount, recipient },
},
"https://vault.sonr.id",
);
}
// Listen for responses
window.addEventListener("message", (event) => {
if (event.origin !== "https://vault.sonr.id") return;
switch (event.data.type) {
case "CAPABILITY_GRANTED":
console.log("Payment authorized");
break;
case "TRANSACTION_COMPLETE":
console.log("Transaction done");
break;
}
});
</script>
Vault Implementation
The Vault handles user authorization:
// Inside vault.sonr.id
class VaultMessenger {
handleMessage(event) {
// Verify origin
if (!this.isAllowedOrigin(event.origin)) return;
switch (event.data.type) {
case "REQUEST_CAPABILITY":
this.showAuthDialog(event.data);
break;
}
}
showAuthDialog(request) {
// Show user-friendly prompt
const dialog = {
title: `${request.origin} wants to send payments`,
description: `Allow up to $${request.params.amount}?`,
actions: ["Approve", "Deny"],
};
// User decision
dialog.onApprove = () => {
const token = this.generateUCAN(request);
parent.postMessage(
{
type: "CAPABILITY_GRANTED",
token: token,
},
request.origin,
);
};
}
}
Browser API Integration
WebAuthn Authentication
Passwordless authentication using device biometrics:
// Create credential
async function createAccount(email) {
const credential = await navigator.credentials.create({
publicKey: {
challenge: crypto.getRandomValues(new Uint8Array(32)),
rp: { name: "Sonr Network" },
user: {
id: crypto.getRandomValues(new Uint8Array(32)),
name: email,
displayName: email,
},
pubKeyCredParams: [{ alg: -7, type: "public-key" }],
authenticatorSelection: {
authenticatorAttachment: "platform",
userVerification: "required",
},
},
});
return credential;
}
// Authenticate
async function authenticate() {
const credential = await navigator.credentials.get({
publicKey: {
challenge: new TextEncoder().encode("login-challenge"),
userVerification: "required",
},
});
return credential;
}
IndexedDB Storage
Encrypted local storage for Vault data:
class VaultStorage {
async initialize() {
const request = indexedDB.open("SonrVault", 1);
request.onupgradeneeded = (event) => {
const db = event.target.result;
db.createObjectStore("vault_data", { keyPath: "id" });
db.createObjectStore("transactions", { keyPath: "txId" });
};
this.db = await new Promise((resolve, reject) => {
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
async store(id, data) {
// Encrypt before storage
const encrypted = await this.encrypt(data);
const tx = this.db.transaction(["vault_data"], "readwrite");
return tx.objectStore("vault_data").put({ id, data: encrypted });
}
async encrypt(data) {
const key = await this.getEncryptionKey();
const iv = crypto.getRandomValues(new Uint8Array(12));
const encrypted = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv },
key,
new TextEncoder().encode(JSON.stringify(data)),
);
return { iv, data: encrypted };
}
}
ServiceWorker Background Operations
Handle blockchain operations in the background:
// vault-worker.js
self.addEventListener("message", async (event) => {
const { type, data } = event.data;
switch (type) {
case "EXECUTE_TRANSACTION":
await handleTransaction(data, event.ports[0]);
break;
case "SYNC_STATE":
await syncVaultState(data, event.ports[0]);
break;
}
});
async function handleTransaction(txData, port) {
try {
// Validate transaction
if (!(await validateTransaction(txData))) {
throw new Error("Invalid transaction");
}
// Execute on blockchain
const result = await executeTransaction(txData);
// Notify all clients
const clients = await self.clients.matchAll();
clients.forEach((client) => {
client.postMessage({
type: "TRANSACTION_COMPLETE",
result,
});
});
port.postMessage({ result });
} catch (error) {
port.postMessage({ error: error.message });
}
}
Device Compatibility
Responsive Design
Sonr clients adapt to all screen sizes:
/* Mobile-first design */
.sonr-vault {
width: 100%;
height: 60vh;
}
/* Tablets */
@media (min-width: 768px) {
.sonr-vault {
width: 400px;
height: 500px;
border-radius: 12px;
}
}
/* Desktop */
@media (min-width: 1024px) {
.sonr-vault {
width: 320px;
height: 600px;
position: fixed;
right: 20px;
top: 20px;
}
}
Progressive Web App
Transform websites into app experiences:
{
"name": "DeFi App with Sonr",
"short_name": "DeFi+Sonr",
"display": "standalone",
"start_url": "/",
"icons": [
{
"src": "/icon-192.png",
"sizes": "192x192",
"type": "image/png"
}
],
"shortcuts": [
{
"name": "Send Payment",
"url": "/pay",
"icons": [{ "src": "/pay.png", "sizes": "96x96" }]
}
]
}
Integration Examples
E-commerce Checkout
Add crypto payments to any store:
<!-- Checkout form -->
<div class="checkout">
<h2>Order Total: $29.99</h2>
<button hx-post="/api/crypto-checkout" hx-target="#checkout-result">
💎 Pay with Crypto
</button>
<div id="checkout-result"></div>
</div>
<!-- Server returns payment interface -->
<iframe
src="https://pay.sonr.id/checkout?amount=29.99"
width="100%"
height="400"
>
</iframe>
Content Tipping
Enable micropayments on content:
<!-- Tip buttons -->
<div class="tip-section">
<p>Enjoyed this article?</p>
<button
hx-post="/api/tip"
hx-vals='{"amount": "1", "currency": "USDC"}'
hx-target="#tip-result"
>
$1 💙
</button>
<button
hx-post="/api/tip"
hx-vals='{"amount": "5", "currency": "USDC"}'
hx-target="#tip-result"
>
$5 ❤️
</button>
<div id="tip-result"></div>
</div>
Drop-in Widget
Add Sonr to any site with one line:
<!-- Add wallet widget -->
<script
src="https://cdn.sonr.id/wallet-widget.js"
data-position="bottom-right"
data-theme="light"
></script>
Security Considerations
Content Security Policy
Configure CSP headers for iFrame security:
Content-Security-Policy:
frame-src https://vault.sonr.id;
connect-src https://api.sonr.id;
script-src 'self' https://cdn.sonr.id;
Origin Verification
Always verify message origins:
window.addEventListener("message", (event) => {
// Strict origin check
if (event.origin !== "https://vault.sonr.id") {
return;
}
// Validate message structure
if (!event.data.type || !event.data.signature) {
return;
}
// Process trusted message
handleVaultMessage(event.data);
});
Permission Scoping
Request minimal capabilities:
// Good: Specific permission
vault.requestCapability({
action: "transfer",
constraints: {
maxAmount: "100",
allowedTokens: ["USDC"],
expires: Date.now() + 3600000, // 1 hour
},
});
// Bad: Unlimited permission
vault.requestCapability({
action: "transfer",
constraints: {}, // No limits
});
Performance Optimization
Lazy Loading
Load features on demand:
// Register features
const features = new Map([
["trading", () => import("./features/trading.js")],
["nft", () => import("./features/nft.js")],
["defi", () => import("./features/defi.js")],
]);
// Load when needed
async function loadFeature(name) {
const loader = features.get(name);
if (loader) {
const module = await loader();
return module.initialize();
}
}
// Intersection observer for auto-loading
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const feature = entry.target.dataset.feature;
loadFeature(feature);
}
});
});
Caching Strategy
Cache blockchain data intelligently:
class SonrCache {
async get(key) {
// Check memory first
const cached = this.memory.get(key);
if (cached && cached.expiry > Date.now()) {
return cached.data;
}
// Check IndexedDB
const stored = await this.getFromDB(key);
if (stored && stored.expiry > Date.now()) {
return stored.data;
}
return null;
}
async set(key, data, ttl = 60000) {
const entry = {
data,
expiry: Date.now() + ttl,
};
// Store in memory
this.memory.set(key, entry);
// Persist to IndexedDB
await this.saveToDB(key, entry);
}
}
Best Practices
For Developers
- Use semantic HTML: Structure content properly
- Progressive enhancement: Ensure functionality without JavaScript
- Secure communications: Always verify origins and validate messages
- Optimize performance: Implement caching and lazy loading
- Test across devices: Ensure compatibility with various screen sizes
For Security
- Strict CSP policies: Limit resource loading
- Origin validation: Never trust unverified messages
- Minimal permissions: Request only necessary capabilities
- Regular updates: Keep dependencies current
- Audit regularly: Review security practices
Next Steps
Ready to Build?
Developers: Start with the Quick Start Guide to build your first Sonr application.
Frontend Engineers: Explore HTMX Patterns for advanced techniques.
Architects: Review Security Best Practices for production deployments.
Learn how browser integration connects with other Sonr components by exploring the Wallet Architecture or Service Integration documentation.
On-Chain Authorization
Stake-based service registration system using DNS verification and UCAN capabilities for decentralized authentication and authorization
Bridgeless Transactions (IBC)
Cross-chain communication and interoperability using IBC v2 and IBC Classic for seamless blockchain integration, token transfers, and interchain account management