Command Palette
Search for a command to run...
Getting Started
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.
Install
bun add @1sat/connectQuick Start
import { 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)Detecting the Provider
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
}Establishing a Connection
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.
connect()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
disconnect()Promise<void>await wallet.disconnect()
// Session cleared — user must re-approve to connect againCheck Connection
isConnected()booleanif (wallet.isConnected()) {
// Wallet is connected, safe to call methods
}Getting Addresses & Keys
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 encryptionGet Balance
getBalance()Promise<BalanceResult>const { satoshis, usd } = await wallet.getBalance()
console.log(`${satoshis} sats ($${usd?.toFixed(2)} USD)`)Sign Transaction
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)Sign Message
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 verificationOrdinals
Inscribe
inscribe(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',
},
})Get Ordinals
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 })Transfer Ordinals
sendOrdinals(request: SendOrdinalsRequest)Promise<SendResult>const { txid } = await wallet.sendOrdinals({
outpoints: ['abc123_0', 'def456_0'], // Ordinal outpoints to send
destinationAddress: '1RecipientAddress...',
})Tokens
Transfer BSV20 (tick-based) and BSV21 (contract-based) fungible tokens.
Get Tokens
getTokens(options?: ListOptions)Promise<TokenOutput[]>const tokens = await wallet.getTokens()
for (const tok of tokens) {
console.log(tok.symbol, tok.amount, tok.tokenId)
}Transfer Tokens
transferToken(request: TransferTokenRequest)Promise<SendResult>const { txid } = await wallet.transferToken({
tokenId: 'token-origin-txid_0',
amount: '100',
destinationAddress: '1RecipientAddress...',
})Marketplace Listings
Create, purchase, and cancel trustless OrdLock marketplace listings.
Create Listing
createListing(request: CreateListingRequest)Promise<ListingResult>const { txid, listingOutpoints } = await wallet.createListing({
outpoints: ['abc123_0'], // Ordinals to list
priceSatoshis: 100000, // Price in satoshis
})Purchase Listing
purchaseListing(request: PurchaseListingRequest)Promise<SendResult>const { txid } = await wallet.purchaseListing({
listingOutpoint: 'listing-txid_0',
})Cancel Listing
cancelListing(request: CancelListingRequest)Promise<SendResult>const { txid } = await wallet.cancelListing({
listingOutpoints: ['listing-txid_0'],
})Events
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)Error Handling
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
}
}Types Reference
All types are exported from @1sat/connect for TypeScript projects.
interface OneSatConfig {
appName?: string // Shown in approval popup
popupUrl?: string // Default: 'https://1sat.market'
timeout?: number // Default: 300000 (5 min)
network?: 'main' | 'test'
}
interface ConnectResult {
paymentAddress: string
ordinalAddress: string
identityPubKey: string
}
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'Global Declaration
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
}
}