RPC & Utilities
Query blockchain data, resolve usernames, and use utility helpers with @oviato/sdk
@oviato/sdk provides a powerful RPC client for querying blockchain data and a collection of utility functions for common tasks.
RPC Client Overview
The RPC client uses JSON-RPC 2.0 protocol to communicate with Oviato's backend services. It automatically handles:
- Authentication - Includes auth tokens when available
- Network context - Routes requests to the correct network
- Rate limiting - Respects backend rate limits
- Error handling - Provides typed error responses
import { rpc, rpcRequest } from "@oviato/sdk";
// Use the default client
const balance = await rpc.getBalance({ address: "bc1q..." });Get Balance
Retrieve the balance for any address. The response format varies based on network type.
import { rpc, getSession } from "@oviato/sdk";
const session = getSession();
const result = await rpc.getBalance({
address: session.address,
});
if (result.result) {
const balance = result.result;
console.log("Address:", balance.address);
console.log("Network:", balance.networkName);
console.log("Symbol:", balance.symbol);
// Balance is returned in three formats
console.log("Raw (satoshis/wei):", balance.balance.raw);
console.log("Decimal value:", balance.balance.value);
console.log("Formatted:", balance.balance.formatted); // e.g., "1.5 BTC"
}EVM Balance Response
For Ethereum and EVM-compatible networks:
interface EvmBalanceResponse {
address: string;
network: string; // e.g., "ethmainnet"
networkName: string; // e.g., "Ethereum Mainnet"
chainType: "evm";
symbol: string; // e.g., "ETH"
decimals: number; // e.g., 18
balance: {
raw: string; // Balance in wei
value: string; // Decimal value (e.g., "1.50000000")
formatted: string; // Human-readable (e.g., "1.5 ETH")
};
}UTXO Balance Response
For Bitcoin and UTXO-based networks, includes confirmed/unconfirmed breakdown:
interface UtxoBalanceResponse {
address: string;
network: string; // e.g., "bitcoinmainnet"
networkName: string; // e.g., "Bitcoin Mainnet"
chainType: "utxo";
symbol: string; // e.g., "BTC"
decimals: number; // e.g., 8
balance: {
raw: string; // Total balance in satoshis
value: string; // Decimal value (e.g., "0.00100000")
formatted: string; // Human-readable (e.g., "0.001 BTC")
confirmed: { // On-chain confirmed balance
raw: string;
value: string;
formatted: string;
};
unconfirmed: { // Mempool unconfirmed balance
raw: string;
value: string;
formatted: string;
};
};
}Display Balance Example
import { rpc, getSession } from "@oviato/sdk";
async function displayBalance() {
const session = getSession();
const result = await rpc.getBalance({ address: session.address });
if (!result.result) {
console.error("Failed to fetch balance");
return;
}
const { balance, chainType, symbol } = result.result;
// Show formatted balance
console.log(`Balance: ${balance.formatted}`);
// For UTXO networks, show confirmed/unconfirmed
if (chainType === "utxo") {
console.log(`Confirmed: ${balance.confirmed.formatted}`);
console.log(`Pending: ${balance.unconfirmed.formatted}`);
}
}Resolve OVI.ID Username
Look up blockchain addresses associated with an OVI.ID username:
import { resolveUsername } from "@oviato/sdk";
const result = await resolveUsername("alice");
if (result.status === "success") {
console.log("Names:", result.data.names);
// Addresses for different networks
result.data.addresses.forEach((addr) => {
console.log(`${addr.network}: ${addr.address}`);
});
}Using RPC Client
import { rpc } from "@oviato/sdk";
const result = await rpc.resolveUsername({ username: "alice" });
if (result.result) {
const { names, addresses } = result.result;
// Find Bitcoin address
const btcAddress = addresses.find(a => a.network === "bitcoinmainnet");
if (btcAddress) {
console.log("Bitcoin address:", btcAddress.address);
console.log("Public key:", btcAddress.publicKey);
}
}Username Resolution Response
interface ResolveUsernameResponse {
names: Record<string, string>; // Name mappings
addresses: Array<{
network: string; // Network identifier
address: string; // Blockchain address
publicKey: string; // Public key
path: number; // Derivation path index
}>;
}Custom RPC Calls
Make arbitrary RPC calls for advanced use cases:
import { rpcRequest, rpc } from "@oviato/sdk";
// Using rpcRequest directly
const result = await rpcRequest("customMethod", {
param1: "value1",
param2: "value2",
});
// Using the client's call method
const result2 = await rpc.call("anotherMethod", {
data: "example",
});
// With type parameter
const typedResult = await rpcRequest<{ custom: string }>("getCustomData", {
id: "123",
});Request Headers
The RPC client automatically includes these headers:
| Header | Description |
|---|---|
X-Ovi-App-Id | Your application ID |
X-Ovi-Network | Current network (e.g., bitcoinmainnet) |
X-Ovi-Type | Network type (e.g., utxo, evm) |
X-Ovi-Environment | Environment (mainnet, testnet) |
X-Ovi-Version | SDK version |
X-Ovi-Origin | Request origin |
X-Ovi-Address | User's address (if authenticated) |
Authorization | Bearer token (if authenticated) |
RPC Error Handling
Handle RPC errors gracefully:
import { rpc } from "@oviato/sdk";
try {
const result = await rpc.getBalance({ address: "invalid" });
if (result.error) {
console.error("RPC Error:", result.error.message);
console.error("Error Code:", result.error.code);
}
} catch (error) {
console.error("Request failed:", error.message);
}Error Codes
Standard JSON-RPC 2.0 error codes plus custom Oviato codes:
| Code | Name | Description |
|---|---|---|
-32700 | Parse Error | Invalid JSON |
-32600 | Invalid Request | Malformed request |
-32601 | Method Not Found | Unknown RPC method |
-32602 | Invalid Params | Invalid method parameters |
-32603 | Internal Error | Server-side error |
-32001 | Unauthorized | Authentication required |
-32002 | Rate Limit | Too many requests |
-32003 | Insufficient Funds | Not enough balance |
Utility Functions
@oviato/sdk includes utility functions for common tasks.
Browser Detection
Detect browser environment and capabilities:
import {
isBrowser,
isIOS,
isSafari,
supportsWebAuthn,
getOrigin,
} from "@oviato/sdk/utils/browser";
// Check if running in browser (not SSR)
if (isBrowser()) {
console.log("Running in browser");
}
// Detect iOS devices
if (isIOS()) {
console.log("iOS device detected");
}
// Detect Safari browser
if (isSafari()) {
console.log("Safari browser");
}
// Check WebAuthn support
if (supportsWebAuthn()) {
console.log("WebAuthn is supported");
} else {
console.log("WebAuthn not available");
}
// Get current origin
const origin = getOrigin();
console.log("Origin:", origin); // e.g., "https://example.com"Validation Utilities
Validate URLs, colors, and domains:
import {
isValidUrl,
isValidImageUrl,
isValidHexColor,
isValidDomain,
normalizeDomain,
} from "@oviato/sdk/utils/validation";
// Validate any URL
isValidUrl("https://example.com"); // true
isValidUrl("not-a-url"); // false
// Validate image URLs
isValidImageUrl("https://example.com/logo.png"); // true
isValidImageUrl("https://example.com/file.pdf"); // false
// Validate hex colors
isValidHexColor("#ff0000"); // true
isValidHexColor("#f00"); // true
isValidHexColor("red"); // false
// Validate domains
isValidDomain("example.com"); // true
isValidDomain("sub.example.com"); // true
// Normalize domain (remove protocol and trailing slash)
normalizeDomain("https://example.com/"); // "example.com"Storage Utilities
SSR-safe localStorage and sessionStorage helpers:
import {
getItem,
setItem,
removeItem,
getJSON,
setJSON,
clear,
} from "@oviato/sdk/utils/storage";
// Basic string storage
setItem("myKey", "myValue");
const value = getItem("myKey");
removeItem("myKey");
// JSON storage (auto serialize/deserialize)
setJSON("user", { name: "Alice", id: 123 });
const user = getJSON("user");
console.log(user.name); // "Alice"
// Use session storage instead of local
setItem("tempKey", "tempValue", "session");
const temp = getItem("tempKey", "session");
// Clear all storage
clear(); // Clear localStorage
clear("session"); // Clear sessionStorageStorage Type Reference
type StorageType = "local" | "session";
// All functions accept optional storage type (default: "local")
getItem(key: string, type?: StorageType): string | null;
setItem(key: string, value: string, type?: StorageType): boolean;
removeItem(key: string, type?: StorageType): boolean;
getJSON<T>(key: string, type?: StorageType): T | null;
setJSON<T>(key: string, value: T, type?: StorageType): boolean;
clear(type?: StorageType): boolean;Complete RPC Example
Here's a comprehensive example using RPC and utilities:
import {
initialize,
authenticate,
getSession,
rpc,
resolveUsername,
} from "@oviato/sdk";
async function main() {
// Initialize SDK
await initialize({
appId: "your-app-id",
network: "bitcoinmainnet",
});
// Authenticate user
const authResult = await authenticate();
if (authResult.status !== "success") {
console.error("Authentication failed");
return;
}
const session = getSession();
console.log("Logged in as:", session.username);
// Get balance with detailed breakdown
const balanceResult = await rpc.getBalance({
address: session.address,
});
if (balanceResult.result) {
const { balance, chainType } = balanceResult.result;
console.log("\n--- Balance ---");
console.log("Total:", balance.formatted);
if (chainType === "utxo") {
console.log("Confirmed:", balance.confirmed.formatted);
console.log("Pending:", balance.unconfirmed.formatted);
}
}
// Get recent transactions
const historyResult = await rpc.getTransactionHistory({
address: session.address,
limit: 5,
});
if (historyResult.result) {
console.log("\n--- Recent Transactions ---");
historyResult.result.transactions.forEach((tx) => {
const type = tx.type === "received" ? "+" : "-";
const date = new Date(tx.timestamp * 1000).toLocaleDateString();
console.log(`${type}${tx.amount} sats | ${tx.confirmations} conf | ${date}`);
});
}
// Resolve a friend's username
const friendResult = await resolveUsername("bob");
if (friendResult.status === "success") {
console.log("\n--- Bob's Addresses ---");
friendResult.data.addresses.forEach((addr) => {
console.log(`${addr.network}: ${addr.address}`);
});
}
}
main();Security Considerations
Read-Only Access
The RPC client is read-only by design:
- Cannot sign transactions
- Cannot send funds
- Cannot modify wallet state
All signing operations require WebAuthn authentication through the SDK's wallet methods.
Token Security
- Auth tokens are automatically included when available
- Tokens expire and require re-authentication
- Tokens are domain-scoped (only work from registered origins)
Rate Limiting
- Requests are rate-limited per app/token/IP
- Handle
-32002error codes gracefully - Implement exponential backoff for retries
async function fetchWithRetry(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (error.message.includes("-32002") && i < maxRetries - 1) {
// Rate limited - wait before retry
await new Promise(r => setTimeout(r, 1000 * (i + 1)));
continue;
}
throw error;
}
}
}Next Steps
- Wallet Operations - Sign messages and send transactions
- Authentication - Learn about user authentication
- Installation - SDK setup guide