@usebutr/bitcoin 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -46,69 +46,23 @@ type BitcoinSendTransferFeature = {
46
46
  //#endregion
47
47
  //#region src/wallet-standard-adapter.d.ts
48
48
  /**
49
- * Adapt a Bitcoin Wallet Standard `Wallet` into a butr `WalletAdapter`.
50
- *
51
- * Returns `null` if the wallet doesn't advertise any `bip122:` chain or
52
- * doesn't expose `standard:connect`. Same posture as the SVM/Sui
53
- * adapters: gate features on what each wallet advertises, return
54
- * placeholders for RPC reads, forward `subscribe` through
55
- * `standard:events`.
56
- *
57
- * **Known limitations**
58
- *
59
- * - `getBalance` returns `{ value: 0n, symbol: "BTC" }`. Bitcoin
60
- * balance lookups require an Esplora/Electrum/RPC client outside
61
- * butr's scope.
62
- * - `getTransactionReceipt` returns `"Pending"` for the same reason.
63
- * - `sendTx` expects the consumer to pass `{ amount: bigint, recipient: string }`
64
- * via butr's `unknown` tx parameter; the simplest mental model that
65
- * maps onto `bitcoin:sendTransfer`. PSBT-shaped flows go through
66
- * `signTransaction`.
67
- * - `signTransaction` expects a `Uint8Array` PSBT (raw bytes); the
68
- * same shape `bitcoin:signPsbt` accepts. Consumers building with
69
- * `bitcoinjs-lib` get bytes via `psbt.toBuffer()`.
70
- * - `switchChain` is local-state-only; the capability flag is `false`
71
- * when only one chain is advertised. Wallets that ship a real switch
72
- * primitive (none today, portably) would need their own adapter.
49
+ * `sendTx` takes `{ amount, recipient }` for `bitcoin:sendTransfer` and
50
+ * `signTransaction` takes raw PSBT bytes for `bitcoin:signPsbt`. Balance
51
+ * and receipt reads would need an Esplora/Electrum client butr doesn't ship.
73
52
  */
74
53
  declare const buildBitcoinAdapter: (wallet: WalletStandardWallet,
75
54
  /** Optional. Called with a function that pushes a synthetic
76
55
  * `disconnected` event to all current subscribers. The discovery
77
56
  * layer invokes it on Wallet Standard `unregister`. */
78
57
  registerDisconnector?: (emit: () => void) => void) => WalletAdapter | null;
79
- /**
80
- * Subscribe to Bitcoin Wallet Standard announcements.
81
- *
82
- * Phantom (Bitcoin), Magic Eden, OKX Wallet (Bitcoin), and Leather
83
- * advertise `bitcoin:*` features over the same `getWallets()` bus that
84
- * SVM and Sui consume. `buildBitcoinAdapter` returns `null` for wallets
85
- * that don't advertise any `bip122:` chain, so multi-chain wallets
86
- * (Phantom: EVM + SVM + Sui + Bitcoin) produce one adapter per platform.
87
- *
88
- * Requires the **optional peer dependency** `@wallet-standard/app`.
89
- *
90
- * The discovery loop (dynamic import, dedupe, register / unregister
91
- * bridge) lives in `@usebutr/wallet-standard-shared`.
92
- */
58
+ /** Requires the optional `@wallet-standard/app` peer dep. */
93
59
  declare const discoverBitcoinAdapters: (onAdapter: (adapter: WalletAdapter) => void) => (() => void);
94
60
  //#endregion
95
61
  //#region src/chains.d.ts
96
62
  /**
97
- * CAIP-2 Bitcoin chain references use the first 8 bytes (big-endian)
98
- * of each network's genesis block hash. These string ids are what
99
- * Bitcoin Wallet Standard wallets (Phantom, Magic Eden, OKX) advertise
100
- * in `wallet.chains`; the adapter's `switchChain` rejects anything else.
101
- *
102
- * - Mainnet: `bip122:000000000019d6689c085ae165831e93`
103
- * - Testnet: `bip122:000000000933ea01ad0ee984209779ba`
104
- * - Signet: `bip122:00000008819873e925422c1ff0f99f7c`
105
- *
106
- * Bitcoin has no per-call "switch chain" RPC across the major wallets,
107
- * so butr's adapter treats chain as a wallet-side configuration;
108
- * `switchChain` updates local state to influence subsequent calls (same
109
- * posture as SVM). Bitcoin's `switchChain` capability is `false` by
110
- * default; consumers building on top can opt in if their wallet exposes
111
- * a switch primitive.
63
+ * CAIP-2 Bitcoin references are the first 8 bytes of each network's
64
+ * genesis block hash, which is what wallets advertise in `wallet.chains`.
65
+ * No major wallet exposes a switch-chain RPC, so chain stays local state.
112
66
  */
113
67
  declare const BITCOIN_CHAINS: {
114
68
  readonly mainnet: {
@@ -143,19 +97,9 @@ type BitcoinCapabilityInput = {
143
97
  };
144
98
  };
145
99
  /**
146
- * Bitcoin feature → butr capability mapping. The flags that hold for every
147
- * namespace are documented on `buildWalletCapabilities`.
148
- *
149
- * - `sendTransaction = bitcoin:sendTransfer present`. The wallet handles
150
- * UTXO selection, fee estimation, signing, and broadcast.
151
- * - `signTransaction = bitcoin:signPsbt present`. Sign-only PSBT path
152
- * for consumers that build their own UTXO-shaped transactions with
153
- * `bitcoinjs-lib` and broadcast through their own provider.
154
- * - `signMessage = bitcoin:signMessage present`.
155
- * - `signIn = false`. No Sign-In-With-Bitcoin standard yet (BIP-322 is
156
- * message-signing oriented, not auth).
157
- * - `subscribe = events present`. Most Bitcoin wallets don't emit
158
- * change events portably; capabilities admits the truth.
100
+ * `signIn` is false: there is no Sign-In-With-Bitcoin standard, and BIP-322
101
+ * is message-signing oriented rather than auth. `sendTransfer` leaves UTXO
102
+ * selection, fees and broadcast to the wallet; `signPsbt` is sign-only.
159
103
  */
160
104
  declare const resolveBitcoinCapabilities: (input: BitcoinCapabilityInput) => WalletCapabilities;
161
105
  //#endregion
@@ -188,13 +132,8 @@ type UnisatProvider = {
188
132
  //#endregion
189
133
  //#region src/injected/icon.d.ts
190
134
  /**
191
- * Generic Bitcoin wallet icon, same purse SVG style as the EVM
192
- * package, recoloured. Inline so the picker doesn't flash.
193
- *
194
- * Lives in its own file because both injected adapters (UniSat-shape
195
- * and sats-connect) need the same fallback when the wallet doesn't
196
- * advertise its own icon, and there's no other "shared" thing they
197
- * both want; a one-line module beats a grab-bag.
135
+ * Inlined as a data URI so the picker doesn't flash, and shared by both
136
+ * injected adapters as the fallback when a wallet advertises no icon.
198
137
  */
199
138
  declare const GENERIC_BITCOIN_ICON = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI2Y3OTMxYSIgc3Ryb2tlPSJub25lIj48Y2lyY2xlIGN4PSIxMiIgY3k9IjEyIiByPSIxMCIvPjx0ZXh0IHg9IjEyIiB5PSIxNyIgZm9udC1mYW1pbHk9InNlcmlmIiBmb250LXNpemU9IjE0IiBmb250LXdlaWdodD0iYm9sZCIgZmlsbD0iI2ZmZiIgdGV4dC1hbmNob3I9Im1pZGRsZSI+QjwvdGV4dD48L3N2Zz4=";
200
139
  //#endregion
@@ -220,46 +159,25 @@ type InjectedBitcoinDiscoveryOptions = {
220
159
  target?: InjectedHost | null;
221
160
  };
222
161
  /**
223
- * Last-resort discovery path for Bitcoin wallets that DON'T announce via
224
- * Wallet Standard. Probes, in order: `window.unisat`,
225
- * `window.okxwallet.bitcoin`, `window.XverseProviders.BitcoinProvider`,
226
- * `window.btc`.
227
- *
228
- * Wallet Standard wallets (Phantom, Magic Eden, Leather, modern OKX
229
- * builds) are already handled by `discoverBitcoinAdapters`. This
230
- * fallback waits `settleMs` (default 150ms), consults
231
- * `hasAnyWalletStandardAdapter` if provided, and emits adapters for
232
- * any injected providers that weren't already covered.
233
- *
234
- * Each emitted adapter has a stable id prefix (`injected:bitcoin:*`)
235
- * so consumers can distinguish them in the picker UI.
162
+ * Last-resort path for wallets that don't announce via Wallet Standard:
163
+ * `window.unisat`, OKX, Xverse, `window.btc`. The `settleMs` wait lets
164
+ * Wallet Standard announce first, else a wallet on both channels lists twice.
236
165
  */
237
166
  declare const discoverInjectedBitcoinAdapter: (onAdapter: (adapter: WalletAdapter) => void, options?: InjectedBitcoinDiscoveryOptions) => (() => void);
238
167
  //#endregion
239
168
  //#region src/discoverer.d.ts
240
169
  /**
241
- * Bitcoin's `PlatformDiscoverer`: Wallet Standard primary (Phantom,
242
- * Magic Eden, Leather, OKX); legacy fallback covers sats-connect
243
- * (Xverse), `window.unisat`, OKX legacy, and `window.btc`. The
244
- * fallback defers to Wallet Standard via `hasAnyPrimaryAdapter`.
170
+ * Wallet Standard is primary; the legacy fallback covers sats-connect
171
+ * (Xverse), `window.unisat`, OKX legacy and `window.btc`, and defers to
172
+ * the primary channel via `hasAnyPrimaryAdapter`.
245
173
  */
246
174
  declare const bitcoinDiscoverer: PlatformDiscoverer;
247
175
  //#endregion
248
176
  //#region src/signer-augmentation.d.ts
249
177
  /**
250
- * Augment `@usebutr/core`'s `SignerForPlatform` registry with the Bitcoin
251
- * entry. Bitcoin's signer surface is fragmented: `getSigner()` may
252
- * return one of three shapes depending on which adapter discovered the
253
- * wallet:
254
- *
255
- * - `WalletStandardWallet`: for Phantom, Magic Eden, Leather, OKX
256
- * (modern Wallet Standard path).
257
- * - A UniSat-shaped provider; for Unisat, OKX legacy, `window.btc`
258
- * (injected path).
259
- * - A sats-connect `BitcoinProvider`; for Xverse (sats-connect path).
260
- *
261
- * The registry surfaces the union honestly so consumers know they need
262
- * to runtime-narrow.
178
+ * Bitcoin's signer surface is fragmented: `getSigner()` returns a
179
+ * `WalletStandardWallet`, a UniSat-shaped provider, or a sats-connect
180
+ * `BitcoinProvider` depending on which adapter discovered the wallet.
263
181
  */
264
182
  type UnisatLike = {
265
183
  getAccounts: () => Promise<ReadonlyArray<string>>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/wallet-standard-types.ts","../src/wallet-standard-adapter.ts","../src/chains.ts","../src/capabilities.ts","../src/injected/sats-connect.ts","../src/injected/unisat.ts","../src/injected/icon.ts","../src/injected/index.ts","../src/discoverer.ts","../src/signer-augmentation.ts"],"mappings":";;;KAgBK;EACH,SAAS;EACT,SAAS;;KAGN;EACH,WAAW;EACX,eAAe;;KAGZ;EACH,cAAc,OAAO,4BAA4B,QAAQ;;KAGtD;EACH,SAAS;;EAET;;EAEA,MAAM;;;EAGN,aAAa;;KAGV;;;EAGH,YAAY;;KAGT;EACH,WAAW,OAAO,yBAAyB,QAAQ;;KAGhD;EACH,SAAS;EACT;EACA;EACA;;KAGG;;EAEH;;KAGG;EACH,eAAe,OAAO,6BAA6B,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cClBvD,sBACJ,QAAQ,sBAIR;;;;AAAA,wBAAwB,8BACvB;;;;;;;;;;;;;;;cAiIG,0BAA2B,YAAY,SAAS;;;;;;;;;;;;;;;;;;;;cClKhD;;;;;;;;;;;;;;;;;;;;cAqBA,qBAAqB,cAAc;;;KCrCpC;EACH;EACA;IACE;IACA;IACA;IACA;;;;;;;;;;;;;;;;;;cAmBE,6BAA8B,OAAO,2BAAyB;;;;KCnB/D;EACH,UACE,gBACA,SAAS,4BACN;IAAU;MAAU;;IAAmB;;;;;;;;KCFzC;EACH,mBAAmB,QAAQ;EAC3B,mBAAmB;EACnB,MACE,6CACA,cAAc,MAAM;EAEtB,YAAY,oBAAoB;EAChC,kBACE,6CACA,cAAc,MAAM;EAEtB,uBAAuB,QAAQ;EAC/B,eAAe,mBAAmB,mBAAmB;EACrD,cAAc,iBAAiB,qCAAqC;EACpE,WAAW,iBAAiB,UAAU,4BAA4B;;;;;;;;;;;;;cCjB9D;;;KCAD;EACH,MAAM;EACN;IAAc,UAAU;;EACxB,SAAS;EACT;IAAoB,kBAAkB;;;KAGnC;;;EAGH;;;EAGA;;EAEA,SAAS;;;;;;;;;;;;;;;;;cA+DL,iCACJ,YAAY,SAAS,wBACrB,UAAS;;;;;;;;;cC9EL,mBAAmB;;;;;;;;;;;;;;;;;;KCMpB;EACH,mBAAmB,QAAQ;EAC3B,uBAAuB,QAAQ;EAC/B,cAAc,iBAAiB,kBAAkB;EACjD,WAAW,iBAAiB,UAAU,4BAA4B;;KAG/D;EACH,UACE,gBACA,SAAS,4BACN;IAAU;MAAU;;IAAmB;;;;YAKlC;IACR,SAAS,kBAAkB,aAAa"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/wallet-standard-types.ts","../src/wallet-standard-adapter.ts","../src/chains.ts","../src/capabilities.ts","../src/injected/sats-connect.ts","../src/injected/unisat.ts","../src/injected/icon.ts","../src/injected/index.ts","../src/discoverer.ts","../src/signer-augmentation.ts"],"mappings":";;;KAQK;EACH,SAAS;EACT,SAAS;;KAGN;EACH,WAAW;EACX,eAAe;;KAGZ;EACH,cAAc,OAAO,4BAA4B,QAAQ;;KAGtD;EACH,SAAS;;EAET;;EAEA,MAAM;;;EAGN,aAAa;;KAGV;;;EAGH,YAAY;;KAGT;EACH,WAAW,OAAO,yBAAyB,QAAQ;;KAGhD;EACH,SAAS;EACT;EACA;EACA;;KAGG;;EAEH;;KAGG;EACH,eAAe,OAAO,6BAA6B,QAAQ;;;;;;;;;cC/BvD,sBACJ,QAAQ,sBAIR;;;;AAAA,wBAAwB,8BACvB;;cAyIG,0BAA2B,YAAY,SAAS;;;;;;;;cCjKhD;;;;;;;;;;;;;;;;;;;;cAqBA,qBAAqB,cAAc;;;KCzBpC;EACH;EACA;IACE;IACA;IACA;IACA;;;;;;;;cASE,6BAA8B,OAAO,2BAAyB;;;;KCT/D;EACH,UACE,gBACA,SAAS,4BACN;IAAU;MAAU;;IAAmB;;;;;;;;KCFzC;EACH,mBAAmB,QAAQ;EAC3B,mBAAmB;EACnB,MACE,6CACA,cAAc,MAAM;EAEtB,YAAY,oBAAoB;EAChC,kBACE,6CACA,cAAc,MAAM;EAEtB,uBAAuB,QAAQ;EAC/B,eAAe,mBAAmB,mBAAmB;EACrD,cAAc,iBAAiB,qCAAqC;EACpE,WAAW,iBAAiB,UAAU,4BAA4B;;;;;;;;cCtB9D;;;KCKD;EACH,MAAM;EACN;IAAc,UAAU;;EACxB,SAAS;EACT;IAAoB,kBAAkB;;;KAGnC;;;EAGH;;;EAGA;;EAEA,SAAS;;;;;;;cAqDL,iCACJ,YAAY,SAAS,wBACrB,UAAS;;;;;;;;cCrEL,mBAAmB;;;;;;;;KCHpB;EACH,mBAAmB,QAAQ;EAC3B,uBAAuB,QAAQ;EAC/B,cAAc,iBAAiB,kBAAkB;EACjD,WAAW,iBAAiB,UAAU,4BAA4B;;KAG/D;EACH,UACE,gBACA,SAAS,4BACN;IAAU;MAAU;;IAAmB;;;;YAKlC;IACR,SAAS,kBAAkB,aAAa"}
package/dist/index.js CHANGED
@@ -3,19 +3,9 @@ import { base64ToBytes, bytesToBase64, bytesToHex, hexToBytes } from "@usebutr/c
3
3
 
4
4
  //#region src/capabilities.ts
5
5
  /**
6
- * Bitcoin feature → butr capability mapping. The flags that hold for every
7
- * namespace are documented on `buildWalletCapabilities`.
8
- *
9
- * - `sendTransaction = bitcoin:sendTransfer present`. The wallet handles
10
- * UTXO selection, fee estimation, signing, and broadcast.
11
- * - `signTransaction = bitcoin:signPsbt present`. Sign-only PSBT path
12
- * for consumers that build their own UTXO-shaped transactions with
13
- * `bitcoinjs-lib` and broadcast through their own provider.
14
- * - `signMessage = bitcoin:signMessage present`.
15
- * - `signIn = false`. No Sign-In-With-Bitcoin standard yet (BIP-322 is
16
- * message-signing oriented, not auth).
17
- * - `subscribe = events present`. Most Bitcoin wallets don't emit
18
- * change events portably; capabilities admits the truth.
6
+ * `signIn` is false: there is no Sign-In-With-Bitcoin standard, and BIP-322
7
+ * is message-signing oriented rather than auth. `sendTransfer` leaves UTXO
8
+ * selection, fees and broadcast to the wallet; `signPsbt` is sign-only.
19
9
  */
20
10
  const resolveBitcoinCapabilities = (input) => buildWalletCapabilities({
21
11
  chainCount: input.chainCount,
@@ -32,30 +22,9 @@ const BITCOIN_PREFIX = "bip122:";
32
22
  const BITCOIN_DECIMALS = 8;
33
23
  const BITCOIN_MAINNET_ID = "bip122:000000000019d6689c085ae165831e93";
34
24
  /**
35
- * Adapt a Bitcoin Wallet Standard `Wallet` into a butr `WalletAdapter`.
36
- *
37
- * Returns `null` if the wallet doesn't advertise any `bip122:` chain or
38
- * doesn't expose `standard:connect`. Same posture as the SVM/Sui
39
- * adapters: gate features on what each wallet advertises, return
40
- * placeholders for RPC reads, forward `subscribe` through
41
- * `standard:events`.
42
- *
43
- * **Known limitations**
44
- *
45
- * - `getBalance` returns `{ value: 0n, symbol: "BTC" }`. Bitcoin
46
- * balance lookups require an Esplora/Electrum/RPC client outside
47
- * butr's scope.
48
- * - `getTransactionReceipt` returns `"Pending"` for the same reason.
49
- * - `sendTx` expects the consumer to pass `{ amount: bigint, recipient: string }`
50
- * via butr's `unknown` tx parameter; the simplest mental model that
51
- * maps onto `bitcoin:sendTransfer`. PSBT-shaped flows go through
52
- * `signTransaction`.
53
- * - `signTransaction` expects a `Uint8Array` PSBT (raw bytes); the
54
- * same shape `bitcoin:signPsbt` accepts. Consumers building with
55
- * `bitcoinjs-lib` get bytes via `psbt.toBuffer()`.
56
- * - `switchChain` is local-state-only; the capability flag is `false`
57
- * when only one chain is advertised. Wallets that ship a real switch
58
- * primitive (none today, portably) would need their own adapter.
25
+ * `sendTx` takes `{ amount, recipient }` for `bitcoin:sendTransfer` and
26
+ * `signTransaction` takes raw PSBT bytes for `bitcoin:signPsbt`. Balance
27
+ * and receipt reads would need an Esplora/Electrum client butr doesn't ship.
59
28
  */
60
29
  const buildBitcoinAdapter = (wallet, registerDisconnector) => {
61
30
  const core = createWalletStandardCore({
@@ -73,14 +42,21 @@ const buildBitcoinAdapter = (wallet, registerDisconnector) => {
73
42
  const signMessage = getFeature(wallet, "bitcoin:signMessage");
74
43
  const signPsbt = getFeature(wallet, "bitcoin:signPsbt");
75
44
  const sendTransfer = getFeature(wallet, "bitcoin:sendTransfer");
76
- const sendTransferTx = async (tx, account) => {
45
+ /** Resolve a caller-supplied target into a chain the wallet advertises,
46
+ * accepting either a full CAIP-2 id or a bare genesis-hash reference. */
47
+ const resolveTargetChain = (targetChainId) => {
48
+ const candidate = targetChainId.startsWith(BITCOIN_PREFIX) ? targetChainId : `${BITCOIN_PREFIX}${targetChainId}`;
49
+ if (!wallet.chains.includes(candidate)) throw new Error(`Wallet ${wallet.name} does not advertise chain "${candidate}". Available: ${wallet.chains.join(", ")}`);
50
+ return candidate;
51
+ };
52
+ const sendTransferTx = async (tx, account, chain) => {
77
53
  if (sendTransfer === void 0) throw new Error(`Wallet ${wallet.name} does not advertise bitcoin:sendTransfer`);
78
54
  if (typeof tx !== "object" || tx === null || !("amount" in tx) || typeof tx.amount !== "bigint" || !("recipient" in tx) || typeof tx.recipient !== "string") throw new TypeError("Bitcoin sendTx expects { amount: bigint, recipient: string }: pass the recipient address and an amount in satoshis");
79
55
  const { amount, recipient } = tx;
80
56
  return (await sendTransfer.sendTransfer({
81
57
  account: core.resolveAccount(account),
82
58
  amount,
83
- chain: core.currentChainId(),
59
+ chain: chain ?? core.currentChainId(),
84
60
  recipient
85
61
  })).txid;
86
62
  };
@@ -107,9 +83,10 @@ const buildBitcoinAdapter = (wallet, registerDisconnector) => {
107
83
  await core.connect();
108
84
  },
109
85
  sendTx: (tx, account) => sendTransferTx(tx, account),
110
- sendTxToChain: (tx, _targetChainId, account, cb) => {
111
- cb?.();
112
- return sendTransferTx(tx, account);
86
+ async sendTxToChain(tx, targetChainId, account, cb) {
87
+ const target = resolveTargetChain(targetChainId);
88
+ if (target !== core.currentChainId()) cb?.();
89
+ return await sendTransferTx(tx, account, target);
113
90
  },
114
91
  async signMessage(msg, account) {
115
92
  if (signMessage === void 0) throw new Error(`Wallet ${wallet.name} does not advertise bitcoin:signMessage`);
@@ -133,40 +110,15 @@ const buildBitcoinAdapter = (wallet, registerDisconnector) => {
133
110
  } }
134
111
  };
135
112
  };
136
- /**
137
- * Subscribe to Bitcoin Wallet Standard announcements.
138
- *
139
- * Phantom (Bitcoin), Magic Eden, OKX Wallet (Bitcoin), and Leather
140
- * advertise `bitcoin:*` features over the same `getWallets()` bus that
141
- * SVM and Sui consume. `buildBitcoinAdapter` returns `null` for wallets
142
- * that don't advertise any `bip122:` chain, so multi-chain wallets
143
- * (Phantom: EVM + SVM + Sui + Bitcoin) produce one adapter per platform.
144
- *
145
- * Requires the **optional peer dependency** `@wallet-standard/app`.
146
- *
147
- * The discovery loop (dynamic import, dedupe, register / unregister
148
- * bridge) lives in `@usebutr/wallet-standard-shared`.
149
- */
113
+ /** Requires the optional `@wallet-standard/app` peer dep. */
150
114
  const discoverBitcoinAdapters = (onAdapter) => discoverWalletStandard(onAdapter, (wallet, registerDisconnector) => buildBitcoinAdapter(wallet, registerDisconnector));
151
115
 
152
116
  //#endregion
153
117
  //#region src/chains.ts
154
118
  /**
155
- * CAIP-2 Bitcoin chain references use the first 8 bytes (big-endian)
156
- * of each network's genesis block hash. These string ids are what
157
- * Bitcoin Wallet Standard wallets (Phantom, Magic Eden, OKX) advertise
158
- * in `wallet.chains`; the adapter's `switchChain` rejects anything else.
159
- *
160
- * - Mainnet: `bip122:000000000019d6689c085ae165831e93`
161
- * - Testnet: `bip122:000000000933ea01ad0ee984209779ba`
162
- * - Signet: `bip122:00000008819873e925422c1ff0f99f7c`
163
- *
164
- * Bitcoin has no per-call "switch chain" RPC across the major wallets,
165
- * so butr's adapter treats chain as a wallet-side configuration;
166
- * `switchChain` updates local state to influence subsequent calls (same
167
- * posture as SVM). Bitcoin's `switchChain` capability is `false` by
168
- * default; consumers building on top can opt in if their wallet exposes
169
- * a switch primitive.
119
+ * CAIP-2 Bitcoin references are the first 8 bytes of each network's
120
+ * genesis block hash, which is what wallets advertise in `wallet.chains`.
121
+ * No major wallet exposes a switch-chain RPC, so chain stays local state.
170
122
  */
171
123
  const BITCOIN_CHAINS = {
172
124
  mainnet: {
@@ -193,13 +145,8 @@ const BITCOIN_CHAINS_LIST = Object.values(BITCOIN_CHAINS);
193
145
  //#endregion
194
146
  //#region src/injected/icon.ts
195
147
  /**
196
- * Generic Bitcoin wallet icon, same purse SVG style as the EVM
197
- * package, recoloured. Inline so the picker doesn't flash.
198
- *
199
- * Lives in its own file because both injected adapters (UniSat-shape
200
- * and sats-connect) need the same fallback when the wallet doesn't
201
- * advertise its own icon, and there's no other "shared" thing they
202
- * both want; a one-line module beats a grab-bag.
148
+ * Inlined as a data URI so the picker doesn't flash, and shared by both
149
+ * injected adapters as the fallback when a wallet advertises no icon.
203
150
  */
204
151
  const GENERIC_BITCOIN_ICON = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI2Y3OTMxYSIgc3Ryb2tlPSJub25lIj48Y2lyY2xlIGN4PSIxMiIgY3k9IjEyIiByPSIxMCIvPjx0ZXh0IHg9IjEyIiB5PSIxNyIgZm9udC1mYW1pbHk9InNlcmlmIiBmb250LXNpemU9IjE0IiBmb250LXdlaWdodD0iYm9sZCIgZmlsbD0iI2ZmZiIgdGV4dC1hbmNob3I9Im1pZGRsZSI+QjwvdGV4dD48L3N2Zz4=";
205
152
 
@@ -217,34 +164,50 @@ const CAPS_SATS_CONNECT = {
217
164
  switchAccount: false,
218
165
  switchChain: false
219
166
  };
167
+ const ACCOUNT_PURPOSES = ["payment", "ordinals"];
168
+ const CONNECT_MESSAGE = "Connect to butr";
169
+ const pickPaymentAddress = (addresses) => addresses.find((a) => a.purpose === "payment") ?? addresses[0];
220
170
  /**
221
- * Wrap an Xverse-style `BitcoinProvider` (sats-connect) into a butr
222
- * `WalletAdapter`. sats-connect routes everything through a single
223
- * `request(method, params)` RPC; we shim each butr capability onto the
224
- * corresponding sats-connect method.
225
- *
226
- * Tested against Xverse's `getAccounts`, `signMessage`, `signPsbt`,
227
- * `sendTransfer` methods.
171
+ * Only the payment address is exposed as an Account: sats-connect's
172
+ * `sendTransfer` takes no sender and always debits it. The taproot ordinals
173
+ * address stays reachable through `getSigner()` and `signMessage`'s account.
228
174
  */
229
175
  const buildSatsConnectAdapter = (id, name, provider) => {
230
176
  let chain = BITCOIN_CHAINS.mainnet;
231
- let cachedAccounts = [];
177
+ let session = { status: "disconnected" };
232
178
  const callRequest = async (method, params) => {
233
179
  const response = await provider.request(method, params);
234
180
  if (response.error) throw new Error(`[butr/bitcoin] sats-connect ${method} failed: ${response.error.message}`);
235
181
  return response.result;
236
182
  };
237
- const loadAccounts = async () => {
238
- const result = await callRequest("getAccounts", {
239
- message: "Connect to butr",
240
- purposes: ["payment", "ordinals"]
241
- });
242
- if (result?.addresses === void 0) return [];
243
- const addresses = result.addresses.map((a) => a.address);
244
- cachedAccounts = addresses;
245
- return addresses;
183
+ const startSession = async (method, params) => {
184
+ const addresses = (await callRequest(method, params))?.addresses ?? [];
185
+ const payment = pickPaymentAddress(addresses);
186
+ if (payment === void 0) throw new Error(`Wallet ${name} returned no addresses from ${method}`);
187
+ session = {
188
+ addresses,
189
+ payment: payment.address,
190
+ status: "connected"
191
+ };
192
+ };
193
+ const requestSession = () => startSession("getAccounts", {
194
+ message: CONNECT_MESSAGE,
195
+ purposes: ACCOUNT_PURPOSES
196
+ });
197
+ const requireSession = () => {
198
+ if (session.status !== "connected") throw new Error("No connected account");
199
+ return session;
200
+ };
201
+ const resolveAddress = (account) => {
202
+ const active = requireSession();
203
+ if (account === void 0) return active.payment;
204
+ const match = active.addresses.find((a) => a.address === account.walletAddress);
205
+ if (match === void 0) throw new Error(`Account ${account.walletAddress} is not exposed by ${name}`);
206
+ return match.address;
246
207
  };
247
- const sendTransferTx = async (tx) => {
208
+ const sendTransferTx = async (tx, account) => {
209
+ const sender = resolveAddress(account);
210
+ if (sender !== requireSession().payment) throw new Error(`Wallet ${name} always debits its payment address; ${sender} cannot be used as the sender.`);
248
211
  if (typeof tx !== "object" || tx === null || !("recipient" in tx) || typeof tx.recipient !== "string" || !("amount" in tx) || typeof tx.amount !== "bigint") throw new TypeError("Bitcoin sendTx expects { amount: bigint, recipient: string }: amount in satoshis");
249
212
  const { amount, recipient } = tx;
250
213
  return (await callRequest("sendTransfer", { recipients: [{
@@ -255,22 +218,19 @@ const buildSatsConnectAdapter = (id, name, provider) => {
255
218
  return {
256
219
  capabilities: CAPS_SATS_CONNECT,
257
220
  chainPlatform: "bitcoin",
258
- async connect() {
259
- await loadAccounts();
221
+ async connect(opts) {
222
+ if (opts?.silent === true) {
223
+ await startSession("wallet_getAccount", { addresses: ACCOUNT_PURPOSES });
224
+ return;
225
+ }
226
+ await requestSession();
260
227
  },
261
228
  disconnect: () => {
262
- cachedAccounts = [];
229
+ session = { status: "disconnected" };
263
230
  return Promise.resolve();
264
231
  },
265
- async getAccount() {
266
- const first = cachedAccounts[0];
267
- if (first !== void 0) return buildAccount(first, chain);
268
- const firstFresh = (await loadAccounts())[0];
269
- return firstFresh === void 0 ? null : buildAccount(firstFresh, chain);
270
- },
271
- async getAccounts() {
272
- return (cachedAccounts.length > 0 ? cachedAccounts : await loadAccounts()).map((a) => buildAccount(a, chain));
273
- },
232
+ getAccount: () => Promise.resolve(session.status === "connected" ? buildAccount(session.payment, chain) : null),
233
+ getAccounts: () => Promise.resolve(session.status === "connected" ? [buildAccount(session.payment, chain)] : []),
274
234
  getBalance: () => Promise.resolve({
275
235
  decimals: 8,
276
236
  formatted: "0",
@@ -282,17 +242,14 @@ const buildSatsConnectAdapter = (id, name, provider) => {
282
242
  icon: GENERIC_BITCOIN_ICON,
283
243
  id,
284
244
  name,
285
- async requestAccounts() {
286
- await loadAccounts();
287
- },
288
- sendTx: (tx) => sendTransferTx(tx),
289
- sendTxToChain: (tx, _targetChainId, _account, cb) => {
245
+ requestAccounts: () => requestSession(),
246
+ sendTx: (tx, account) => sendTransferTx(tx, account),
247
+ sendTxToChain: (tx, _targetChainId, account, cb) => {
290
248
  cb?.();
291
- return sendTransferTx(tx);
249
+ return sendTransferTx(tx, account);
292
250
  },
293
- async signMessage(msg) {
294
- const address = cachedAccounts[0];
295
- if (address === void 0) throw new Error("No connected account");
251
+ async signMessage(msg, account) {
252
+ const address = resolveAddress(account);
296
253
  const result = await callRequest("signMessage", {
297
254
  address,
298
255
  message: new TextDecoder().decode(msg)
@@ -329,23 +286,19 @@ const CAPS_UNISAT = {
329
286
  switchAccount: false,
330
287
  switchChain: false
331
288
  };
289
+ const toStringArray = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
332
290
  /**
333
- * Wrap a UniSat-style provider (`window.unisat`, `window.okxwallet.bitcoin`,
334
- * `window.btc`) into a butr `WalletAdapter`.
335
- *
336
- * The UniSat shape is consistent enough that one adapter handles every
337
- * derivative. Differences (network names, `sendBitcoin` presence) are
338
- * gated by feature detection per call.
291
+ * The UniSat shape is consistent enough across `window.unisat`,
292
+ * `window.okxwallet.bitcoin` and `window.btc` that one adapter covers all
293
+ * three; the differences (network names, `sendBitcoin`) are probed per call.
339
294
  */
340
- const toStringArray = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
341
295
  const buildUnisatAdapter = (id, name, provider) => {
342
296
  let chain = BITCOIN_CHAINS.mainnet;
343
- const listenersSet = /* @__PURE__ */ new Set();
344
- const sendBitcoinTx = (tx) => {
297
+ const sendBitcoinTx = async (tx) => {
345
298
  if (typeof provider.sendBitcoin !== "function") throw new TypeError(`Wallet ${name} does not expose sendBitcoin`);
346
299
  if (typeof tx !== "object" || tx === null || !("recipient" in tx) || typeof tx.recipient !== "string" || !("amount" in tx) || typeof tx.amount !== "bigint") throw new TypeError("Bitcoin sendTx expects { amount: bigint, recipient: string }: amount in satoshis");
347
300
  const { amount, recipient } = tx;
348
- return provider.sendBitcoin(recipient, Number(amount));
301
+ return await provider.sendBitcoin(recipient, Number(amount));
349
302
  };
350
303
  const refreshChain = async () => {
351
304
  if (typeof provider.getNetwork !== "function") return;
@@ -415,7 +368,6 @@ const buildUnisatAdapter = (id, name, provider) => {
415
368
  return hexToBytes(signedHex);
416
369
  },
417
370
  subscribe(listener) {
418
- listenersSet.add(listener);
419
371
  const onAccountsChanged = (...args) => {
420
372
  const accounts = toStringArray(args[0]);
421
373
  if (accounts.length === 0) {
@@ -439,14 +391,12 @@ const buildUnisatAdapter = (id, name, provider) => {
439
391
  return () => {
440
392
  provider.removeListener?.("accountsChanged", onAccountsChanged);
441
393
  provider.removeListener?.("networkChanged", onNetworkChanged);
442
- listenersSet.delete(listener);
443
394
  };
444
395
  },
445
- switchChain: (target) => {
396
+ async switchChain(target) {
446
397
  if (target.namespace !== "bip122") throw new Error(`Bitcoin adapter received non-Bitcoin chain "${target.id}". Pass a chain with namespace "bip122".`);
447
398
  chain = target;
448
- refreshChain();
449
- return Promise.resolve();
399
+ await refreshChain();
450
400
  }
451
401
  };
452
402
  };
@@ -483,19 +433,9 @@ const probeProviders = (host) => {
483
433
  return adapters;
484
434
  };
485
435
  /**
486
- * Last-resort discovery path for Bitcoin wallets that DON'T announce via
487
- * Wallet Standard. Probes, in order: `window.unisat`,
488
- * `window.okxwallet.bitcoin`, `window.XverseProviders.BitcoinProvider`,
489
- * `window.btc`.
490
- *
491
- * Wallet Standard wallets (Phantom, Magic Eden, Leather, modern OKX
492
- * builds) are already handled by `discoverBitcoinAdapters`. This
493
- * fallback waits `settleMs` (default 150ms), consults
494
- * `hasAnyWalletStandardAdapter` if provided, and emits adapters for
495
- * any injected providers that weren't already covered.
496
- *
497
- * Each emitted adapter has a stable id prefix (`injected:bitcoin:*`)
498
- * so consumers can distinguish them in the picker UI.
436
+ * Last-resort path for wallets that don't announce via Wallet Standard:
437
+ * `window.unisat`, OKX, Xverse, `window.btc`. The `settleMs` wait lets
438
+ * Wallet Standard announce first, else a wallet on both channels lists twice.
499
439
  */
500
440
  const discoverInjectedBitcoinAdapter = (onAdapter, options = {}) => {
501
441
  const settleMs = options.settleMs ?? DEFAULT_SETTLE_MS;
@@ -517,10 +457,9 @@ const discoverInjectedBitcoinAdapter = (onAdapter, options = {}) => {
517
457
  //#endregion
518
458
  //#region src/discoverer.ts
519
459
  /**
520
- * Bitcoin's `PlatformDiscoverer`: Wallet Standard primary (Phantom,
521
- * Magic Eden, Leather, OKX); legacy fallback covers sats-connect
522
- * (Xverse), `window.unisat`, OKX legacy, and `window.btc`. The
523
- * fallback defers to Wallet Standard via `hasAnyPrimaryAdapter`.
460
+ * Wallet Standard is primary; the legacy fallback covers sats-connect
461
+ * (Xverse), `window.unisat`, OKX legacy and `window.btc`, and defers to
462
+ * the primary channel via `hasAnyPrimaryAdapter`.
524
463
  */
525
464
  const bitcoinDiscoverer = {
526
465
  fallback: { subscribe: (onAdapter, opts) => discoverInjectedBitcoinAdapter(onAdapter, { hasAnyWalletStandardAdapter: opts.hasAnyPrimaryAdapter }) },
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/capabilities.ts","../src/wallet-standard-adapter.ts","../src/chains.ts","../src/injected/icon.ts","../src/injected/sats-connect.ts","../src/injected/unisat.ts","../src/injected/index.ts","../src/discoverer.ts"],"sourcesContent":["import type { WalletCapabilities } from \"@usebutr/core\";\nimport { buildWalletCapabilities } from \"@usebutr/wallet-standard-shared\";\n\ntype BitcoinCapabilityInput = {\n chainCount: number;\n features: {\n events: boolean;\n sendTransfer: boolean;\n signMessage: boolean;\n signPsbt: boolean;\n };\n};\n\n/**\n * Bitcoin feature → butr capability mapping. The flags that hold for every\n * namespace are documented on `buildWalletCapabilities`.\n *\n * - `sendTransaction = bitcoin:sendTransfer present`. The wallet handles\n * UTXO selection, fee estimation, signing, and broadcast.\n * - `signTransaction = bitcoin:signPsbt present`. Sign-only PSBT path\n * for consumers that build their own UTXO-shaped transactions with\n * `bitcoinjs-lib` and broadcast through their own provider.\n * - `signMessage = bitcoin:signMessage present`.\n * - `signIn = false`. No Sign-In-With-Bitcoin standard yet (BIP-322 is\n * message-signing oriented, not auth).\n * - `subscribe = events present`. Most Bitcoin wallets don't emit\n * change events portably; capabilities admits the truth.\n */\nconst resolveBitcoinCapabilities = (input: BitcoinCapabilityInput): WalletCapabilities =>\n buildWalletCapabilities({\n chainCount: input.chainCount,\n events: input.features.events,\n sendTransaction: input.features.sendTransfer,\n signIn: false,\n signMessage: input.features.signMessage,\n signTransaction: input.features.signPsbt,\n });\n\nexport type { BitcoinCapabilityInput };\nexport { resolveBitcoinCapabilities };\n","import type { WalletAdapter } from \"@usebutr/core\";\nimport {\n createWalletStandardCore,\n discoverWalletStandard,\n getFeature,\n slugify,\n} from \"@usebutr/wallet-standard-shared\";\nimport type { WalletStandardWallet } from \"@usebutr/wallet-standard-shared\";\n\nimport { resolveBitcoinCapabilities } from \"./capabilities\";\nimport type {\n BitcoinSendTransferFeature,\n BitcoinSignMessageFeature,\n BitcoinSignPsbtFeature,\n} from \"./wallet-standard-types\";\n\nconst BITCOIN_PREFIX = \"bip122:\";\nconst BITCOIN_DECIMALS = 8;\nconst BITCOIN_MAINNET_ID = \"bip122:000000000019d6689c085ae165831e93\";\n\n/**\n * Adapt a Bitcoin Wallet Standard `Wallet` into a butr `WalletAdapter`.\n *\n * Returns `null` if the wallet doesn't advertise any `bip122:` chain or\n * doesn't expose `standard:connect`. Same posture as the SVM/Sui\n * adapters: gate features on what each wallet advertises, return\n * placeholders for RPC reads, forward `subscribe` through\n * `standard:events`.\n *\n * **Known limitations**\n *\n * - `getBalance` returns `{ value: 0n, symbol: \"BTC\" }`. Bitcoin\n * balance lookups require an Esplora/Electrum/RPC client outside\n * butr's scope.\n * - `getTransactionReceipt` returns `\"Pending\"` for the same reason.\n * - `sendTx` expects the consumer to pass `{ amount: bigint, recipient: string }`\n * via butr's `unknown` tx parameter; the simplest mental model that\n * maps onto `bitcoin:sendTransfer`. PSBT-shaped flows go through\n * `signTransaction`.\n * - `signTransaction` expects a `Uint8Array` PSBT (raw bytes); the\n * same shape `bitcoin:signPsbt` accepts. Consumers building with\n * `bitcoinjs-lib` get bytes via `psbt.toBuffer()`.\n * - `switchChain` is local-state-only; the capability flag is `false`\n * when only one chain is advertised. Wallets that ship a real switch\n * primitive (none today, portably) would need their own adapter.\n */\nconst buildBitcoinAdapter = (\n wallet: WalletStandardWallet,\n /** Optional. Called with a function that pushes a synthetic\n * `disconnected` event to all current subscribers. The discovery\n * layer invokes it on Wallet Standard `unregister`. */\n registerDisconnector?: (emit: () => void) => void,\n): WalletAdapter | null => {\n const core = createWalletStandardCore({\n chainPrefix: BITCOIN_PREFIX,\n id: slugify(\"btc\", wallet.name),\n label: \"Bitcoin\",\n namespace: \"bip122\",\n platform: \"Bitcoin\",\n preferredChainIds: [BITCOIN_MAINNET_ID],\n registerDisconnector,\n trackChainChanges: true,\n wallet,\n });\n if (core === null) {\n return null;\n }\n\n const signMessage = getFeature<BitcoinSignMessageFeature>(wallet, \"bitcoin:signMessage\");\n const signPsbt = getFeature<BitcoinSignPsbtFeature>(wallet, \"bitcoin:signPsbt\");\n const sendTransfer = getFeature<BitcoinSendTransferFeature>(wallet, \"bitcoin:sendTransfer\");\n\n const sendTransferTx = async (\n tx: unknown,\n account?: { walletAddress: string },\n ): Promise<string> => {\n if (sendTransfer === undefined) {\n throw new Error(`Wallet ${wallet.name} does not advertise bitcoin:sendTransfer`);\n }\n if (\n typeof tx !== \"object\" ||\n tx === null ||\n !(\"amount\" in tx) ||\n typeof tx.amount !== \"bigint\" ||\n !(\"recipient\" in tx) ||\n typeof tx.recipient !== \"string\"\n ) {\n throw new TypeError(\n \"Bitcoin sendTx expects { amount: bigint, recipient: string }: pass the recipient address and an amount in satoshis\",\n );\n }\n const { amount, recipient } = tx;\n const output = await sendTransfer.sendTransfer({\n account: core.resolveAccount(account),\n amount,\n chain: core.currentChainId(),\n recipient,\n });\n return output.txid;\n };\n\n return {\n ...core,\n capabilities: resolveBitcoinCapabilities({\n chainCount: core.chainCount,\n features: {\n events: core.hasEvents,\n sendTransfer: Boolean(sendTransfer),\n signMessage: Boolean(signMessage),\n signPsbt: Boolean(signPsbt),\n },\n }),\n chainPlatform: \"bitcoin\",\n\n getBalance: () =>\n Promise.resolve({\n decimals: BITCOIN_DECIMALS,\n formatted: \"0\",\n symbol: \"BTC\",\n value: 0n,\n }),\n\n getTransactionReceipt: () => Promise.resolve({ status: \"Pending\" as const }),\n\n async requestAccounts() {\n await core.connect();\n },\n\n sendTx: (tx, account) => sendTransferTx(tx, account),\n\n sendTxToChain: (tx, _targetChainId, account, cb) => {\n cb?.();\n return sendTransferTx(tx, account);\n },\n\n async signMessage(msg, account) {\n if (signMessage === undefined) {\n throw new Error(`Wallet ${wallet.name} does not advertise bitcoin:signMessage`);\n }\n const output = await signMessage.signMessage({\n account: core.resolveAccount(account),\n message: msg,\n });\n return { signature: output.signature, signedMessage: output.signedMessage };\n },\n\n ...(signPsbt === undefined\n ? {}\n : {\n async signTransaction(tx, account) {\n const wsAccount = core.resolveAccount(account);\n if (!(tx instanceof Uint8Array)) {\n throw new TypeError(\n \"Bitcoin signTransaction expects a PSBT as Uint8Array (e.g. psbt.toBuffer())\",\n );\n }\n const output = await signPsbt.signPsbt({\n account: wsAccount,\n chain: core.currentChainId(),\n psbt: tx,\n });\n return output.signedPsbt;\n },\n }),\n };\n};\n\n/**\n * Subscribe to Bitcoin Wallet Standard announcements.\n *\n * Phantom (Bitcoin), Magic Eden, OKX Wallet (Bitcoin), and Leather\n * advertise `bitcoin:*` features over the same `getWallets()` bus that\n * SVM and Sui consume. `buildBitcoinAdapter` returns `null` for wallets\n * that don't advertise any `bip122:` chain, so multi-chain wallets\n * (Phantom: EVM + SVM + Sui + Bitcoin) produce one adapter per platform.\n *\n * Requires the **optional peer dependency** `@wallet-standard/app`.\n *\n * The discovery loop (dynamic import, dedupe, register / unregister\n * bridge) lives in `@usebutr/wallet-standard-shared`.\n */\nconst discoverBitcoinAdapters = (onAdapter: (adapter: WalletAdapter) => void): (() => void) =>\n discoverWalletStandard(onAdapter, (wallet, registerDisconnector) =>\n buildBitcoinAdapter(wallet, registerDisconnector),\n );\n\nexport { buildBitcoinAdapter, discoverBitcoinAdapters };\n","import type { ChainBase } from \"@usebutr/core\";\n\n/**\n * CAIP-2 Bitcoin chain references use the first 8 bytes (big-endian)\n * of each network's genesis block hash. These string ids are what\n * Bitcoin Wallet Standard wallets (Phantom, Magic Eden, OKX) advertise\n * in `wallet.chains`; the adapter's `switchChain` rejects anything else.\n *\n * - Mainnet: `bip122:000000000019d6689c085ae165831e93`\n * - Testnet: `bip122:000000000933ea01ad0ee984209779ba`\n * - Signet: `bip122:00000008819873e925422c1ff0f99f7c`\n *\n * Bitcoin has no per-call \"switch chain\" RPC across the major wallets,\n * so butr's adapter treats chain as a wallet-side configuration;\n * `switchChain` updates local state to influence subsequent calls (same\n * posture as SVM). Bitcoin's `switchChain` capability is `false` by\n * default; consumers building on top can opt in if their wallet exposes\n * a switch primitive.\n */\nconst BITCOIN_CHAINS = {\n mainnet: {\n id: \"bip122:000000000019d6689c085ae165831e93\",\n name: \"Bitcoin\",\n namespace: \"bip122\",\n reference: \"000000000019d6689c085ae165831e93\",\n },\n signet: {\n id: \"bip122:00000008819873e925422c1ff0f99f7c\",\n name: \"Bitcoin Signet\",\n namespace: \"bip122\",\n reference: \"00000008819873e925422c1ff0f99f7c\",\n },\n testnet: {\n id: \"bip122:000000000933ea01ad0ee984209779ba\",\n name: \"Bitcoin Testnet\",\n namespace: \"bip122\",\n reference: \"000000000933ea01ad0ee984209779ba\",\n },\n} as const satisfies Record<string, ChainBase>;\n\nconst BITCOIN_CHAINS_LIST: ReadonlyArray<ChainBase> = Object.values(BITCOIN_CHAINS);\n\nexport { BITCOIN_CHAINS, BITCOIN_CHAINS_LIST };\n","/**\n * Generic Bitcoin wallet icon, same purse SVG style as the EVM\n * package, recoloured. Inline so the picker doesn't flash.\n *\n * Lives in its own file because both injected adapters (UniSat-shape\n * and sats-connect) need the same fallback when the wallet doesn't\n * advertise its own icon, and there's no other \"shared\" thing they\n * both want; a one-line module beats a grab-bag.\n */\nconst GENERIC_BITCOIN_ICON =\n \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI2Y3OTMxYSIgc3Ryb2tlPSJub25lIj48Y2lyY2xlIGN4PSIxMiIgY3k9IjEyIiByPSIxMCIvPjx0ZXh0IHg9IjEyIiB5PSIxNyIgZm9udC1mYW1pbHk9InNlcmlmIiBmb250LXNpemU9IjE0IiBmb250LXdlaWdodD0iYm9sZCIgZmlsbD0iI2ZmZiIgdGV4dC1hbmNob3I9Im1pZGRsZSI+QjwvdGV4dD48L3N2Zz4=\";\n\nexport { GENERIC_BITCOIN_ICON };\n","import type { ChainBase, WalletAdapter, WalletCapabilities } from \"@usebutr/core\";\nimport { base64ToBytes, bytesToBase64 } from \"@usebutr/core\";\nimport { buildAccount } from \"@usebutr/wallet-standard-shared\";\n\nimport { BITCOIN_CHAINS } from \"../chains\";\n\nimport { GENERIC_BITCOIN_ICON } from \"./icon\";\n\n/** sats-connect (Xverse) shape; a JSON-RPC-ish `request(method, params)`. */\ntype SatsConnectProvider = {\n request: (\n method: string,\n params?: Record<string, unknown>,\n ) => Promise<{ error?: { message: string }; result?: unknown }>;\n};\n\nconst CAPS_SATS_CONNECT: WalletCapabilities = {\n getBalance: false,\n getTransactionReceipt: false,\n requestAccounts: true,\n sendTransaction: true,\n signIn: false,\n signMessage: true,\n signTransaction: true,\n subscribe: false,\n switchAccount: false,\n switchChain: false,\n};\n\n/**\n * Wrap an Xverse-style `BitcoinProvider` (sats-connect) into a butr\n * `WalletAdapter`. sats-connect routes everything through a single\n * `request(method, params)` RPC; we shim each butr capability onto the\n * corresponding sats-connect method.\n *\n * Tested against Xverse's `getAccounts`, `signMessage`, `signPsbt`,\n * `sendTransfer` methods.\n */\nconst buildSatsConnectAdapter = (\n id: string,\n name: string,\n provider: SatsConnectProvider,\n): WalletAdapter => {\n let chain: ChainBase = BITCOIN_CHAINS.mainnet;\n let cachedAccounts: ReadonlyArray<string> = [];\n\n // oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- caller-supplied result shape for an untyped RPC bridge\n const callRequest = async <T>(method: string, params?: Record<string, unknown>): Promise<T> => {\n const response = await provider.request(method, params);\n if (response.error) {\n throw new Error(`[butr/bitcoin] sats-connect ${method} failed: ${response.error.message}`);\n }\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- untyped sats-connect RPC result; caller declares the shape\n return response.result as T;\n };\n\n const loadAccounts = async (): Promise<ReadonlyArray<string>> => {\n // `callRequest` only asserts the caller's shape onto an untyped RPC\n // payload, so a wallet that answers without a `result` lands here as\n // null/undefined and has to be read through an optional chain.\n const result = await callRequest<{\n addresses?: ReadonlyArray<{ address: string; purpose?: string }>;\n } | null>(\"getAccounts\", {\n message: \"Connect to butr\",\n purposes: [\"payment\", \"ordinals\"],\n });\n if (result?.addresses === undefined) {\n return [];\n }\n const addresses = result.addresses.map((a) => a.address);\n cachedAccounts = addresses;\n return addresses;\n };\n\n const sendTransferTx = async (tx: unknown): Promise<string> => {\n if (\n typeof tx !== \"object\" ||\n tx === null ||\n !(\"recipient\" in tx) ||\n typeof tx.recipient !== \"string\" ||\n !(\"amount\" in tx) ||\n typeof tx.amount !== \"bigint\"\n ) {\n throw new TypeError(\n \"Bitcoin sendTx expects { amount: bigint, recipient: string }: amount in satoshis\",\n );\n }\n const { amount, recipient } = tx;\n const result = await callRequest<{ txid: string }>(\"sendTransfer\", {\n recipients: [{ address: recipient, amount: amount.toString() }],\n });\n return result.txid;\n };\n\n return {\n capabilities: CAPS_SATS_CONNECT,\n chainPlatform: \"bitcoin\",\n\n async connect() {\n await loadAccounts();\n },\n\n disconnect: () => {\n cachedAccounts = [];\n return Promise.resolve();\n },\n\n async getAccount() {\n const first = cachedAccounts[0];\n if (first !== undefined) {\n return buildAccount(first, chain);\n }\n const accounts = await loadAccounts();\n const firstFresh = accounts[0];\n return firstFresh === undefined ? null : buildAccount(firstFresh, chain);\n },\n\n async getAccounts() {\n const accounts = cachedAccounts.length > 0 ? cachedAccounts : await loadAccounts();\n return accounts.map((a) => buildAccount(a, chain));\n },\n\n getBalance: () =>\n Promise.resolve({\n decimals: 8,\n formatted: \"0\",\n symbol: \"BTC\",\n value: 0n,\n }),\n\n getSigner: () => Promise.resolve(provider),\n\n getTransactionReceipt: () => Promise.resolve({ status: \"Pending\" as const }),\n\n icon: GENERIC_BITCOIN_ICON,\n id,\n name,\n\n async requestAccounts() {\n await loadAccounts();\n },\n\n sendTx: (tx) => sendTransferTx(tx),\n\n sendTxToChain: (tx, _targetChainId, _account, cb) => {\n cb?.();\n return sendTransferTx(tx);\n },\n\n async signMessage(msg) {\n const address = cachedAccounts[0];\n if (address === undefined) {\n throw new Error(\"No connected account\");\n }\n const result = await callRequest<{ messageHash?: string; signature: string }>(\"signMessage\", {\n address,\n message: new TextDecoder().decode(msg),\n });\n return { signature: base64ToBytes(result.signature), signedMessage: msg };\n },\n\n async signTransaction(tx) {\n if (!(tx instanceof Uint8Array)) {\n throw new TypeError(\n \"Bitcoin signTransaction expects a PSBT as Uint8Array (e.g. psbt.toBuffer())\",\n );\n }\n // sats-connect's signPsbt takes a base64 PSBT; encode our bytes\n // and decode the response back to bytes.\n const result = await callRequest<{ psbt: string }>(\"signPsbt\", {\n psbt: bytesToBase64(tx),\n });\n return base64ToBytes(result.psbt);\n },\n\n switchChain: (target) => {\n if (target.namespace !== \"bip122\") {\n throw new Error(\n `Bitcoin adapter received non-Bitcoin chain \"${target.id}\". Pass a chain with namespace \"bip122\".`,\n );\n }\n chain = target;\n return Promise.resolve();\n },\n };\n};\n\nexport type { SatsConnectProvider };\nexport { buildSatsConnectAdapter };\n","import type { ChainBase, ConnectorEvent, WalletAdapter, WalletCapabilities } from \"@usebutr/core\";\nimport { base64ToBytes, bytesToHex, hexToBytes } from \"@usebutr/core\";\nimport { buildAccount } from \"@usebutr/wallet-standard-shared\";\n\nimport { BITCOIN_CHAINS } from \"../chains\";\n\nimport { GENERIC_BITCOIN_ICON } from \"./icon\";\n\n/** UniSat-style provider: a single object on `window.unisat` with the\n * same four methods every UniSat-derivative wallet exposes (UniSat\n * itself, OKX Wallet's Bitcoin path at `window.okxwallet.bitcoin`). */\ntype UnisatProvider = {\n getAccounts: () => Promise<ReadonlyArray<string>>;\n getNetwork?: () => Promise<\"livenet\" | \"mainnet\" | \"testnet\" | \"signet\">;\n on?: (\n event: \"accountsChanged\" | \"networkChanged\",\n listener: (...args: Array<unknown>) => void,\n ) => void;\n pushPsbt?: (psbtHex: string) => Promise<string>;\n removeListener?: (\n event: \"accountsChanged\" | \"networkChanged\",\n listener: (...args: Array<unknown>) => void,\n ) => void;\n requestAccounts: () => Promise<ReadonlyArray<string>>;\n sendBitcoin?: (recipient: string, amount: number) => Promise<string>;\n signMessage: (message: string, type?: \"ecdsa\" | \"bip322-simple\") => Promise<string>;\n signPsbt: (psbtHex: string, options?: Record<string, unknown>) => Promise<string>;\n};\n\nconst CAPS_UNISAT: WalletCapabilities = {\n getBalance: false,\n getTransactionReceipt: false,\n requestAccounts: true,\n sendTransaction: true,\n signIn: false,\n signMessage: true,\n signTransaction: true,\n subscribe: true,\n switchAccount: false,\n switchChain: false,\n};\n\n/**\n * Wrap a UniSat-style provider (`window.unisat`, `window.okxwallet.bitcoin`,\n * `window.btc`) into a butr `WalletAdapter`.\n *\n * The UniSat shape is consistent enough that one adapter handles every\n * derivative. Differences (network names, `sendBitcoin` presence) are\n * gated by feature detection per call.\n */\nconst toStringArray = (value: unknown): Array<string> =>\n Array.isArray(value) ? value.filter((item): item is string => typeof item === \"string\") : [];\n\nconst buildUnisatAdapter = (id: string, name: string, provider: UnisatProvider): WalletAdapter => {\n let chain: ChainBase = BITCOIN_CHAINS.mainnet;\n const listenersSet = new Set<(event: ConnectorEvent) => void>();\n\n const sendBitcoinTx = (tx: unknown): Promise<string> => {\n if (typeof provider.sendBitcoin !== \"function\") {\n throw new TypeError(`Wallet ${name} does not expose sendBitcoin`);\n }\n if (\n typeof tx !== \"object\" ||\n tx === null ||\n !(\"recipient\" in tx) ||\n typeof tx.recipient !== \"string\" ||\n !(\"amount\" in tx) ||\n typeof tx.amount !== \"bigint\"\n ) {\n throw new TypeError(\n \"Bitcoin sendTx expects { amount: bigint, recipient: string }: amount in satoshis\",\n );\n }\n const { amount, recipient } = tx;\n return provider.sendBitcoin(recipient, Number(amount));\n };\n\n const refreshChain = async () => {\n if (typeof provider.getNetwork !== \"function\") {\n return;\n }\n try {\n const network = await provider.getNetwork();\n if (network === \"testnet\") {\n chain = BITCOIN_CHAINS.testnet;\n } else if (network === \"signet\") {\n chain = BITCOIN_CHAINS.signet;\n } else {\n chain = BITCOIN_CHAINS.mainnet;\n }\n } catch {\n void 0;\n }\n };\n\n return {\n capabilities: CAPS_UNISAT,\n chainPlatform: \"bitcoin\",\n\n async connect(opts) {\n if (opts?.silent === true) {\n const accounts = await provider.getAccounts();\n if (accounts.length === 0) {\n throw new Error(\"No authorized accounts for silent reconnect\");\n }\n await refreshChain();\n return;\n }\n await provider.requestAccounts();\n await refreshChain();\n },\n\n disconnect: () =>\n // No portable disconnect across UniSat-shaped wallets. butr's\n // reducer marks the wallet disconnected on its side regardless.\n Promise.resolve(),\n\n async getAccount() {\n const accounts = await provider.getAccounts();\n const first = accounts[0];\n if (first === undefined) {\n return null;\n }\n await refreshChain();\n return buildAccount(first, chain);\n },\n\n async getAccounts() {\n const accounts = await provider.getAccounts();\n if (accounts.length === 0) {\n return [];\n }\n await refreshChain();\n return accounts.map((a) => buildAccount(a, chain));\n },\n\n getBalance: () =>\n Promise.resolve({\n decimals: 8,\n formatted: \"0\",\n symbol: \"BTC\",\n value: 0n,\n }),\n\n getSigner: () => Promise.resolve(provider),\n\n getTransactionReceipt: () => Promise.resolve({ status: \"Pending\" as const }),\n\n icon: GENERIC_BITCOIN_ICON,\n id,\n name,\n\n async requestAccounts() {\n await provider.requestAccounts();\n await refreshChain();\n },\n\n sendTx: (tx) => sendBitcoinTx(tx),\n\n sendTxToChain: (tx, _targetChainIdDecimal, _account, cb) => {\n cb?.();\n return sendBitcoinTx(tx);\n },\n\n async signMessage(msg) {\n // UniSat takes a UTF-8 message and returns a base64 signature.\n // We return the raw signature bytes (decoded from base64) and the\n // bytes the wallet signed (pass-through of the input). UniSat\n // doesn't expose a \"pre-image\" so the input itself is the signed\n // message from butr's perspective.\n const text = new TextDecoder().decode(msg);\n const signatureB64 = await provider.signMessage(text);\n return { signature: base64ToBytes(signatureB64), signedMessage: msg };\n },\n\n async signTransaction(tx) {\n if (!(tx instanceof Uint8Array)) {\n throw new TypeError(\n \"Bitcoin signTransaction expects a PSBT as Uint8Array (e.g. psbt.toBuffer())\",\n );\n }\n const signedHex = await provider.signPsbt(bytesToHex(tx));\n return hexToBytes(signedHex);\n },\n\n subscribe(listener) {\n listenersSet.add(listener);\n const onAccountsChanged = (...args: ReadonlyArray<unknown>) => {\n const accounts = toStringArray(args[0]);\n if (accounts.length === 0) {\n listener({ type: \"disconnected\" });\n return;\n }\n const built = accounts.map((a) => buildAccount(a, chain));\n const first = built[0];\n if (first === undefined) {\n return;\n }\n listener({ account: first, accounts: built, type: \"accountChanged\" });\n };\n const onNetworkChanged = () => {\n void refreshChain();\n };\n provider.on?.(\"accountsChanged\", onAccountsChanged);\n provider.on?.(\"networkChanged\", onNetworkChanged);\n return () => {\n provider.removeListener?.(\"accountsChanged\", onAccountsChanged);\n provider.removeListener?.(\"networkChanged\", onNetworkChanged);\n listenersSet.delete(listener);\n };\n },\n\n switchChain: (target) => {\n if (target.namespace !== \"bip122\") {\n throw new Error(\n `Bitcoin adapter received non-Bitcoin chain \"${target.id}\". Pass a chain with namespace \"bip122\".`,\n );\n }\n // UniSat-shaped wallets pick their network via the user's\n // extension UI; we update local state so consumers see it, but\n // the wallet itself doesn't switch.\n chain = target;\n void refreshChain();\n return Promise.resolve();\n },\n };\n};\n\nexport type { UnisatProvider };\nexport { buildUnisatAdapter };\n","import type { WalletAdapter } from \"@usebutr/core\";\n\nimport type { SatsConnectProvider } from \"./sats-connect\";\nimport { buildSatsConnectAdapter } from \"./sats-connect\";\nimport type { UnisatProvider } from \"./unisat\";\nimport { buildUnisatAdapter } from \"./unisat\";\n\nconst DEFAULT_SETTLE_MS = 150;\n\ntype InjectedHost = {\n btc?: UnisatProvider;\n okxwallet?: { bitcoin?: UnisatProvider };\n unisat?: UnisatProvider;\n XverseProviders?: { BitcoinProvider?: SatsConnectProvider };\n};\n\ntype InjectedBitcoinDiscoveryOptions = {\n /** Suppresses emission when Wallet Standard discovery has already\n * registered a Bitcoin adapter for the same browser session. */\n hasAnyWalletStandardAdapter?: () => boolean;\n /** Settle deadline in ms before the fallback fires. Same default and\n * intent as the EVM injected fallback. */\n settleMs?: number;\n /** Override the global host (tests, iframes). */\n target?: InjectedHost | null;\n};\n\nconst readHost = (target: InjectedBitcoinDiscoveryOptions[\"target\"]): InjectedHost | null => {\n if (target !== undefined) {\n return target;\n }\n if (typeof window === \"undefined\") {\n return null;\n }\n // `window` carries the injected wallet globals (`unisat`, `okxwallet`,\n // etc.) as an untyped host object; `InjectedHost` names the four keys\n // butr probes, all optional and read defensively in `probeProviders`.\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- untyped injected-wallet window globals\n return window as unknown as InjectedHost;\n};\n\nconst probeProviders = (host: InjectedHost): Array<WalletAdapter> => {\n const adapters: Array<WalletAdapter> = [];\n const seen = new Set<UnisatProvider | SatsConnectProvider>();\n\n if (host.unisat && !seen.has(host.unisat)) {\n seen.add(host.unisat);\n adapters.push(buildUnisatAdapter(\"injected:bitcoin:unisat\", \"Unisat\", host.unisat));\n }\n\n const okxBitcoin = host.okxwallet?.bitcoin;\n if (okxBitcoin && !seen.has(okxBitcoin)) {\n seen.add(okxBitcoin);\n adapters.push(buildUnisatAdapter(\"injected:bitcoin:okx\", \"OKX Wallet (Bitcoin)\", okxBitcoin));\n }\n\n const xverse = host.XverseProviders?.BitcoinProvider;\n if (xverse && !seen.has(xverse)) {\n seen.add(xverse);\n adapters.push(buildSatsConnectAdapter(\"injected:bitcoin:xverse\", \"Xverse\", xverse));\n }\n\n if (host.btc && !seen.has(host.btc)) {\n seen.add(host.btc);\n adapters.push(\n buildUnisatAdapter(\"injected:bitcoin:legacy\", \"Browser Bitcoin wallet\", host.btc),\n );\n }\n\n return adapters;\n};\n\n/**\n * Last-resort discovery path for Bitcoin wallets that DON'T announce via\n * Wallet Standard. Probes, in order: `window.unisat`,\n * `window.okxwallet.bitcoin`, `window.XverseProviders.BitcoinProvider`,\n * `window.btc`.\n *\n * Wallet Standard wallets (Phantom, Magic Eden, Leather, modern OKX\n * builds) are already handled by `discoverBitcoinAdapters`. This\n * fallback waits `settleMs` (default 150ms), consults\n * `hasAnyWalletStandardAdapter` if provided, and emits adapters for\n * any injected providers that weren't already covered.\n *\n * Each emitted adapter has a stable id prefix (`injected:bitcoin:*`)\n * so consumers can distinguish them in the picker UI.\n */\nconst discoverInjectedBitcoinAdapter = (\n onAdapter: (adapter: WalletAdapter) => void,\n options: InjectedBitcoinDiscoveryOptions = {},\n): (() => void) => {\n const settleMs = options.settleMs ?? DEFAULT_SETTLE_MS;\n let cancelled = false;\n const timer = setTimeout(() => {\n if (cancelled) {\n return;\n }\n if (options.hasAnyWalletStandardAdapter?.() === true) {\n return;\n }\n const host = readHost(options.target);\n if (host === null) {\n return;\n }\n const adapters = probeProviders(host);\n for (const adapter of adapters) {\n onAdapter(adapter);\n }\n }, settleMs);\n\n return () => {\n cancelled = true;\n clearTimeout(timer);\n };\n};\n\nexport type { InjectedBitcoinDiscoveryOptions };\nexport { GENERIC_BITCOIN_ICON } from \"./icon\";\nexport { discoverInjectedBitcoinAdapter };\n","import type { PlatformDiscoverer } from \"@usebutr/core\";\n\nimport { discoverInjectedBitcoinAdapter } from \"./injected\";\nimport { discoverBitcoinAdapters } from \"./wallet-standard-adapter\";\n\n/**\n * Bitcoin's `PlatformDiscoverer`: Wallet Standard primary (Phantom,\n * Magic Eden, Leather, OKX); legacy fallback covers sats-connect\n * (Xverse), `window.unisat`, OKX legacy, and `window.btc`. The\n * fallback defers to Wallet Standard via `hasAnyPrimaryAdapter`.\n */\nconst bitcoinDiscoverer: PlatformDiscoverer = {\n fallback: {\n subscribe: (onAdapter, opts) =>\n discoverInjectedBitcoinAdapter(onAdapter, {\n hasAnyWalletStandardAdapter: opts.hasAnyPrimaryAdapter,\n }),\n },\n subscribe: discoverBitcoinAdapters,\n};\n\nexport { bitcoinDiscoverer };\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/capabilities.ts","../src/wallet-standard-adapter.ts","../src/chains.ts","../src/injected/icon.ts","../src/injected/sats-connect.ts","../src/injected/unisat.ts","../src/injected/index.ts","../src/discoverer.ts"],"sourcesContent":["import type { WalletCapabilities } from \"@usebutr/core\";\nimport { buildWalletCapabilities } from \"@usebutr/wallet-standard-shared\";\n\ntype BitcoinCapabilityInput = {\n chainCount: number;\n features: {\n events: boolean;\n sendTransfer: boolean;\n signMessage: boolean;\n signPsbt: boolean;\n };\n};\n\n/**\n * `signIn` is false: there is no Sign-In-With-Bitcoin standard, and BIP-322\n * is message-signing oriented rather than auth. `sendTransfer` leaves UTXO\n * selection, fees and broadcast to the wallet; `signPsbt` is sign-only.\n */\nconst resolveBitcoinCapabilities = (input: BitcoinCapabilityInput): WalletCapabilities =>\n buildWalletCapabilities({\n chainCount: input.chainCount,\n events: input.features.events,\n sendTransaction: input.features.sendTransfer,\n signIn: false,\n signMessage: input.features.signMessage,\n signTransaction: input.features.signPsbt,\n });\n\nexport type { BitcoinCapabilityInput };\nexport { resolveBitcoinCapabilities };\n","import type { WalletAdapter } from \"@usebutr/core\";\nimport {\n createWalletStandardCore,\n discoverWalletStandard,\n getFeature,\n slugify,\n} from \"@usebutr/wallet-standard-shared\";\nimport type { WalletStandardWallet } from \"@usebutr/wallet-standard-shared\";\n\nimport { resolveBitcoinCapabilities } from \"./capabilities\";\nimport type {\n BitcoinSendTransferFeature,\n BitcoinSignMessageFeature,\n BitcoinSignPsbtFeature,\n} from \"./wallet-standard-types\";\n\nconst BITCOIN_PREFIX = \"bip122:\";\nconst BITCOIN_DECIMALS = 8;\nconst BITCOIN_MAINNET_ID = \"bip122:000000000019d6689c085ae165831e93\";\n\n/**\n * `sendTx` takes `{ amount, recipient }` for `bitcoin:sendTransfer` and\n * `signTransaction` takes raw PSBT bytes for `bitcoin:signPsbt`. Balance\n * and receipt reads would need an Esplora/Electrum client butr doesn't ship.\n */\nconst buildBitcoinAdapter = (\n wallet: WalletStandardWallet,\n /** Optional. Called with a function that pushes a synthetic\n * `disconnected` event to all current subscribers. The discovery\n * layer invokes it on Wallet Standard `unregister`. */\n registerDisconnector?: (emit: () => void) => void,\n): WalletAdapter | null => {\n const core = createWalletStandardCore({\n chainPrefix: BITCOIN_PREFIX,\n id: slugify(\"btc\", wallet.name),\n label: \"Bitcoin\",\n namespace: \"bip122\",\n platform: \"Bitcoin\",\n preferredChainIds: [BITCOIN_MAINNET_ID],\n registerDisconnector,\n trackChainChanges: true,\n wallet,\n });\n if (core === null) {\n return null;\n }\n\n const signMessage = getFeature<BitcoinSignMessageFeature>(wallet, \"bitcoin:signMessage\");\n const signPsbt = getFeature<BitcoinSignPsbtFeature>(wallet, \"bitcoin:signPsbt\");\n const sendTransfer = getFeature<BitcoinSendTransferFeature>(wallet, \"bitcoin:sendTransfer\");\n\n /** Resolve a caller-supplied target into a chain the wallet advertises,\n * accepting either a full CAIP-2 id or a bare genesis-hash reference. */\n const resolveTargetChain = (targetChainId: string): string => {\n const candidate = targetChainId.startsWith(BITCOIN_PREFIX)\n ? targetChainId\n : `${BITCOIN_PREFIX}${targetChainId}`;\n if (!wallet.chains.includes(candidate)) {\n throw new Error(\n `Wallet ${wallet.name} does not advertise chain \"${candidate}\". Available: ${wallet.chains.join(\", \")}`,\n );\n }\n return candidate;\n };\n\n const sendTransferTx = async (\n tx: unknown,\n account?: { walletAddress: string },\n chain?: string,\n ): Promise<string> => {\n if (sendTransfer === undefined) {\n throw new Error(`Wallet ${wallet.name} does not advertise bitcoin:sendTransfer`);\n }\n if (\n typeof tx !== \"object\" ||\n tx === null ||\n !(\"amount\" in tx) ||\n typeof tx.amount !== \"bigint\" ||\n !(\"recipient\" in tx) ||\n typeof tx.recipient !== \"string\"\n ) {\n throw new TypeError(\n \"Bitcoin sendTx expects { amount: bigint, recipient: string }: pass the recipient address and an amount in satoshis\",\n );\n }\n const { amount, recipient } = tx;\n const output = await sendTransfer.sendTransfer({\n account: core.resolveAccount(account),\n amount,\n chain: chain ?? core.currentChainId(),\n recipient,\n });\n return output.txid;\n };\n\n return {\n ...core,\n capabilities: resolveBitcoinCapabilities({\n chainCount: core.chainCount,\n features: {\n events: core.hasEvents,\n sendTransfer: Boolean(sendTransfer),\n signMessage: Boolean(signMessage),\n signPsbt: Boolean(signPsbt),\n },\n }),\n chainPlatform: \"bitcoin\",\n\n getBalance: () =>\n Promise.resolve({\n decimals: BITCOIN_DECIMALS,\n formatted: \"0\",\n symbol: \"BTC\",\n value: 0n,\n }),\n\n getTransactionReceipt: () => Promise.resolve({ status: \"Pending\" as const }),\n\n async requestAccounts() {\n await core.connect();\n },\n\n sendTx: (tx, account) => sendTransferTx(tx, account),\n\n // Async so an unadvertised chain surfaces as a rejection rather than a\n // synchronous throw; the declared return type is a promise either way.\n async sendTxToChain(tx, targetChainId, account, cb) {\n const target = resolveTargetChain(targetChainId);\n if (target !== core.currentChainId()) {\n cb?.();\n }\n const txid = await sendTransferTx(tx, account, target);\n return txid;\n },\n\n async signMessage(msg, account) {\n if (signMessage === undefined) {\n throw new Error(`Wallet ${wallet.name} does not advertise bitcoin:signMessage`);\n }\n const output = await signMessage.signMessage({\n account: core.resolveAccount(account),\n message: msg,\n });\n return { signature: output.signature, signedMessage: output.signedMessage };\n },\n\n ...(signPsbt === undefined\n ? {}\n : {\n async signTransaction(tx, account) {\n const wsAccount = core.resolveAccount(account);\n if (!(tx instanceof Uint8Array)) {\n throw new TypeError(\n \"Bitcoin signTransaction expects a PSBT as Uint8Array (e.g. psbt.toBuffer())\",\n );\n }\n const output = await signPsbt.signPsbt({\n account: wsAccount,\n chain: core.currentChainId(),\n psbt: tx,\n });\n return output.signedPsbt;\n },\n }),\n };\n};\n\n/** Requires the optional `@wallet-standard/app` peer dep. */\nconst discoverBitcoinAdapters = (onAdapter: (adapter: WalletAdapter) => void): (() => void) =>\n discoverWalletStandard(onAdapter, (wallet, registerDisconnector) =>\n buildBitcoinAdapter(wallet, registerDisconnector),\n );\n\nexport { buildBitcoinAdapter, discoverBitcoinAdapters };\n","import type { ChainBase } from \"@usebutr/core\";\n\n/**\n * CAIP-2 Bitcoin references are the first 8 bytes of each network's\n * genesis block hash, which is what wallets advertise in `wallet.chains`.\n * No major wallet exposes a switch-chain RPC, so chain stays local state.\n */\nconst BITCOIN_CHAINS = {\n mainnet: {\n id: \"bip122:000000000019d6689c085ae165831e93\",\n name: \"Bitcoin\",\n namespace: \"bip122\",\n reference: \"000000000019d6689c085ae165831e93\",\n },\n signet: {\n id: \"bip122:00000008819873e925422c1ff0f99f7c\",\n name: \"Bitcoin Signet\",\n namespace: \"bip122\",\n reference: \"00000008819873e925422c1ff0f99f7c\",\n },\n testnet: {\n id: \"bip122:000000000933ea01ad0ee984209779ba\",\n name: \"Bitcoin Testnet\",\n namespace: \"bip122\",\n reference: \"000000000933ea01ad0ee984209779ba\",\n },\n} as const satisfies Record<string, ChainBase>;\n\nconst BITCOIN_CHAINS_LIST: ReadonlyArray<ChainBase> = Object.values(BITCOIN_CHAINS);\n\nexport { BITCOIN_CHAINS, BITCOIN_CHAINS_LIST };\n","/**\n * Inlined as a data URI so the picker doesn't flash, and shared by both\n * injected adapters as the fallback when a wallet advertises no icon.\n */\nconst GENERIC_BITCOIN_ICON =\n \"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI2Y3OTMxYSIgc3Ryb2tlPSJub25lIj48Y2lyY2xlIGN4PSIxMiIgY3k9IjEyIiByPSIxMCIvPjx0ZXh0IHg9IjEyIiB5PSIxNyIgZm9udC1mYW1pbHk9InNlcmlmIiBmb250LXNpemU9IjE0IiBmb250LXdlaWdodD0iYm9sZCIgZmlsbD0iI2ZmZiIgdGV4dC1hbmNob3I9Im1pZGRsZSI+QjwvdGV4dD48L3N2Zz4=\";\n\nexport { GENERIC_BITCOIN_ICON };\n","import type { Account, ChainBase, WalletAdapter, WalletCapabilities } from \"@usebutr/core\";\nimport { base64ToBytes, bytesToBase64 } from \"@usebutr/core\";\nimport { buildAccount } from \"@usebutr/wallet-standard-shared\";\n\nimport { BITCOIN_CHAINS } from \"../chains\";\n\nimport { GENERIC_BITCOIN_ICON } from \"./icon\";\n\n/** sats-connect (Xverse) shape; a JSON-RPC-ish `request(method, params)`. */\ntype SatsConnectProvider = {\n request: (\n method: string,\n params?: Record<string, unknown>,\n ) => Promise<{ error?: { message: string }; result?: unknown }>;\n};\n\n/** One address entry of a sats-connect account result. `purpose` tags the\n * role: `\"payment\"` is the spendable address, `\"ordinals\"` is a taproot\n * address that `sendTransfer` never debits. */\ntype SatsAddress = {\n address: string;\n publicKey?: string;\n purpose?: string;\n};\n\ntype ConnectedSession = {\n addresses: ReadonlyArray<SatsAddress>;\n payment: string;\n status: \"connected\";\n};\n\ntype Session = ConnectedSession | { status: \"disconnected\" };\n\nconst CAPS_SATS_CONNECT: WalletCapabilities = {\n getBalance: false,\n getTransactionReceipt: false,\n requestAccounts: true,\n sendTransaction: true,\n signIn: false,\n signMessage: true,\n signTransaction: true,\n subscribe: false,\n switchAccount: false,\n switchChain: false,\n};\n\nconst ACCOUNT_PURPOSES = [\"payment\", \"ordinals\"];\nconst CONNECT_MESSAGE = \"Connect to butr\";\n\nconst pickPaymentAddress = (addresses: ReadonlyArray<SatsAddress>): SatsAddress | undefined =>\n addresses.find((a) => a.purpose === \"payment\") ?? addresses[0];\n\n/**\n * Only the payment address is exposed as an Account: sats-connect's\n * `sendTransfer` takes no sender and always debits it. The taproot ordinals\n * address stays reachable through `getSigner()` and `signMessage`'s account.\n */\nconst buildSatsConnectAdapter = (\n id: string,\n name: string,\n provider: SatsConnectProvider,\n): WalletAdapter => {\n let chain: ChainBase = BITCOIN_CHAINS.mainnet;\n let session: Session = { status: \"disconnected\" };\n\n // oxlint-disable-next-line typescript/no-unnecessary-type-parameters -- caller-supplied result shape for an untyped RPC bridge\n const callRequest = async <T>(method: string, params?: Record<string, unknown>): Promise<T> => {\n const response = await provider.request(method, params);\n if (response.error) {\n throw new Error(`[butr/bitcoin] sats-connect ${method} failed: ${response.error.message}`);\n }\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- untyped sats-connect RPC result; caller declares the shape\n return response.result as T;\n };\n\n const startSession = async (method: string, params: Record<string, unknown>): Promise<void> => {\n const result = await callRequest<{\n addresses?: ReadonlyArray<SatsAddress>;\n } | null>(method, params);\n const addresses = result?.addresses ?? [];\n const payment = pickPaymentAddress(addresses);\n if (payment === undefined) {\n throw new Error(`Wallet ${name} returned no addresses from ${method}`);\n }\n session = { addresses, payment: payment.address, status: \"connected\" };\n };\n\n const requestSession = () =>\n startSession(\"getAccounts\", { message: CONNECT_MESSAGE, purposes: ACCOUNT_PURPOSES });\n\n const requireSession = (): ConnectedSession => {\n if (session.status !== \"connected\") {\n throw new Error(\"No connected account\");\n }\n return session;\n };\n\n const resolveAddress = (account?: Account): string => {\n const active = requireSession();\n if (account === undefined) {\n return active.payment;\n }\n const match = active.addresses.find((a) => a.address === account.walletAddress);\n if (match === undefined) {\n throw new Error(`Account ${account.walletAddress} is not exposed by ${name}`);\n }\n return match.address;\n };\n\n const sendTransferTx = async (tx: unknown, account?: Account): Promise<string> => {\n const sender = resolveAddress(account);\n if (sender !== requireSession().payment) {\n throw new Error(\n `Wallet ${name} always debits its payment address; ${sender} cannot be used as the sender.`,\n );\n }\n if (\n typeof tx !== \"object\" ||\n tx === null ||\n !(\"recipient\" in tx) ||\n typeof tx.recipient !== \"string\" ||\n !(\"amount\" in tx) ||\n typeof tx.amount !== \"bigint\"\n ) {\n throw new TypeError(\n \"Bitcoin sendTx expects { amount: bigint, recipient: string }: amount in satoshis\",\n );\n }\n const { amount, recipient } = tx;\n const result = await callRequest<{ txid: string }>(\"sendTransfer\", {\n recipients: [{ address: recipient, amount: amount.toString() }],\n });\n return result.txid;\n };\n\n return {\n capabilities: CAPS_SATS_CONNECT,\n chainPlatform: \"bitcoin\",\n\n async connect(opts) {\n if (opts?.silent === true) {\n // `wallet_getAccount` reads already-granted permissions without\n // showing wallet UI; `getAccounts` IS Xverse's approval prompt.\n // Builds predating `wallet_getAccount` reject here, which butr's\n // hydration treats as a clean restore failure.\n await startSession(\"wallet_getAccount\", { addresses: ACCOUNT_PURPOSES });\n return;\n }\n await requestSession();\n },\n\n disconnect: () => {\n session = { status: \"disconnected\" };\n return Promise.resolve();\n },\n\n getAccount: () =>\n Promise.resolve(session.status === \"connected\" ? buildAccount(session.payment, chain) : null),\n\n getAccounts: () =>\n Promise.resolve(session.status === \"connected\" ? [buildAccount(session.payment, chain)] : []),\n\n getBalance: () =>\n Promise.resolve({\n decimals: 8,\n formatted: \"0\",\n symbol: \"BTC\",\n value: 0n,\n }),\n\n getSigner: () => Promise.resolve(provider),\n\n getTransactionReceipt: () => Promise.resolve({ status: \"Pending\" as const }),\n\n icon: GENERIC_BITCOIN_ICON,\n id,\n name,\n\n requestAccounts: () => requestSession(),\n\n sendTx: (tx, account) => sendTransferTx(tx, account),\n\n sendTxToChain: (tx, _targetChainId, account, cb) => {\n cb?.();\n return sendTransferTx(tx, account);\n },\n\n async signMessage(msg, account) {\n const address = resolveAddress(account);\n const result = await callRequest<{ messageHash?: string; signature: string }>(\"signMessage\", {\n address,\n message: new TextDecoder().decode(msg),\n });\n return { signature: base64ToBytes(result.signature), signedMessage: msg };\n },\n\n async signTransaction(tx) {\n if (!(tx instanceof Uint8Array)) {\n throw new TypeError(\n \"Bitcoin signTransaction expects a PSBT as Uint8Array (e.g. psbt.toBuffer())\",\n );\n }\n const result = await callRequest<{ psbt: string }>(\"signPsbt\", {\n psbt: bytesToBase64(tx),\n });\n return base64ToBytes(result.psbt);\n },\n\n switchChain: (target) => {\n if (target.namespace !== \"bip122\") {\n throw new Error(\n `Bitcoin adapter received non-Bitcoin chain \"${target.id}\". Pass a chain with namespace \"bip122\".`,\n );\n }\n chain = target;\n return Promise.resolve();\n },\n };\n};\n\nexport type { SatsConnectProvider };\nexport { buildSatsConnectAdapter };\n","import type { ChainBase, WalletAdapter, WalletCapabilities } from \"@usebutr/core\";\nimport { base64ToBytes, bytesToHex, hexToBytes } from \"@usebutr/core\";\nimport { buildAccount } from \"@usebutr/wallet-standard-shared\";\n\nimport { BITCOIN_CHAINS } from \"../chains\";\n\nimport { GENERIC_BITCOIN_ICON } from \"./icon\";\n\n/** UniSat-style provider: a single object on `window.unisat` with the\n * same four methods every UniSat-derivative wallet exposes (UniSat\n * itself, OKX Wallet's Bitcoin path at `window.okxwallet.bitcoin`). */\ntype UnisatProvider = {\n getAccounts: () => Promise<ReadonlyArray<string>>;\n getNetwork?: () => Promise<\"livenet\" | \"mainnet\" | \"testnet\" | \"signet\">;\n on?: (\n event: \"accountsChanged\" | \"networkChanged\",\n listener: (...args: Array<unknown>) => void,\n ) => void;\n pushPsbt?: (psbtHex: string) => Promise<string>;\n removeListener?: (\n event: \"accountsChanged\" | \"networkChanged\",\n listener: (...args: Array<unknown>) => void,\n ) => void;\n requestAccounts: () => Promise<ReadonlyArray<string>>;\n sendBitcoin?: (recipient: string, amount: number) => Promise<string>;\n signMessage: (message: string, type?: \"ecdsa\" | \"bip322-simple\") => Promise<string>;\n signPsbt: (psbtHex: string, options?: Record<string, unknown>) => Promise<string>;\n};\n\nconst CAPS_UNISAT: WalletCapabilities = {\n getBalance: false,\n getTransactionReceipt: false,\n requestAccounts: true,\n sendTransaction: true,\n signIn: false,\n signMessage: true,\n signTransaction: true,\n subscribe: true,\n switchAccount: false,\n switchChain: false,\n};\n\nconst toStringArray = (value: unknown): Array<string> =>\n Array.isArray(value) ? value.filter((item): item is string => typeof item === \"string\") : [];\n\n/**\n * The UniSat shape is consistent enough across `window.unisat`,\n * `window.okxwallet.bitcoin` and `window.btc` that one adapter covers all\n * three; the differences (network names, `sendBitcoin`) are probed per call.\n */\nconst buildUnisatAdapter = (id: string, name: string, provider: UnisatProvider): WalletAdapter => {\n let chain: ChainBase = BITCOIN_CHAINS.mainnet;\n\n const sendBitcoinTx = async (tx: unknown): Promise<string> => {\n if (typeof provider.sendBitcoin !== \"function\") {\n throw new TypeError(`Wallet ${name} does not expose sendBitcoin`);\n }\n if (\n typeof tx !== \"object\" ||\n tx === null ||\n !(\"recipient\" in tx) ||\n typeof tx.recipient !== \"string\" ||\n !(\"amount\" in tx) ||\n typeof tx.amount !== \"bigint\"\n ) {\n throw new TypeError(\n \"Bitcoin sendTx expects { amount: bigint, recipient: string }: amount in satoshis\",\n );\n }\n const { amount, recipient } = tx;\n const txid = await provider.sendBitcoin(recipient, Number(amount));\n return txid;\n };\n\n const refreshChain = async () => {\n if (typeof provider.getNetwork !== \"function\") {\n return;\n }\n try {\n const network = await provider.getNetwork();\n if (network === \"testnet\") {\n chain = BITCOIN_CHAINS.testnet;\n } else if (network === \"signet\") {\n chain = BITCOIN_CHAINS.signet;\n } else {\n chain = BITCOIN_CHAINS.mainnet;\n }\n } catch {\n void 0;\n }\n };\n\n return {\n capabilities: CAPS_UNISAT,\n chainPlatform: \"bitcoin\",\n\n async connect(opts) {\n if (opts?.silent === true) {\n const accounts = await provider.getAccounts();\n if (accounts.length === 0) {\n throw new Error(\"No authorized accounts for silent reconnect\");\n }\n await refreshChain();\n return;\n }\n await provider.requestAccounts();\n await refreshChain();\n },\n\n disconnect: () => Promise.resolve(),\n\n async getAccount() {\n const accounts = await provider.getAccounts();\n const first = accounts[0];\n if (first === undefined) {\n return null;\n }\n await refreshChain();\n return buildAccount(first, chain);\n },\n\n async getAccounts() {\n const accounts = await provider.getAccounts();\n if (accounts.length === 0) {\n return [];\n }\n await refreshChain();\n return accounts.map((a) => buildAccount(a, chain));\n },\n\n getBalance: () =>\n Promise.resolve({\n decimals: 8,\n formatted: \"0\",\n symbol: \"BTC\",\n value: 0n,\n }),\n\n getSigner: () => Promise.resolve(provider),\n\n getTransactionReceipt: () => Promise.resolve({ status: \"Pending\" as const }),\n\n icon: GENERIC_BITCOIN_ICON,\n id,\n name,\n\n async requestAccounts() {\n await provider.requestAccounts();\n await refreshChain();\n },\n\n sendTx: (tx) => sendBitcoinTx(tx),\n\n sendTxToChain: (tx, _targetChainIdDecimal, _account, cb) => {\n cb?.();\n return sendBitcoinTx(tx);\n },\n\n async signMessage(msg) {\n const text = new TextDecoder().decode(msg);\n const signatureB64 = await provider.signMessage(text);\n return { signature: base64ToBytes(signatureB64), signedMessage: msg };\n },\n\n async signTransaction(tx) {\n if (!(tx instanceof Uint8Array)) {\n throw new TypeError(\n \"Bitcoin signTransaction expects a PSBT as Uint8Array (e.g. psbt.toBuffer())\",\n );\n }\n const signedHex = await provider.signPsbt(bytesToHex(tx));\n return hexToBytes(signedHex);\n },\n\n subscribe(listener) {\n const onAccountsChanged = (...args: ReadonlyArray<unknown>) => {\n const accounts = toStringArray(args[0]);\n if (accounts.length === 0) {\n listener({ type: \"disconnected\" });\n return;\n }\n const built = accounts.map((a) => buildAccount(a, chain));\n const first = built[0];\n if (first === undefined) {\n return;\n }\n listener({ account: first, accounts: built, type: \"accountChanged\" });\n };\n const onNetworkChanged = () => {\n void refreshChain();\n };\n provider.on?.(\"accountsChanged\", onAccountsChanged);\n provider.on?.(\"networkChanged\", onNetworkChanged);\n return () => {\n provider.removeListener?.(\"accountsChanged\", onAccountsChanged);\n provider.removeListener?.(\"networkChanged\", onNetworkChanged);\n };\n },\n\n // `capabilities.switchChain` is false: UniSat exposes no way to ask the\n // wallet to change network, so the wallet's own setting is authoritative\n // and every account read re-reads it. This validates the request and then\n // reports what the wallet actually says, rather than assigning the target\n // locally and having the next read silently contradict it.\n async switchChain(target) {\n if (target.namespace !== \"bip122\") {\n throw new Error(\n `Bitcoin adapter received non-Bitcoin chain \"${target.id}\". Pass a chain with namespace \"bip122\".`,\n );\n }\n chain = target;\n await refreshChain();\n },\n };\n};\n\nexport type { UnisatProvider };\nexport { buildUnisatAdapter };\n","import type { WalletAdapter } from \"@usebutr/core\";\n\nimport type { SatsConnectProvider } from \"./sats-connect\";\nimport { buildSatsConnectAdapter } from \"./sats-connect\";\nimport type { UnisatProvider } from \"./unisat\";\nimport { buildUnisatAdapter } from \"./unisat\";\n\nconst DEFAULT_SETTLE_MS = 150;\n\ntype InjectedHost = {\n btc?: UnisatProvider;\n okxwallet?: { bitcoin?: UnisatProvider };\n unisat?: UnisatProvider;\n XverseProviders?: { BitcoinProvider?: SatsConnectProvider };\n};\n\ntype InjectedBitcoinDiscoveryOptions = {\n /** Suppresses emission when Wallet Standard discovery has already\n * registered a Bitcoin adapter for the same browser session. */\n hasAnyWalletStandardAdapter?: () => boolean;\n /** Settle deadline in ms before the fallback fires. Same default and\n * intent as the EVM injected fallback. */\n settleMs?: number;\n /** Override the global host (tests, iframes). */\n target?: InjectedHost | null;\n};\n\nconst readHost = (target: InjectedBitcoinDiscoveryOptions[\"target\"]): InjectedHost | null => {\n if (target !== undefined) {\n return target;\n }\n if (typeof window === \"undefined\") {\n return null;\n }\n // `window` carries the injected wallet globals (`unisat`, `okxwallet`,\n // etc.) as an untyped host object; `InjectedHost` names the four keys\n // butr probes, all optional and read defensively in `probeProviders`.\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion, anti-slop/no-chained-type-assertions -- untyped injected-wallet window globals\n return window as unknown as InjectedHost;\n};\n\nconst probeProviders = (host: InjectedHost): Array<WalletAdapter> => {\n const adapters: Array<WalletAdapter> = [];\n const seen = new Set<UnisatProvider | SatsConnectProvider>();\n\n if (host.unisat && !seen.has(host.unisat)) {\n seen.add(host.unisat);\n adapters.push(buildUnisatAdapter(\"injected:bitcoin:unisat\", \"Unisat\", host.unisat));\n }\n\n const okxBitcoin = host.okxwallet?.bitcoin;\n if (okxBitcoin && !seen.has(okxBitcoin)) {\n seen.add(okxBitcoin);\n adapters.push(buildUnisatAdapter(\"injected:bitcoin:okx\", \"OKX Wallet (Bitcoin)\", okxBitcoin));\n }\n\n const xverse = host.XverseProviders?.BitcoinProvider;\n if (xverse && !seen.has(xverse)) {\n seen.add(xverse);\n adapters.push(buildSatsConnectAdapter(\"injected:bitcoin:xverse\", \"Xverse\", xverse));\n }\n\n if (host.btc && !seen.has(host.btc)) {\n seen.add(host.btc);\n adapters.push(\n buildUnisatAdapter(\"injected:bitcoin:legacy\", \"Browser Bitcoin wallet\", host.btc),\n );\n }\n\n return adapters;\n};\n\n/**\n * Last-resort path for wallets that don't announce via Wallet Standard:\n * `window.unisat`, OKX, Xverse, `window.btc`. The `settleMs` wait lets\n * Wallet Standard announce first, else a wallet on both channels lists twice.\n */\nconst discoverInjectedBitcoinAdapter = (\n onAdapter: (adapter: WalletAdapter) => void,\n options: InjectedBitcoinDiscoveryOptions = {},\n): (() => void) => {\n const settleMs = options.settleMs ?? DEFAULT_SETTLE_MS;\n let cancelled = false;\n const timer = setTimeout(() => {\n if (cancelled) {\n return;\n }\n if (options.hasAnyWalletStandardAdapter?.() === true) {\n return;\n }\n const host = readHost(options.target);\n if (host === null) {\n return;\n }\n const adapters = probeProviders(host);\n for (const adapter of adapters) {\n onAdapter(adapter);\n }\n }, settleMs);\n\n return () => {\n cancelled = true;\n clearTimeout(timer);\n };\n};\n\nexport type { InjectedBitcoinDiscoveryOptions };\nexport { GENERIC_BITCOIN_ICON } from \"./icon\";\nexport { discoverInjectedBitcoinAdapter };\n","import type { PlatformDiscoverer } from \"@usebutr/core\";\n\nimport { discoverInjectedBitcoinAdapter } from \"./injected\";\nimport { discoverBitcoinAdapters } from \"./wallet-standard-adapter\";\n\n/**\n * Wallet Standard is primary; the legacy fallback covers sats-connect\n * (Xverse), `window.unisat`, OKX legacy and `window.btc`, and defers to\n * the primary channel via `hasAnyPrimaryAdapter`.\n */\nconst bitcoinDiscoverer: PlatformDiscoverer = {\n fallback: {\n subscribe: (onAdapter, opts) =>\n discoverInjectedBitcoinAdapter(onAdapter, {\n hasAnyWalletStandardAdapter: opts.hasAnyPrimaryAdapter,\n }),\n },\n subscribe: discoverBitcoinAdapters,\n};\n\nexport { bitcoinDiscoverer };\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usebutr/bitcoin",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Wallet Standard + injected (sats-connect / Unisat / OKX) wallet discovery and adapters for butr (Bitcoin).",
5
5
  "license": "MIT",
6
6
  "author": "Pedro Filho <pedro@filho.me>",
@@ -25,8 +25,8 @@
25
25
  "access": "public"
26
26
  },
27
27
  "dependencies": {
28
- "@usebutr/core": "1.1.0",
29
- "@usebutr/wallet-standard-shared": "0.4.1"
28
+ "@usebutr/core": "2.0.0",
29
+ "@usebutr/wallet-standard-shared": "0.5.0"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@wallet-standard/app": "^1.1.1",
@@ -47,7 +47,6 @@
47
47
  "build": "tsdown",
48
48
  "clean": "rm -rf dist",
49
49
  "dev": "tsdown --watch",
50
- "lint": "oxlint src",
51
50
  "test": "vitest run --passWithNoTests",
52
51
  "test:coverage": "vitest run --coverage --passWithNoTests",
53
52
  "typecheck": "tsc -p tsconfig.json --noEmit"