otx-btc-wallet-react
v0.1.0
Published
React hooks and components for otx-btc-wallet
Readme
otx-btc-wallet-react
React provider and hooks for the otx-btc-wallet library. Provides a complete set of hooks for connecting wallets, signing messages, signing PSBTs, and sending Bitcoin.
Installation
pnpm add otx-btc-wallet-react otx-btc-wallet-core otx-btc-wallet-connectorsQuick Start
1. Setup Provider
Wrap your app with BtcWalletProvider and pass a config created with createConfig():
import { createConfig } from 'otx-btc-wallet-core';
import { BtcWalletProvider } from 'otx-btc-wallet-react';
import { UnisatConnector, XverseConnector } from 'otx-btc-wallet-connectors';
const config = createConfig({
connectors: [
new UnisatConnector(),
new XverseConnector(),
],
});
function App() {
return (
<BtcWalletProvider config={config}>
<YourApp />
</BtcWalletProvider>
);
}2. Use Hooks
import { useAccount, useConnect, useDisconnect } from 'otx-btc-wallet-react';
function WalletButton() {
const { address, isConnected } = useAccount();
const { connect, connectors, isLoading } = useConnect();
const { disconnect } = useDisconnect();
if (isConnected) {
return (
<div>
<p>{address}</p>
<button onClick={() => disconnect()}>Disconnect</button>
</div>
);
}
return (
<div>
{connectors.map((connector) => (
<button
key={connector.id}
onClick={() => connect({ connector })}
disabled={!connector.ready || isLoading}
>
{connector.name}
{!connector.ready && ' (not installed)'}
</button>
))}
</div>
);
}Provider
BtcWalletProvider
Required wrapper component. Must be placed above any component that uses hooks.
import { BtcWalletProvider } from 'otx-btc-wallet-react';
<BtcWalletProvider config={config}>
{children}
</BtcWalletProvider>Props:
| Prop | Type | Description |
|------|------|-------------|
| config | ResolvedConfig | Config object from createConfig() |
| children | ReactNode | Child components |
Behavior:
- Provides the config context to all child hooks
- Handles auto-reconnect on mount if
autoConnectis enabled and a previous wallet session exists
Hooks
useConfig
Access the config object directly. Useful for advanced store operations.
import { useConfig } from 'otx-btc-wallet-react';
function Debug() {
const config = useConfig();
const state = config.store.getState();
return <pre>{JSON.stringify(state, null, 2)}</pre>;
}Returns: ResolvedConfig - The full config object including the Zustand store.
useAccount
Get the connected account information. Reactively updates when the account changes.
import { useAccount } from 'otx-btc-wallet-react';
function Account() {
const {
address, // string | undefined
publicKey, // string | undefined
addressType, // AddressType | undefined ('legacy' | 'nested-segwit' | 'segwit' | 'taproot')
account, // WalletAccount | undefined
connector, // BitcoinConnector | undefined
status, // ConnectionStatus
isConnected, // boolean
isConnecting, // boolean
isDisconnected, // boolean
isReconnecting, // boolean
} = useAccount();
if (!isConnected) return <p>Not connected</p>;
return (
<div>
<p>Address: {address}</p>
<p>Type: {addressType}</p>
<p>Public Key: {publicKey}</p>
</div>
);
}Return Type: UseAccountReturn
| Field | Type | Description |
|-------|------|-------------|
| address | string \| undefined | Connected Bitcoin address |
| publicKey | string \| undefined | Hex-encoded public key |
| addressType | AddressType \| undefined | Address type |
| account | WalletAccount \| undefined | Full account object |
| connector | BitcoinConnector \| undefined | Active connector instance |
| status | ConnectionStatus | 'connected' | 'connecting' | 'disconnected' | 'reconnecting' |
| isConnected | boolean | true when status is 'connected' |
| isConnecting | boolean | true when status is 'connecting' |
| isDisconnected | boolean | true when status is 'disconnected' |
| isReconnecting | boolean | true when status is 'reconnecting' |
useConnect
Connect to a wallet connector.
import { useConnect } from 'otx-btc-wallet-react';
function ConnectWallet() {
const {
connect, // (args: ConnectArgs) => void
connectAsync, // (args: ConnectArgs) => Promise<WalletAccount>
connectors, // BitcoinConnector[]
isLoading, // boolean
isSuccess, // boolean
isError, // boolean
error, // Error | null
reset, // () => void
} = useConnect();
return (
<div>
{connectors.map((connector) => (
<button
key={connector.id}
onClick={() => connect({ connector })}
disabled={!connector.ready || isLoading}
>
{connector.name}
{!connector.ready && ' (not installed)'}
</button>
))}
{isError && <p>Error: {error?.message}</p>}
</div>
);
}Connect Args:
type ConnectArgs = {
connector: BitcoinConnector; // The connector to use
network?: BitcoinNetwork; // Optional network override
};Return Type: UseConnectReturn
| Field | Type | Description |
|-------|------|-------------|
| connect | (args: ConnectArgs) => void | Fire-and-forget connect |
| connectAsync | (args: ConnectArgs) => Promise<WalletAccount> | Connect and await result |
| connectors | BitcoinConnector[] | All registered connectors |
| isLoading | boolean | Connection in progress |
| isSuccess | boolean | Connection succeeded |
| isError | boolean | Connection failed |
| error | Error \| null | Error details |
| reset | () => void | Reset loading/error/success state |
Async Usage:
const handleConnect = async () => {
try {
const account = await connectAsync({ connector, network: 'testnet' });
console.log('Connected:', account.address);
} catch (err) {
console.error('Connection failed:', err);
}
};useDisconnect
Disconnect the current wallet.
import { useDisconnect } from 'otx-btc-wallet-react';
function DisconnectButton() {
const {
disconnect, // () => void
disconnectAsync, // () => Promise<void>
isLoading, // boolean
isSuccess, // boolean
isError, // boolean
error, // Error | null
} = useDisconnect();
return (
<button onClick={() => disconnect()} disabled={isLoading}>
Disconnect
</button>
);
}Return Type: UseDisconnectReturn
| Field | Type | Description |
|-------|------|-------------|
| disconnect | () => void | Fire-and-forget disconnect |
| disconnectAsync | () => Promise<void> | Disconnect and await completion |
| isLoading | boolean | Disconnect in progress |
| isSuccess | boolean | Disconnect succeeded |
| isError | boolean | Disconnect failed |
| error | Error \| null | Error details |
useNetwork
Get the current Bitcoin network.
import { useNetwork } from 'otx-btc-wallet-react';
function NetworkInfo() {
const { network } = useNetwork();
// 'mainnet' | 'testnet' | 'testnet4' | 'signet'
return <p>Network: {network}</p>;
}Return Type: UseNetworkReturn
| Field | Type | Description |
|-------|------|-------------|
| network | BitcoinNetwork | Current Bitcoin network |
useSignMessage
Sign a text message with the connected wallet.
import { useSignMessage } from 'otx-btc-wallet-react';
function SignMessage() {
const {
signMessage, // (args: { message: string }) => void
signMessageAsync, // (args: { message: string }) => Promise<string>
data, // string | undefined (signature)
isLoading, // boolean
isSuccess, // boolean
isError, // boolean
error, // Error | null
reset, // () => void
} = useSignMessage();
return (
<div>
<button
onClick={() => signMessage({ message: 'Hello Bitcoin!' })}
disabled={isLoading}
>
Sign Message
</button>
{data && <p>Signature: {data}</p>}
{isError && <p>Error: {error?.message}</p>}
</div>
);
}Return Type: UseSignMessageReturn
| Field | Type | Description |
|-------|------|-------------|
| signMessage | (args: { message: string }) => void | Fire-and-forget sign |
| signMessageAsync | (args: { message: string }) => Promise<string> | Sign and await signature |
| data | string \| undefined | The resulting signature |
| isLoading | boolean | Signing in progress |
| isSuccess | boolean | Signing succeeded |
| isError | boolean | Signing failed |
| error | Error \| null | Error details |
| reset | () => void | Reset state |
useSignPsbt
Sign a single PSBT (Partially Signed Bitcoin Transaction).
import { useSignPsbt } from 'otx-btc-wallet-react';
function SignTransaction() {
const {
signPsbt, // (args) => void
signPsbtAsync, // (args) => Promise<string>
data, // string | undefined (signed PSBT hex)
isLoading,
isSuccess,
isError,
error,
reset,
} = useSignPsbt();
const handleSign = () => {
signPsbt({
psbt: '70736274ff01...', // PSBT hex string
options: {
autoFinalize: true,
broadcast: false,
toSignInputs: [
{ index: 0, address: 'bc1q...' },
],
},
});
};
return (
<div>
<button onClick={handleSign} disabled={isLoading}>
Sign PSBT
</button>
{data && <p>Signed: {data}</p>}
</div>
);
}Return Type: UseSignPsbtReturn
| Field | Type | Description |
|-------|------|-------------|
| signPsbt | (args: { psbt: string; options?: SignPsbtOptions }) => void | Fire-and-forget sign |
| signPsbtAsync | (args: { psbt: string; options?: SignPsbtOptions }) => Promise<string> | Sign and await result |
| data | string \| undefined | Signed PSBT hex |
| isLoading | boolean | Signing in progress |
| isSuccess | boolean | Signing succeeded |
| isError | boolean | Signing failed |
| error | Error \| null | Error details |
| reset | () => void | Reset state |
useSignPsbts
Sign multiple PSBTs in a batch. Not all wallets support this (check isSupported).
import { useSignPsbts } from 'otx-btc-wallet-react';
function BatchSign() {
const {
signPsbts, // (args) => void
signPsbtsAsync, // (args) => Promise<string[]>
data, // string[] | undefined (signed PSBT hex array)
isLoading,
isSuccess,
isError,
error,
isSupported, // boolean - does the wallet support batch signing?
reset,
} = useSignPsbts();
if (!isSupported) {
return <p>This wallet does not support batch signing.</p>;
}
return (
<button
onClick={() => signPsbts({
psbts: [psbt1Hex, psbt2Hex],
options: { autoFinalize: true },
})}
disabled={isLoading}
>
Sign {2} PSBTs
</button>
);
}Return Type: UseSignPsbtsReturn
| Field | Type | Description |
|-------|------|-------------|
| signPsbts | (args: { psbts: string[]; options?: SignPsbtOptions }) => void | Fire-and-forget batch sign |
| signPsbtsAsync | (args: { psbts: string[]; options?: SignPsbtOptions }) => Promise<string[]> | Sign and await results |
| data | string[] \| undefined | Array of signed PSBT hex strings |
| isLoading | boolean | Signing in progress |
| isSuccess | boolean | Signing succeeded |
| isError | boolean | Signing failed |
| error | Error \| null | Error details |
| isSupported | boolean | true if the connected wallet has signPsbts method |
| reset | () => void | Reset state |
Supported wallets: Currently only Unisat supports batch PSBT signing.
useSendTransaction
Send Bitcoin to an address. Amount is specified in satoshis.
import { useSendTransaction } from 'otx-btc-wallet-react';
function SendBitcoin() {
const {
sendTransaction, // (args: { to: string; amount: number }) => void
sendTransactionAsync, // (args: { to: string; amount: number }) => Promise<string>
data, // string | undefined (transaction ID)
isLoading,
isSuccess,
isError,
error,
reset,
} = useSendTransaction();
return (
<div>
<button
onClick={() => sendTransaction({
to: 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh',
amount: 10000, // 10,000 satoshis
})}
disabled={isLoading}
>
Send 10,000 sats
</button>
{data && <p>TX ID: {data}</p>}
{isError && <p>Error: {error?.message}</p>}
</div>
);
}Return Type: UseSendTransactionReturn
| Field | Type | Description |
|-------|------|-------------|
| sendTransaction | (args: { to: string; amount: number }) => void | Fire-and-forget send |
| sendTransactionAsync | (args: { to: string; amount: number }) => Promise<string> | Send and await txid |
| data | string \| undefined | Transaction ID |
| isLoading | boolean | Transaction in progress |
| isSuccess | boolean | Transaction succeeded |
| isError | boolean | Transaction failed |
| error | Error \| null | Error details |
| reset | () => void | Reset state |
useMultiAddress
Manage multiple addresses from a single wallet. Useful for wallets like Xverse that provide separate payment and ordinals addresses.
import { useMultiAddress } from 'otx-btc-wallet-react';
function MultiAddress() {
const {
addresses, // WalletAccount[] - all addresses
paymentAddress, // WalletAccount | null - segwit/nested-segwit address
ordinalsAddress, // WalletAccount | null - taproot address
primaryAddress, // WalletAccount | null - currently selected
setPrimaryAddress, // (address: WalletAccount) => void
isLoading, // boolean
refresh, // () => Promise<void> - refetch from wallet
} = useMultiAddress();
return (
<div>
{paymentAddress && (
<p>Payment: {paymentAddress.address} ({paymentAddress.type})</p>
)}
{ordinalsAddress && (
<p>Ordinals: {ordinalsAddress.address} ({ordinalsAddress.type})</p>
)}
<h3>All Addresses:</h3>
{addresses.map((addr) => (
<button
key={addr.address}
onClick={() => setPrimaryAddress(addr)}
style={{ fontWeight: addr === primaryAddress ? 'bold' : 'normal' }}
>
{addr.address} ({addr.type})
</button>
))}
<button onClick={refresh}>Refresh</button>
</div>
);
}Return Type: UseMultiAddressReturn
| Field | Type | Description |
|-------|------|-------------|
| addresses | WalletAccount[] | All wallet addresses |
| paymentAddress | WalletAccount \| null | First segwit or nested-segwit address (for payments) |
| ordinalsAddress | WalletAccount \| null | First taproot address (for ordinals/inscriptions) |
| primaryAddress | WalletAccount \| null | Currently selected address |
| setPrimaryAddress | (address: WalletAccount) => void | Change the primary address |
| isLoading | boolean | Fetching addresses |
| refresh | () => Promise<void> | Re-fetch addresses from the wallet |
Address Selection Logic:
paymentAddress: First address with type'segwit'or'nested-segwit', falls back to the first addressordinalsAddress: First address with type'taproot'
Hook Patterns
All action hooks follow a consistent pattern:
Fire-and-Forget vs Async
Every action hook provides two versions:
action()- Fire-and-forget. Updates internal state (isLoading,isSuccess,isError,data).actionAsync()- Returns a Promise. Use withtry/catchfor more control.
// Fire-and-forget (state updates automatically)
signMessage({ message: 'Hello' });
// Async (handle result directly)
try {
const signature = await signMessageAsync({ message: 'Hello' });
doSomethingWith(signature);
} catch (err) {
handleError(err);
}Reset State
All action hooks provide a reset() method to clear data, error, isSuccess, and isError back to their initial values.
const { signMessage, data, reset } = useSignMessage();
// After showing result, clear it
<button onClick={reset}>Clear</button>Complete Example
import { createConfig } from 'otx-btc-wallet-core';
import {
BtcWalletProvider,
useAccount,
useConnect,
useDisconnect,
useSignMessage,
useSendTransaction,
useNetwork,
} from 'otx-btc-wallet-react';
import { UnisatConnector, OKXConnector } from 'otx-btc-wallet-connectors';
const config = createConfig({
connectors: [new UnisatConnector(), new OKXConnector()],
});
function Wallet() {
const { address, isConnected, addressType } = useAccount();
const { connect, connectors, isLoading: connecting } = useConnect();
const { disconnect } = useDisconnect();
const { signMessage, data: signature } = useSignMessage();
const { sendTransaction, data: txid } = useSendTransaction();
const { network } = useNetwork();
if (!isConnected) {
return (
<div>
<h2>Connect Wallet</h2>
{connectors.map((c) => (
<button
key={c.id}
onClick={() => connect({ connector: c })}
disabled={!c.ready || connecting}
>
{c.name} {!c.ready ? '(not installed)' : ''}
</button>
))}
</div>
);
}
return (
<div>
<p>Address: {address}</p>
<p>Type: {addressType}</p>
<p>Network: {network}</p>
<button onClick={() => signMessage({ message: 'Hello!' })}>
Sign Message
</button>
{signature && <p>Signature: {signature}</p>}
<button onClick={() => sendTransaction({ to: 'bc1q...', amount: 10000 })}>
Send 10,000 sats
</button>
{txid && <p>TX: {txid}</p>}
<button onClick={() => disconnect()}>Disconnect</button>
</div>
);
}
function App() {
return (
<BtcWalletProvider config={config}>
<Wallet />
</BtcWalletProvider>
);
}Full Exports
// Provider
export { BtcWalletProvider } from './provider';
export type { BtcWalletProviderProps } from './provider';
// Context
export { useConfig, BtcWalletContext } from './context';
// Hooks
export { useAccount } from './hooks/useAccount';
export { useConnect } from './hooks/useConnect';
export { useDisconnect } from './hooks/useDisconnect';
export { useNetwork } from './hooks/useNetwork';
export { useSignMessage } from './hooks/useSignMessage';
export { useSignPsbt } from './hooks/useSignPsbt';
export { useSignPsbts } from './hooks/useSignPsbts';
export { useSendTransaction } from './hooks/useSendTransaction';
export { useMultiAddress } from './hooks/useMultiAddress';
// Return types
export type {
UseAccountReturn,
UseConnectReturn,
ConnectArgs,
UseDisconnectReturn,
UseNetworkReturn,
UseSignMessageReturn,
UseSignPsbtReturn,
UseSignPsbtsReturn,
UseSendTransactionReturn,
UseMultiAddressReturn,
} from './hooks';License
MIT
