Wallet Operations
Sign messages, send transactions, and manage wallets with @oviato/sdk
@oviato/sdk provides comprehensive wallet functionality including message signing, transaction sending, network switching, and PSBT signing for Bitcoin.
Sign Messages
Sign arbitrary messages with the user's passkey:
import { signMessage } from "@oviato/sdk";
const result = await signMessage("Hello, Oviato!");
if (result.status === "success") {
console.log("Signature:", result.data.signature);
console.log("Message:", result.data.message);
console.log("Public Key:", result.data.publicKey);
}Use Cases
- Proving wallet ownership
- Authentication challenges
- Off-chain signatures
- Message verification
Verifying Signatures
The signature can be verified using the user's public key:
import { getSession } from "@oviato/sdk";
const session = getSession();
// Use session.publicKey to verify the signatureSend Transfers
Send cryptocurrency to an address:
import { sendTransfer } from "@oviato/sdk";
const result = await sendTransfer({
to: "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
amount: 10000, // Amount in smallest unit (satoshis, wei, lamports)
fee: 1000, // Fee in smallest unit
});
if (result.status === "success") {
console.log("Transaction ID:", result.data.txid);
console.log("Explorer URL:", result.data.explorerUrl);
}Amount Units
Different networks use different units:
| Network | Unit | Example |
|---|---|---|
| Bitcoin | Satoshis | 10000 = 0.0001 BTC |
| Ethereum | Wei | 1000000000000000000 = 1 ETH |
Transfer Options
type TransferOptions = {
to: string; // Recipient address
amount: number; // Amount in smallest unit
fee: number; // Fee in smallest unit
memo?: string; // Optional memo (not supported on all networks)
};Error Handling
const result = await sendTransfer({
to: "bc1q...",
amount: 10000,
fee: 1000,
});
if (result.status === "error") {
if (result.message.includes("insufficient funds")) {
console.error("Not enough balance");
} else if (result.message.includes("invalid address")) {
console.error("Invalid recipient address");
} else {
console.error("Transfer failed:", result.message);
}
}Switch Networks
Change the active blockchain network:
import { switchNetwork } from "@oviato/sdk";
const result = await switchNetwork("ethmainnet");
if (result.status === "success") {
console.log("Switched to:", result.data.network);
console.log("New address:", result.data.session.address);
console.log("New public key:", result.data.session.publicKey);
}Supported Networks
// Bitcoin
await switchNetwork("bitcoinmainnet");
await switchNetwork("bitcointestnet");
// Ethereum (soon)
await switchNetwork("ethmainnet");
await switchNetwork("ethsepolia");Getting Current Network
import { getSession } from "@oviato/sdk";
const session = getSession();
console.log("Current network:", session.network);
console.log("Network type:", session.networkType);
console.log("Environment:", session.networkEnvironment);Sign PSBT (Bitcoin Only)
Sign Partially Signed Bitcoin Transactions:
import { signPsbt } from "@oviato/sdk";
const result = await signPsbt({
psbt: "cHNidP8BAH0CAAAAA...", // Base64 encoded PSBT
signInputs: [0, 1], // Indices of inputs to sign
broadcast: true, // Auto-broadcast after signing
});
if (result.status === "success") {
console.log("Signed PSBT:", result.data.psbt);
if (result.data.txid) {
console.log("Broadcast TX ID:", result.data.txid);
}
}PSBT Options
type SignPsbtOptions = {
psbt: string; // Base64 encoded PSBT
signInputs: number[]; // Input indices to sign
broadcast?: boolean; // Auto-broadcast (default: false)
};Without Broadcasting
const result = await signPsbt({
psbt: "cHNidP8BAH0...",
signInputs: [0],
broadcast: false, // Don't broadcast, just sign
});
if (result.status === "success") {
const signedPsbt = result.data.psbt;
// Handle signed PSBT (e.g., send to another signer)
}RPC Client
Direct queries to blockchain nodes and Oviato services:
Get Balance
import { rpc } from "@oviato/sdk";
const result = await rpc.getBalance({
address: "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
});
if (result.result) {
console.log("Balance:", result.result.balance);
console.log("Confirmed:", result.result.confirmed);
console.log("Unconfirmed:", result.result.unconfirmed);
}Get Transaction History
const result = await rpc.getTransactionHistory({
address: "bc1q...",
limit: 10,
offset: 0,
});
if (result.result) {
result.result.transactions.forEach((tx) => {
console.log("TX ID:", tx.txid);
console.log("Amount:", tx.amount);
console.log("Time:", tx.timestamp);
});
}Resolve OVI.ID Username
import { resolveOviId } from "@oviato/sdk";
const addresses = await resolveOviId("alice");
if (addresses) {
console.log("Bitcoin:", addresses.bitcoin);
console.log("Ethereum:", addresses.ethereum);
}Custom RPC Calls
import { rpcRequest } from "@oviato/sdk";
const result = await rpcRequest("customMethod", {
param1: "value1",
param2: "value2",
});Complete Wallet Example
Here's a complete example combining all wallet operations:
import {
initialize,
authenticate,
getSession,
signMessage,
sendTransfer,
switchNetwork,
signPsbt,
rpc,
resolveOviId,
} from "@oviato/sdk";
// Initialize
await initialize({
appId: "your-app-id",
network: "bitcoinmainnet",
});
// Authenticate
const authResult = await authenticate();
if (authResult.status === "success") {
const session = getSession();
console.log("Connected as:", session.username);
console.log("Address:", session.address);
// Get balance
const balanceResult = await rpc.getBalance({
address: session.address,
});
console.log("Balance:", balanceResult.result?.balance);
// Sign message
const signResult = await signMessage("Prove ownership");
console.log("Signature:", signResult.data?.signature);
// Send transfer
const transferResult = await sendTransfer({
to: "bc1q...",
amount: 10000,
fee: 1000,
});
console.log("TX ID:", transferResult.data?.txid);
// Switch to Ethereum
const switchResult = await switchNetwork("ethmainnet");
console.log("New network:", switchResult.data?.network);
// Resolve username
const addresses = await resolveOviId("bob");
console.log("Bob's addresses:", addresses);
}Framework-Specific Examples
Vue 3 Wallet Component
<script setup>
import { ref } from "vue";
import { getSession, signMessage, sendTransfer, rpc } from "@oviato/sdk";
const session = ref(getSession());
const balance = ref(null);
const isLoading = ref(false);
const loadBalance = async () => {
if (!session.value) return;
const result = await rpc.getBalance({
address: session.value.address,
});
if (result.result) {
balance.value = result.result.balance;
}
};
const handleSign = async () => {
isLoading.value = true;
const result = await signMessage("Hello from Vue!");
if (result.status === "success") {
alert("Signature: " + result.data.signature);
}
isLoading.value = false;
};
const handleTransfer = async () => {
isLoading.value = true;
const result = await sendTransfer({
to: "bc1q...",
amount: 10000,
fee: 1000,
});
if (result.status === "success") {
alert("TX ID: " + result.data.txid);
await loadBalance(); // Refresh balance
}
isLoading.value = false;
};
// Load balance on mount
loadBalance();
</script>
<template>
<div v-if="session">
<h2>Wallet</h2>
<p>Address: {{ session.address }}</p>
<p>Balance: {{ balance }} satoshis</p>
<button @click="handleSign" :disabled="isLoading">Sign Message</button>
<button @click="handleTransfer" :disabled="isLoading">Send Transfer</button>
</div>
</template>Svelte Wallet Component
<script>
import { onMount } from 'svelte';
import { getSession, signMessage, sendTransfer, rpc } from '@oviato/sdk';
let session = getSession();
let balance = null;
let isLoading = false;
onMount(async () => {
if (session) {
await loadBalance();
}
});
const loadBalance = async () => {
const result = await rpc.getBalance({
address: session.address,
});
if (result.result) {
balance = result.result.balance;
}
};
const handleSign = async () => {
isLoading = true;
const result = await signMessage('Hello from Svelte!');
if (result.status === 'success') {
alert('Signature: ' + result.data.signature);
}
isLoading = false;
};
const handleTransfer = async () => {
isLoading = true;
const result = await sendTransfer({
to: 'bc1q...',
amount: 10000,
fee: 1000,
});
if (result.status === 'success') {
alert('TX ID: ' + result.data.txid);
await loadBalance();
}
isLoading = false;
};
</script>
{#if session}
<div>
<h2>Wallet</h2>
<p>Address: {session.address}</p>
<p>Balance: {balance} satoshis</p>
<button on:click={handleSign} disabled={isLoading}>
Sign Message
</button>
<button on:click={handleTransfer} disabled={isLoading}>
Send Transfer
</button>
</div>
{/if}Security Considerations
1. Validate Addresses
Always validate recipient addresses before sending:
function isValidBitcoinAddress(address) {
return /^(bc1|[13])[a-zA-HJ-NP-Z0-9]{25,62}$/.test(address);
}
const recipientAddress = "bc1q...";
if (isValidBitcoinAddress(recipientAddress)) {
await sendTransfer({
to: recipientAddress,
amount: 10000,
fee: 1000,
});
} else {
console.error("Invalid address");
}2. Confirm Before Sending
Always show confirmation before sending transactions:
async function confirmAndSend(to, amount) {
const confirmed = confirm(`Send ${amount} satoshis to ${to}?`);
if (confirmed) {
const result = await sendTransfer({ to, amount, fee: 1000 });
return result;
}
}3. Handle Errors Gracefully
try {
const result = await sendTransfer({
to: recipientAddress,
amount: amount,
fee: fee,
});
if (result.status === "error") {
throw new Error(result.message);
}
console.log("Success:", result.data.txid);
} catch (error) {
console.error("Transfer failed:", error.message);
alert("Transfer failed: " + error.message);
}Troubleshooting
"Insufficient funds"
User doesn't have enough balance. Check balance before sending:
const balanceResult = await rpc.getBalance({ address: session.address });
const balance = balanceResult.result?.balance || 0;
if (balance < amount + fee) {
alert("Insufficient funds");
}"Invalid address"
Recipient address is malformed. Validate addresses before sending.
"User rejected"
User cancelled the transaction in the signing modal.
"Network mismatch"
Trying to use an address from a different network. Switch networks first.
Next Steps
- Authentication - Learn about authentication
- Installation & Setup - Back to installation guide