Command Palette
Search for a command to run...
1Sat Wallet exposes a provider API that lets any web application request wallet operations from the user. The wallet opens as a popup at 1sat.market where the user approves or rejects each request. No browser extension is required.
bun add @1sat/connectimport { createOneSat } from '@1sat/connect'
const wallet = createOneSat({
appName: 'My dApp',
})
// Connect — opens wallet popup for user approval
const { paymentAddress, ordinalAddress } = await wallet.connect()
console.log('Connected:', paymentAddress)
// Get balance
const { satoshis } = await wallet.getBalance()
console.log('Balance:', satoshis, 'sats')
// Inscribe data
const { txid } = await wallet.inscribe({
dataB64: btoa('Hello, Ordinals!'),
contentType: 'text/plain',
})
console.log('Inscribed:', txid)The createOneSat() factory handles provider detection automatically. It checks for a browser extension first (window.onesat), then falls back to the popup-based provider.
import { createOneSat, isOneSatInjected, waitForOneSat } from '@1sat/connect'
// Recommended: auto-detects extension or uses popup
const wallet = createOneSat({ appName: 'My dApp' })
// Manual detection
if (isOneSatInjected()) {
// Browser extension is installed
const wallet = window.onesat
} else {
// No extension — popup provider will be used
}
// Wait for extension injection (useful on page load)
try {
const wallet = await waitForOneSat(3000) // 3s timeout
} catch {
// Extension not installed
}Calling connect() opens the wallet popup where the user can approve the connection. On approval, you receive the user's payment address, ordinal address, and identity public key. You can optionally pass a challenge to get a BSM signature in the same popup — see Challenge Signing below.
connect(params?: ConnectParams)Promise<ConnectResult>try {
const { paymentAddress, ordinalAddress, identityPubKey } = await wallet.connect()
console.log('Payment:', paymentAddress)
console.log('Ordinal:', ordinalAddress)
console.log('Identity:', identityPubKey)
} catch (err) {
if (err instanceof UserRejectedError) {
console.log('User closed the popup')
}
}disconnect()Promise<void>await wallet.disconnect()
// Session cleared — user must re-approve to connect againisConnected()booleanif (wallet.isConnected()) {
// Wallet is connected, safe to call methods
}When your app needs to verify wallet ownership (e.g. during authentication), pass a challenge string to connect(). The wallet signs the challenge with BSM during the same popup interaction, so the user only sees one popup instead of two. This avoids browsers blocking a second window.open call.
import { createOneSat, PopupBlockedError } from '@1sat/connect'
const wallet = createOneSat({ appName: 'My dApp' })
// Build a challenge for your server to verify
const timestamp = new Date().toISOString()
const challenge = `/api/auth|${timestamp}`
// Single popup: connect + sign in one user gesture
const result = await wallet.connect({ challenge })
console.log('Payment:', result.paymentAddress)
console.log('Ordinal:', result.ordinalAddress)
// signedMessage is present when a challenge was provided
if (result.signedMessage) {
console.log('Signature:', result.signedMessage.signature)
console.log('Address:', result.signedMessage.address)
console.log('Message:', result.signedMessage.message)
// Send to your server for BSM verification
await fetch('/api/auth', {
method: 'POST',
body: JSON.stringify({
address: result.signedMessage.address,
signature: result.signedMessage.signature,
message: challenge,
}),
})
}If no challenge is provided, signedMessage will be undefined and the popup behaves exactly as before — fully backward compatible.
After connecting, retrieve the user's addresses and identity key at any time.
getAddresses(){ paymentAddress, ordinalAddress } | nullgetIdentityPubKey()string | nullconst addrs = wallet.getAddresses()
if (addrs) {
console.log('Payment:', addrs.paymentAddress)
console.log('Ordinal:', addrs.ordinalAddress)
}
const identityPubKey = wallet.getIdentityPubKey()
// Use for identity verification, BAP, or encryptiongetBalance()Promise<BalanceResult>const { satoshis, usd } = await wallet.getBalance()
console.log(`${satoshis} sats ($${usd?.toFixed(2)} USD)`)Submit a raw transaction hex for the user to review and sign. The wallet shows a summary of inputs and outputs before the user approves.
signTransaction(request: SignTransactionRequest)Promise<SignTransactionResult>const { rawtx, txid } = await wallet.signTransaction({
rawtx: '0100000001...', // Unsigned transaction hex
description: 'Send 0.01 BSV', // Shown to user in approval popup
})
console.log('Signed TXID:', txid)Request a Bitcoin Signed Message (BSM) from the user. Useful for authentication, attestation, and identity verification.
signMessage(message: string)Promise<SignMessageResult>const { message, signature, address } = await wallet.signMessage('Authenticate to My dApp')
console.log('Signature:', signature)
console.log('Signed by:', address)
// Verify server-side with @bsv/sdk BSM verificationinscribe(request: InscribeRequest)Promise<InscribeResult>// Inscribe text
const { txid, origin } = await wallet.inscribe({
dataB64: btoa('Hello, Ordinals!'),
contentType: 'text/plain',
})
// Inscribe an image
const file = document.querySelector('input[type=file]').files[0]
const reader = new FileReader()
reader.onload = async () => {
const dataB64 = reader.result.split(',')[1]
const { txid } = await wallet.inscribe({
dataB64,
contentType: file.type,
})
}
reader.readAsDataURL(file)
// Inscribe with MAP metadata
const { txid } = await wallet.inscribe({
dataB64: btoa('Content here'),
contentType: 'text/plain',
metaData: {
app: 'my-app',
type: 'post',
context: 'channel-id',
},
})getOrdinals(options?: ListOptions)Promise<OrdinalOutput[]>// Get first 20 ordinals
const ordinals = await wallet.getOrdinals({ limit: 20 })
for (const ord of ordinals) {
console.log(ord.outpoint, ord.contentType)
}
// Paginate
const page2 = await wallet.getOrdinals({ limit: 20, offset: 20 })sendOrdinals(request: SendOrdinalsRequest)Promise<SendResult>const { txid } = await wallet.sendOrdinals({
outpoints: ['abc123_0', 'def456_0'], // Ordinal outpoints to send
destinationAddress: '1RecipientAddress...',
})Transfer BSV20 (tick-based) and BSV21 (contract-based) fungible tokens.
getTokens(options?: ListOptions)Promise<TokenOutput[]>const tokens = await wallet.getTokens()
for (const tok of tokens) {
console.log(tok.symbol, tok.amount, tok.tokenId)
}transferToken(request: TransferTokenRequest)Promise<SendResult>const { txid } = await wallet.transferToken({
tokenId: 'token-origin-txid_0',
amount: '100',
destinationAddress: '1RecipientAddress...',
})Create, purchase, and cancel trustless OrdLock marketplace listings.
createListing(request: CreateListingRequest)Promise<ListingResult>const { txid, listingOutpoints } = await wallet.createListing({
outpoints: ['abc123_0'], // Ordinals to list
priceSatoshis: 100000, // Price in satoshis
})purchaseListing(request: PurchaseListingRequest)Promise<SendResult>const { txid } = await wallet.purchaseListing({
listingOutpoint: 'listing-txid_0',
})cancelListing(request: CancelListingRequest)Promise<SendResult>const { txid } = await wallet.cancelListing({
listingOutpoints: ['listing-txid_0'],
})Listen for wallet state changes. Always handle these events to keep your app in sync with the wallet.
ConnectResult// Subscribe to events
wallet.on('connect', ({ paymentAddress, ordinalAddress }) => {
console.log('Connected:', paymentAddress)
})
wallet.on('disconnect', () => {
console.log('Disconnected')
// Clear local state, show connect button
})
wallet.on('accountChange', ({ paymentAddress, ordinalAddress }) => {
console.log('Account switched:', paymentAddress)
// Refresh balances, ordinals, etc.
})
// Unsubscribe
const handler = (data) => console.log(data)
wallet.on('connect', handler)
wallet.off('connect', handler)All methods return promises. Errors use typed classes with numeric codes for reliable programmatic handling.
import {
UserRejectedError,
TimeoutError,
InsufficientFundsError,
WalletLockedError,
WalletNotConnectedError,
PopupBlockedError,
} from '@1sat/connect'
try {
await wallet.connect()
} catch (err) {
if (err instanceof UserRejectedError) {
// User closed popup or clicked reject
} else if (err instanceof TimeoutError) {
// Request timed out (default 5 minutes)
} else if (err instanceof PopupBlockedError) {
// Browser blocked the popup — prompt user to allow popups
} else if (err instanceof WalletLockedError) {
// Wallet is locked — user needs to unlock
} else if (err instanceof WalletNotConnectedError) {
// Called a method before connecting
} else if (err instanceof InsufficientFundsError) {
// Not enough BSV for the operation
}
}All types are exported from @1sat/connect for TypeScript projects.
interface OneSatConfig {
appName?: string // Shown in approval popup
popupUrl?: string // Default: 'https://www.1satwallet.com'
timeout?: number // Default: 300000 (5 min)
network?: 'main' | 'test'
}
interface ConnectParams {
challenge?: string // Optional BSM challenge signed in same popup
}
interface ConnectResult {
paymentAddress: string
ordinalAddress: string
identityPubKey: string
signedMessage?: SignMessageResult // Present when challenge was provided
}
interface BalanceResult {
satoshis: number
usd?: number
}
interface SignTransactionRequest {
rawtx: string
description?: string
}
interface SignTransactionResult {
rawtx: string
txid: string
}
interface SignMessageResult {
message: string
signature: string
address: string
}
interface InscribeRequest {
dataB64: string
contentType: string
destinationAddress?: string
metaData?: Record<string, string>
}
interface InscribeResult {
txid: string
origin: string
}
interface SendOrdinalsRequest {
outpoints: string[]
destinationAddress: string
}
interface TransferTokenRequest {
tokenId: string
amount: string
destinationAddress: string
}
interface CreateListingRequest {
outpoints: string[]
priceSatoshis: number
}
interface ListingResult {
txid: string
listingOutpoints: string[]
}
interface PurchaseListingRequest {
listingOutpoint: string
}
interface CancelListingRequest {
listingOutpoints: string[]
}
interface SendResult {
txid: string
}
interface OrdinalOutput {
outpoint: string
satoshis: number
origin?: string
contentType?: string
}
interface TokenOutput {
outpoint: string
satoshis: number
tokenId: string
amount: string
symbol?: string
}
interface Utxo {
txid: string
vout: number
satoshis: number
script: string
}
interface ListOptions {
limit?: number
offset?: number
}
type OneSatEvent = 'connect' | 'disconnect' | 'accountChange'If you access window.onesat directly (e.g. with a browser extension), add this declaration to your project:
import type { OneSatProvider } from '@1sat/connect'
declare global {
interface Window {
onesat?: OneSatProvider
}
}