@usebutr/bitcoin 0.2.5 → 0.2.7
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 +23 -36
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +107 -212
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { ChainBase, PlatformDiscoverer, WalletAdapter, WalletCapabilities } from "@usebutr/core";
|
|
2
1
|
import { WalletStandardWallet, WalletStandardWalletAccount } from "@usebutr/wallet-standard-shared";
|
|
3
|
-
|
|
2
|
+
import { ChainBase, PlatformDiscoverer, WalletAdapter, WalletCapabilities } from "@usebutr/core";
|
|
4
3
|
//#region src/wallet-standard-types.d.ts
|
|
5
4
|
type BitcoinSignMessageInput = {
|
|
6
5
|
account: WalletStandardWalletAccount;
|
|
@@ -14,8 +13,10 @@ type BitcoinSignMessageFeature = {
|
|
|
14
13
|
signMessage: (input: BitcoinSignMessageInput) => Promise<BitcoinSignMessageOutput>;
|
|
15
14
|
};
|
|
16
15
|
type BitcoinSignPsbtInput = {
|
|
17
|
-
account: WalletStandardWalletAccount;
|
|
18
|
-
|
|
16
|
+
account: WalletStandardWalletAccount;
|
|
17
|
+
/** Network chain id (`bip122:…`). */
|
|
18
|
+
chain: string;
|
|
19
|
+
/** Base64- or hex-encoded PSBT bytes. */
|
|
19
20
|
psbt: Uint8Array;
|
|
20
21
|
/** Optional input indexes to sign; omit to sign every input the wallet
|
|
21
22
|
* controls. */
|
|
@@ -36,7 +37,8 @@ type BitcoinSendTransferInput = {
|
|
|
36
37
|
recipient: string;
|
|
37
38
|
};
|
|
38
39
|
type BitcoinSendTransferOutput = {
|
|
39
|
-
/** Broadcast txid. */
|
|
40
|
+
/** Broadcast txid. */
|
|
41
|
+
txid: string;
|
|
40
42
|
};
|
|
41
43
|
type BitcoinSendTransferFeature = {
|
|
42
44
|
sendTransfer: (input: BitcoinSendTransferInput) => Promise<BitcoinSendTransferOutput>;
|
|
@@ -60,10 +62,10 @@ declare const slugify: (name: string) => string;
|
|
|
60
62
|
* butr's scope.
|
|
61
63
|
* - `getTransactionReceipt` returns `"Pending"` for the same reason.
|
|
62
64
|
* - `sendTx` expects the consumer to pass `{ amount: bigint, recipient: string }`
|
|
63
|
-
* via butr's `unknown` tx parameter
|
|
65
|
+
* via butr's `unknown` tx parameter; the simplest mental model that
|
|
64
66
|
* maps onto `bitcoin:sendTransfer`. PSBT-shaped flows go through
|
|
65
67
|
* `signTransaction`.
|
|
66
|
-
* - `signTransaction` expects a `Uint8Array` PSBT (raw bytes)
|
|
68
|
+
* - `signTransaction` expects a `Uint8Array` PSBT (raw bytes); the
|
|
67
69
|
* same shape `bitcoin:signPsbt` accepts. Consumers building with
|
|
68
70
|
* `bitcoinjs-lib` get bytes via `psbt.toBuffer()`.
|
|
69
71
|
* - `switchChain` is local-state-only; the capability flag is `false`
|
|
@@ -74,7 +76,6 @@ declare const buildBitcoinAdapter: (wallet: WalletStandardWallet,
|
|
|
74
76
|
/** Optional. Called with a function that pushes a synthetic
|
|
75
77
|
* `disconnected` event to all current subscribers. The discovery
|
|
76
78
|
* layer invokes it on Wallet Standard `unregister`. */
|
|
77
|
-
|
|
78
79
|
registerDisconnector?: (emit: () => void) => void) => WalletAdapter | null;
|
|
79
80
|
//#endregion
|
|
80
81
|
//#region src/wallet-standard.d.ts
|
|
@@ -106,7 +107,7 @@ declare const discoverBitcoinAdapters: (onAdapter: (adapter: WalletAdapter) => v
|
|
|
106
107
|
* - Signet: `bip122:00000008819873e925422c1ff0f99f7c`
|
|
107
108
|
*
|
|
108
109
|
* Bitcoin has no per-call "switch chain" RPC across the major wallets,
|
|
109
|
-
* so butr's adapter treats chain as a wallet-side configuration
|
|
110
|
+
* so butr's adapter treats chain as a wallet-side configuration;
|
|
110
111
|
* `switchChain` updates local state to influence subsequent calls (same
|
|
111
112
|
* posture as SVM). Bitcoin's `switchChain` capability is `false` by
|
|
112
113
|
* default; consumers building on top can opt in if their wallet exposes
|
|
@@ -157,13 +158,15 @@ type BitcoinCapabilityInput = {
|
|
|
157
158
|
* message-signing oriented, not auth).
|
|
158
159
|
* - `subscribe = events present`. Most Bitcoin wallets don't emit
|
|
159
160
|
* change events portably; capabilities admits the truth.
|
|
160
|
-
* - `switchChain =
|
|
161
|
-
* Bitcoin wallets
|
|
161
|
+
* - `switchChain = chainCount > 1`. No portable switch RPC across
|
|
162
|
+
* Bitcoin wallets (Xverse/Unisat/Phantom each pick their own
|
|
163
|
+
* network), so the switch is local-state-only and advertised only
|
|
164
|
+
* when the wallet exposes more than one chain to switch between.
|
|
162
165
|
*/
|
|
163
166
|
declare const resolveBitcoinCapabilities: (input: BitcoinCapabilityInput) => WalletCapabilities;
|
|
164
167
|
//#endregion
|
|
165
168
|
//#region src/injected/sats-connect.d.ts
|
|
166
|
-
/** sats-connect (Xverse) shape
|
|
169
|
+
/** sats-connect (Xverse) shape; a JSON-RPC-ish `request(method, params)`. */
|
|
167
170
|
type SatsConnectProvider = {
|
|
168
171
|
request: (method: string, params?: Record<string, unknown>) => Promise<{
|
|
169
172
|
error?: {
|
|
@@ -172,15 +175,6 @@ type SatsConnectProvider = {
|
|
|
172
175
|
result?: unknown;
|
|
173
176
|
}>;
|
|
174
177
|
};
|
|
175
|
-
/**
|
|
176
|
-
* Wrap an Xverse-style `BitcoinProvider` (sats-connect) into a butr
|
|
177
|
-
* `WalletAdapter`. sats-connect routes everything through a single
|
|
178
|
-
* `request(method, params)` RPC; we shim each butr capability onto the
|
|
179
|
-
* corresponding sats-connect method.
|
|
180
|
-
*
|
|
181
|
-
* Tested against Xverse's `getAccounts`, `signMessage`, `signPsbt`,
|
|
182
|
-
* `sendTransfer` methods.
|
|
183
|
-
*/
|
|
184
178
|
//#endregion
|
|
185
179
|
//#region src/injected/unisat.d.ts
|
|
186
180
|
/** UniSat-style provider: a single object on `window.unisat` with the
|
|
@@ -197,24 +191,16 @@ type UnisatProvider = {
|
|
|
197
191
|
signMessage: (message: string, type?: "ecdsa" | "bip322-simple") => Promise<string>;
|
|
198
192
|
signPsbt: (psbtHex: string, options?: Record<string, unknown>) => Promise<string>;
|
|
199
193
|
};
|
|
200
|
-
/**
|
|
201
|
-
* Wrap a UniSat-style provider (`window.unisat`, `window.okxwallet.bitcoin`,
|
|
202
|
-
* `window.btc`) into a butr `WalletAdapter`.
|
|
203
|
-
*
|
|
204
|
-
* The UniSat shape is consistent enough that one adapter handles every
|
|
205
|
-
* derivative. Differences (network names, `sendBitcoin` presence) are
|
|
206
|
-
* gated by feature detection per call.
|
|
207
|
-
*/
|
|
208
194
|
//#endregion
|
|
209
195
|
//#region src/injected/icon.d.ts
|
|
210
196
|
/**
|
|
211
|
-
* Generic Bitcoin wallet icon
|
|
197
|
+
* Generic Bitcoin wallet icon, same purse SVG style as the EVM
|
|
212
198
|
* package, recoloured. Inline so the picker doesn't flash.
|
|
213
199
|
*
|
|
214
200
|
* Lives in its own file because both injected adapters (UniSat-shape
|
|
215
201
|
* and sats-connect) need the same fallback when the wallet doesn't
|
|
216
202
|
* advertise its own icon, and there's no other "shared" thing they
|
|
217
|
-
* both want
|
|
203
|
+
* both want; a one-line module beats a grab-bag.
|
|
218
204
|
*/
|
|
219
205
|
declare const GENERIC_BITCOIN_ICON = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI2Y3OTMxYSIgc3Ryb2tlPSJub25lIj48Y2lyY2xlIGN4PSIxMiIgY3k9IjEyIiByPSIxMCIvPjx0ZXh0IHg9IjEyIiB5PSIxNyIgZm9udC1mYW1pbHk9InNlcmlmIiBmb250LXNpemU9IjE0IiBmb250LXdlaWdodD0iYm9sZCIgZmlsbD0iI2ZmZiIgdGV4dC1hbmNob3I9Im1pZGRsZSI+QjwvdGV4dD48L3N2Zz4=";
|
|
220
206
|
//#endregion
|
|
@@ -235,7 +221,8 @@ type InjectedBitcoinDiscoveryOptions = {
|
|
|
235
221
|
hasAnyWalletStandardAdapter?: () => boolean;
|
|
236
222
|
/** Settle deadline in ms before the fallback fires. Same default and
|
|
237
223
|
* intent as the EVM injected fallback. */
|
|
238
|
-
settleMs?: number;
|
|
224
|
+
settleMs?: number;
|
|
225
|
+
/** Override the global host (tests, iframes). */
|
|
239
226
|
target?: InjectedHost | null;
|
|
240
227
|
};
|
|
241
228
|
/**
|
|
@@ -267,15 +254,15 @@ declare const bitcoinDiscoverer: PlatformDiscoverer;
|
|
|
267
254
|
//#region src/signer-augmentation.d.ts
|
|
268
255
|
/**
|
|
269
256
|
* Augment `@usebutr/core`'s `SignerForPlatform` registry with the Bitcoin
|
|
270
|
-
* entry. Bitcoin's signer surface is fragmented
|
|
257
|
+
* entry. Bitcoin's signer surface is fragmented: `getSigner()` may
|
|
271
258
|
* return one of three shapes depending on which adapter discovered the
|
|
272
259
|
* wallet:
|
|
273
260
|
*
|
|
274
|
-
* - `WalletStandardWallet
|
|
261
|
+
* - `WalletStandardWallet`: for Phantom, Magic Eden, Leather, OKX
|
|
275
262
|
* (modern Wallet Standard path).
|
|
276
|
-
* - A UniSat-shaped provider
|
|
263
|
+
* - A UniSat-shaped provider; for Unisat, OKX legacy, `window.btc`
|
|
277
264
|
* (injected path).
|
|
278
|
-
* - A sats-connect `BitcoinProvider
|
|
265
|
+
* - A sats-connect `BitcoinProvider`; for Xverse (sats-connect path).
|
|
279
266
|
*
|
|
280
267
|
* The registry surfaces the union honestly so consumers know they need
|
|
281
268
|
* to runtime-narrow.
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/wallet-standard-types.ts","../src/wallet-standard-adapter.ts","../src/wallet-standard.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":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/wallet-standard-types.ts","../src/wallet-standard-adapter.ts","../src/wallet-standard.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;;;;cC7CvD,UAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;cA4BX,sBACJ,QAAQ,sBAIR;;;;AAAA,wBAAwB,8BACvB;;;;;;;;;;;;;;;;;cClCG,0BAA2B,YAAY,SAAS;;;;;;;;;;;;;;;;;;;;cCAhD;;;;;;;;;;;;;;;;;;;;cAqBA,qBAAqB,cAAc;;;KCtCpC;EACH;EACA;IACE;IACA;IACA;IACA;;;;;;;;;;;;;;;;;;;;;cAsBE,6BAA8B,OAAO,2BAAyB;;;;KCrB/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"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { buildAccount, createWalletStandardCore, discoverWalletStandard, getFeature, slugify as slugify$1 } from "@usebutr/wallet-standard-shared";
|
|
2
|
+
import { base64ToBytes, bytesToBase64, bytesToHex, hexToBytes } from "@usebutr/core";
|
|
3
3
|
|
|
4
4
|
//#region src/capabilities.ts
|
|
5
5
|
/**
|
|
@@ -15,8 +15,10 @@ import { buildAccount, discoverWalletStandard, getFeature, pickAccountByAddress,
|
|
|
15
15
|
* message-signing oriented, not auth).
|
|
16
16
|
* - `subscribe = events present`. Most Bitcoin wallets don't emit
|
|
17
17
|
* change events portably; capabilities admits the truth.
|
|
18
|
-
* - `switchChain =
|
|
19
|
-
* Bitcoin wallets
|
|
18
|
+
* - `switchChain = chainCount > 1`. No portable switch RPC across
|
|
19
|
+
* Bitcoin wallets (Xverse/Unisat/Phantom each pick their own
|
|
20
|
+
* network), so the switch is local-state-only and advertised only
|
|
21
|
+
* when the wallet exposes more than one chain to switch between.
|
|
20
22
|
*/
|
|
21
23
|
const resolveBitcoinCapabilities = (input) => ({
|
|
22
24
|
getBalance: false,
|
|
@@ -37,18 +39,6 @@ const BITCOIN_PREFIX = "bip122:";
|
|
|
37
39
|
const BITCOIN_DECIMALS = 8;
|
|
38
40
|
const BITCOIN_MAINNET_ID = "bip122:000000000019d6689c085ae165831e93";
|
|
39
41
|
const slugify = (name) => slugify$1("btc", name);
|
|
40
|
-
const pickBitcoinChain = (wallet) => {
|
|
41
|
-
const mainnet = wallet.chains.find((c) => c === BITCOIN_MAINNET_ID);
|
|
42
|
-
if (mainnet) return mainnet;
|
|
43
|
-
return wallet.chains.find((c) => c.startsWith(BITCOIN_PREFIX)) ?? null;
|
|
44
|
-
};
|
|
45
|
-
const buildBitcoinChain = (chainId, walletName) => ({
|
|
46
|
-
id: chainId,
|
|
47
|
-
name: walletName,
|
|
48
|
-
namespace: "bip122",
|
|
49
|
-
reference: chainId.slice(7)
|
|
50
|
-
});
|
|
51
|
-
const buildBitcoinAccount = (address, chain) => buildAccount(address, chain);
|
|
52
42
|
/**
|
|
53
43
|
* Adapt a Bitcoin Wallet Standard `Wallet` into a butr `WalletAdapter`.
|
|
54
44
|
*
|
|
@@ -65,10 +55,10 @@ const buildBitcoinAccount = (address, chain) => buildAccount(address, chain);
|
|
|
65
55
|
* butr's scope.
|
|
66
56
|
* - `getTransactionReceipt` returns `"Pending"` for the same reason.
|
|
67
57
|
* - `sendTx` expects the consumer to pass `{ amount: bigint, recipient: string }`
|
|
68
|
-
* via butr's `unknown` tx parameter
|
|
58
|
+
* via butr's `unknown` tx parameter; the simplest mental model that
|
|
69
59
|
* maps onto `bitcoin:sendTransfer`. PSBT-shaped flows go through
|
|
70
60
|
* `signTransaction`.
|
|
71
|
-
* - `signTransaction` expects a `Uint8Array` PSBT (raw bytes)
|
|
61
|
+
* - `signTransaction` expects a `Uint8Array` PSBT (raw bytes); the
|
|
72
62
|
* same shape `bitcoin:signPsbt` accepts. Consumers building with
|
|
73
63
|
* `bitcoinjs-lib` get bytes via `psbt.toBuffer()`.
|
|
74
64
|
* - `switchChain` is local-state-only; the capability flag is `false`
|
|
@@ -76,106 +66,63 @@ const buildBitcoinAccount = (address, chain) => buildAccount(address, chain);
|
|
|
76
66
|
* primitive (none today, portably) would need their own adapter.
|
|
77
67
|
*/
|
|
78
68
|
const buildBitcoinAdapter = (wallet, registerDisconnector) => {
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
69
|
+
const core = createWalletStandardCore({
|
|
70
|
+
chainPrefix: BITCOIN_PREFIX,
|
|
71
|
+
id: slugify(wallet.name),
|
|
72
|
+
label: "Bitcoin",
|
|
73
|
+
namespace: "bip122",
|
|
74
|
+
platform: "Bitcoin",
|
|
75
|
+
preferredChainIds: [BITCOIN_MAINNET_ID],
|
|
76
|
+
registerDisconnector,
|
|
77
|
+
trackChainChanges: true,
|
|
78
|
+
wallet
|
|
79
|
+
});
|
|
80
|
+
if (core === null) return null;
|
|
85
81
|
const signMessage = getFeature(wallet, "bitcoin:signMessage");
|
|
86
82
|
const signPsbt = getFeature(wallet, "bitcoin:signPsbt");
|
|
87
83
|
const sendTransfer = getFeature(wallet, "bitcoin:sendTransfer");
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
account: first,
|
|
99
|
-
accounts: built,
|
|
100
|
-
type: "accountChanged"
|
|
101
|
-
});
|
|
84
|
+
const sendTransferTx = async (tx, account) => {
|
|
85
|
+
if (sendTransfer === void 0) throw new Error(`Wallet ${wallet.name} does not advertise bitcoin:sendTransfer`);
|
|
86
|
+
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");
|
|
87
|
+
const { amount, recipient } = tx;
|
|
88
|
+
return (await sendTransfer.sendTransfer({
|
|
89
|
+
account: core.resolveAccount(account),
|
|
90
|
+
amount,
|
|
91
|
+
chain: core.currentChainId(),
|
|
92
|
+
recipient
|
|
93
|
+
})).txid;
|
|
102
94
|
};
|
|
103
|
-
registerDisconnector?.(() => {
|
|
104
|
-
for (const listener of listeners) listener({ type: "disconnected" });
|
|
105
|
-
});
|
|
106
95
|
return {
|
|
96
|
+
...core,
|
|
107
97
|
capabilities: resolveBitcoinCapabilities({
|
|
108
|
-
chainCount:
|
|
98
|
+
chainCount: core.chainCount,
|
|
109
99
|
features: {
|
|
110
|
-
events:
|
|
100
|
+
events: core.hasEvents,
|
|
111
101
|
sendTransfer: Boolean(sendTransfer),
|
|
112
102
|
signMessage: Boolean(signMessage),
|
|
113
103
|
signPsbt: Boolean(signPsbt)
|
|
114
104
|
}
|
|
115
105
|
}),
|
|
116
106
|
chainPlatform: "bitcoin",
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
logWarn("[butr] Bitcoin Wallet Standard disconnect threw:", error);
|
|
125
|
-
}
|
|
126
|
-
},
|
|
127
|
-
getAccount() {
|
|
128
|
-
const address = pickFirstAddress(wallet.accounts);
|
|
129
|
-
if (!address) return Promise.resolve(null);
|
|
130
|
-
return Promise.resolve(buildBitcoinAccount(address, currentChain()));
|
|
131
|
-
},
|
|
132
|
-
getAccounts() {
|
|
133
|
-
const chain = currentChain();
|
|
134
|
-
return Promise.resolve(wallet.accounts.map((a) => buildBitcoinAccount(a.address, chain)));
|
|
135
|
-
},
|
|
136
|
-
getBalance() {
|
|
137
|
-
return Promise.resolve({
|
|
138
|
-
decimals: BITCOIN_DECIMALS,
|
|
139
|
-
formatted: "0",
|
|
140
|
-
symbol: "BTC",
|
|
141
|
-
value: 0n
|
|
142
|
-
});
|
|
143
|
-
},
|
|
144
|
-
getSigner() {
|
|
145
|
-
return Promise.resolve(wallet);
|
|
146
|
-
},
|
|
147
|
-
getTransactionReceipt() {
|
|
148
|
-
return Promise.resolve({ status: "Pending" });
|
|
149
|
-
},
|
|
150
|
-
icon: sanitizeIcon(wallet.icon),
|
|
151
|
-
id: slugify(wallet.name),
|
|
152
|
-
name: wallet.name,
|
|
107
|
+
getBalance: () => Promise.resolve({
|
|
108
|
+
decimals: BITCOIN_DECIMALS,
|
|
109
|
+
formatted: "0",
|
|
110
|
+
symbol: "BTC",
|
|
111
|
+
value: 0n
|
|
112
|
+
}),
|
|
113
|
+
getTransactionReceipt: () => Promise.resolve({ status: "Pending" }),
|
|
153
114
|
async requestAccounts() {
|
|
154
|
-
await
|
|
155
|
-
},
|
|
156
|
-
async sendTx(tx, account) {
|
|
157
|
-
if (!sendTransfer) throw new Error(`Wallet ${wallet.name} does not advertise bitcoin:sendTransfer`);
|
|
158
|
-
if (!tx || typeof tx !== "object" || typeof tx.amount !== "bigint" || typeof tx.recipient !== "string") throw new TypeError("Bitcoin sendTx expects { amount: bigint, recipient: string } — pass the recipient address and an amount in satoshis");
|
|
159
|
-
const { amount, recipient } = tx;
|
|
160
|
-
const wsAccount = account ? pickAccountByAddress(wallet.accounts, account.walletAddress) : wallet.accounts[0] ?? null;
|
|
161
|
-
if (!wsAccount) throw new Error("No connected account");
|
|
162
|
-
return (await sendTransfer.sendTransfer({
|
|
163
|
-
account: wsAccount,
|
|
164
|
-
amount,
|
|
165
|
-
chain: currentChainId,
|
|
166
|
-
recipient
|
|
167
|
-
})).txid;
|
|
115
|
+
await core.connect();
|
|
168
116
|
},
|
|
169
|
-
|
|
117
|
+
sendTx: (tx, account) => sendTransferTx(tx, account),
|
|
118
|
+
sendTxToChain: (tx, _targetChainId, account, cb) => {
|
|
170
119
|
cb?.();
|
|
171
|
-
return
|
|
120
|
+
return sendTransferTx(tx, account);
|
|
172
121
|
},
|
|
173
122
|
async signMessage(msg, account) {
|
|
174
|
-
if (
|
|
175
|
-
const wsAccount = account ? pickAccountByAddress(wallet.accounts, account.walletAddress) : wallet.accounts[0] ?? null;
|
|
176
|
-
if (!wsAccount) throw new Error("No connected account");
|
|
123
|
+
if (signMessage === void 0) throw new Error(`Wallet ${wallet.name} does not advertise bitcoin:signMessage`);
|
|
177
124
|
const output = await signMessage.signMessage({
|
|
178
|
-
account:
|
|
125
|
+
account: core.resolveAccount(account),
|
|
179
126
|
message: msg
|
|
180
127
|
});
|
|
181
128
|
return {
|
|
@@ -183,57 +130,15 @@ const buildBitcoinAdapter = (wallet, registerDisconnector) => {
|
|
|
183
130
|
signedMessage: output.signedMessage
|
|
184
131
|
};
|
|
185
132
|
},
|
|
186
|
-
...signPsbt ? { async signTransaction(tx, account) {
|
|
187
|
-
const wsAccount =
|
|
188
|
-
if (!wsAccount) throw new Error("No connected account");
|
|
133
|
+
...signPsbt === void 0 ? {} : { async signTransaction(tx, account) {
|
|
134
|
+
const wsAccount = core.resolveAccount(account);
|
|
189
135
|
if (!(tx instanceof Uint8Array)) throw new TypeError("Bitcoin signTransaction expects a PSBT as Uint8Array (e.g. psbt.toBuffer())");
|
|
190
136
|
return (await signPsbt.signPsbt({
|
|
191
137
|
account: wsAccount,
|
|
192
|
-
chain: currentChainId,
|
|
138
|
+
chain: core.currentChainId(),
|
|
193
139
|
psbt: tx
|
|
194
140
|
})).signedPsbt;
|
|
195
|
-
} }
|
|
196
|
-
subscribe(listener) {
|
|
197
|
-
listeners.add(listener);
|
|
198
|
-
let unsubWallet = null;
|
|
199
|
-
if (events) {
|
|
200
|
-
const unsub = events.on("change", (changes) => {
|
|
201
|
-
if (changes.chains) {
|
|
202
|
-
const next = changes.chains.find((c) => c === BITCOIN_MAINNET_ID) ?? changes.chains.find((c) => c.startsWith(BITCOIN_PREFIX));
|
|
203
|
-
if (next) currentChainId = next;
|
|
204
|
-
}
|
|
205
|
-
if (changes.accounts) {
|
|
206
|
-
if (changes.accounts.length === 0) {
|
|
207
|
-
listener({ type: "disconnected" });
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
const chain = currentChain();
|
|
211
|
-
const built = changes.accounts.map((a) => buildBitcoinAccount(a.address, chain));
|
|
212
|
-
const first = built[0];
|
|
213
|
-
if (!first) return;
|
|
214
|
-
listener({
|
|
215
|
-
account: first,
|
|
216
|
-
accounts: built,
|
|
217
|
-
type: "accountChanged"
|
|
218
|
-
});
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
if (changes.chains) notifyAccountChanged();
|
|
222
|
-
});
|
|
223
|
-
unsubWallet = () => unsub();
|
|
224
|
-
}
|
|
225
|
-
return () => {
|
|
226
|
-
listeners.delete(listener);
|
|
227
|
-
unsubWallet?.();
|
|
228
|
-
};
|
|
229
|
-
},
|
|
230
|
-
switchChain(chain) {
|
|
231
|
-
if (chain.namespace !== "bip122") throw new Error(`Bitcoin adapter received non-Bitcoin chain "${chain.id}". Pass a chain with namespace "bip122".`);
|
|
232
|
-
if (!wallet.chains.includes(chain.id)) throw new Error(`Wallet ${wallet.name} does not advertise chain "${chain.id}". Available: ${wallet.chains.join(", ")}`);
|
|
233
|
-
currentChainId = chain.id;
|
|
234
|
-
notifyAccountChanged();
|
|
235
|
-
return Promise.resolve();
|
|
236
|
-
}
|
|
141
|
+
} }
|
|
237
142
|
};
|
|
238
143
|
};
|
|
239
144
|
|
|
@@ -268,7 +173,7 @@ const discoverBitcoinAdapters = (onAdapter) => discoverWalletStandard(onAdapter,
|
|
|
268
173
|
* - Signet: `bip122:00000008819873e925422c1ff0f99f7c`
|
|
269
174
|
*
|
|
270
175
|
* Bitcoin has no per-call "switch chain" RPC across the major wallets,
|
|
271
|
-
* so butr's adapter treats chain as a wallet-side configuration
|
|
176
|
+
* so butr's adapter treats chain as a wallet-side configuration;
|
|
272
177
|
* `switchChain` updates local state to influence subsequent calls (same
|
|
273
178
|
* posture as SVM). Bitcoin's `switchChain` capability is `false` by
|
|
274
179
|
* default; consumers building on top can opt in if their wallet exposes
|
|
@@ -299,13 +204,13 @@ const BITCOIN_CHAINS_LIST = Object.values(BITCOIN_CHAINS);
|
|
|
299
204
|
//#endregion
|
|
300
205
|
//#region src/injected/icon.ts
|
|
301
206
|
/**
|
|
302
|
-
* Generic Bitcoin wallet icon
|
|
207
|
+
* Generic Bitcoin wallet icon, same purse SVG style as the EVM
|
|
303
208
|
* package, recoloured. Inline so the picker doesn't flash.
|
|
304
209
|
*
|
|
305
210
|
* Lives in its own file because both injected adapters (UniSat-shape
|
|
306
211
|
* and sats-connect) need the same fallback when the wallet doesn't
|
|
307
212
|
* advertise its own icon, and there's no other "shared" thing they
|
|
308
|
-
* both want
|
|
213
|
+
* both want; a one-line module beats a grab-bag.
|
|
309
214
|
*/
|
|
310
215
|
const GENERIC_BITCOIN_ICON = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI2Y3OTMxYSIgc3Ryb2tlPSJub25lIj48Y2lyY2xlIGN4PSIxMiIgY3k9IjEyIiByPSIxMCIvPjx0ZXh0IHg9IjEyIiB5PSIxNyIgZm9udC1mYW1pbHk9InNlcmlmIiBmb250LXNpemU9IjE0IiBmb250LXdlaWdodD0iYm9sZCIgZmlsbD0iI2ZmZiIgdGV4dC1hbmNob3I9Im1pZGRsZSI+QjwvdGV4dD48L3N2Zz4=";
|
|
311
216
|
|
|
@@ -345,65 +250,60 @@ const buildSatsConnectAdapter = (id, name, provider) => {
|
|
|
345
250
|
message: "Connect to butr",
|
|
346
251
|
purposes: ["payment", "ordinals"]
|
|
347
252
|
});
|
|
348
|
-
if (
|
|
253
|
+
if (result?.addresses === void 0) return [];
|
|
349
254
|
const addresses = result.addresses.map((a) => a.address);
|
|
350
255
|
cachedAccounts = addresses;
|
|
351
256
|
return addresses;
|
|
352
257
|
};
|
|
258
|
+
const sendTransferTx = async (tx) => {
|
|
259
|
+
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");
|
|
260
|
+
const { amount, recipient } = tx;
|
|
261
|
+
return (await callRequest("sendTransfer", { recipients: [{
|
|
262
|
+
address: recipient,
|
|
263
|
+
amount: amount.toString()
|
|
264
|
+
}] })).txid;
|
|
265
|
+
};
|
|
353
266
|
return {
|
|
354
267
|
capabilities: CAPS_SATS_CONNECT,
|
|
355
268
|
chainPlatform: "bitcoin",
|
|
356
269
|
async connect() {
|
|
357
270
|
await loadAccounts();
|
|
358
271
|
},
|
|
359
|
-
disconnect() {
|
|
272
|
+
disconnect: () => {
|
|
360
273
|
cachedAccounts = [];
|
|
361
274
|
return Promise.resolve();
|
|
362
275
|
},
|
|
363
276
|
async getAccount() {
|
|
364
277
|
const first = cachedAccounts[0];
|
|
365
|
-
if (first) return buildAccount(first, chain);
|
|
278
|
+
if (first !== void 0) return buildAccount(first, chain);
|
|
366
279
|
const firstFresh = (await loadAccounts())[0];
|
|
367
|
-
return firstFresh ? buildAccount(firstFresh, chain)
|
|
280
|
+
return firstFresh === void 0 ? null : buildAccount(firstFresh, chain);
|
|
368
281
|
},
|
|
369
282
|
async getAccounts() {
|
|
370
283
|
return (cachedAccounts.length > 0 ? cachedAccounts : await loadAccounts()).map((a) => buildAccount(a, chain));
|
|
371
284
|
},
|
|
372
|
-
getBalance() {
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
},
|
|
380
|
-
getSigner() {
|
|
381
|
-
return Promise.resolve(provider);
|
|
382
|
-
},
|
|
383
|
-
getTransactionReceipt() {
|
|
384
|
-
return Promise.resolve({ status: "Pending" });
|
|
385
|
-
},
|
|
285
|
+
getBalance: () => Promise.resolve({
|
|
286
|
+
decimals: 8,
|
|
287
|
+
formatted: "0",
|
|
288
|
+
symbol: "BTC",
|
|
289
|
+
value: 0n
|
|
290
|
+
}),
|
|
291
|
+
getSigner: () => Promise.resolve(provider),
|
|
292
|
+
getTransactionReceipt: () => Promise.resolve({ status: "Pending" }),
|
|
386
293
|
icon: GENERIC_BITCOIN_ICON,
|
|
387
294
|
id,
|
|
388
295
|
name,
|
|
389
296
|
async requestAccounts() {
|
|
390
297
|
await loadAccounts();
|
|
391
298
|
},
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
const { amount, recipient } = tx;
|
|
395
|
-
return (await callRequest("sendTransfer", { recipients: [{
|
|
396
|
-
address: recipient,
|
|
397
|
-
amount: amount.toString()
|
|
398
|
-
}] })).txid;
|
|
399
|
-
},
|
|
400
|
-
sendTxToChain(tx, _targetChainId, _account, cb) {
|
|
299
|
+
sendTx: (tx) => sendTransferTx(tx),
|
|
300
|
+
sendTxToChain: (tx, _targetChainId, _account, cb) => {
|
|
401
301
|
cb?.();
|
|
402
|
-
return
|
|
302
|
+
return sendTransferTx(tx);
|
|
403
303
|
},
|
|
404
304
|
async signMessage(msg) {
|
|
405
305
|
const address = cachedAccounts[0];
|
|
406
|
-
if (
|
|
306
|
+
if (address === void 0) throw new Error("No connected account");
|
|
407
307
|
return {
|
|
408
308
|
signature: base64ToBytes((await callRequest("signMessage", {
|
|
409
309
|
address,
|
|
@@ -416,7 +316,7 @@ const buildSatsConnectAdapter = (id, name, provider) => {
|
|
|
416
316
|
if (!(tx instanceof Uint8Array)) throw new TypeError("Bitcoin signTransaction expects a PSBT as Uint8Array (e.g. psbt.toBuffer())");
|
|
417
317
|
return base64ToBytes((await callRequest("signPsbt", { psbt: bytesToBase64(tx) })).psbt);
|
|
418
318
|
},
|
|
419
|
-
switchChain(target) {
|
|
319
|
+
switchChain: (target) => {
|
|
420
320
|
if (target.namespace !== "bip122") throw new Error(`Bitcoin adapter received non-Bitcoin chain "${target.id}". Pass a chain with namespace "bip122".`);
|
|
421
321
|
chain = target;
|
|
422
322
|
return Promise.resolve();
|
|
@@ -446,9 +346,16 @@ const CAPS_UNISAT = {
|
|
|
446
346
|
* derivative. Differences (network names, `sendBitcoin` presence) are
|
|
447
347
|
* gated by feature detection per call.
|
|
448
348
|
*/
|
|
349
|
+
const toStringArray = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
449
350
|
const buildUnisatAdapter = (id, name, provider) => {
|
|
450
351
|
let chain = BITCOIN_CHAINS.mainnet;
|
|
451
352
|
const listenersSet = /* @__PURE__ */ new Set();
|
|
353
|
+
const sendBitcoinTx = (tx) => {
|
|
354
|
+
if (typeof provider.sendBitcoin !== "function") throw new TypeError(`Wallet ${name} does not expose sendBitcoin`);
|
|
355
|
+
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");
|
|
356
|
+
const { amount, recipient } = tx;
|
|
357
|
+
return provider.sendBitcoin(recipient, Number(amount));
|
|
358
|
+
};
|
|
452
359
|
const refreshChain = async () => {
|
|
453
360
|
if (typeof provider.getNetwork !== "function") return;
|
|
454
361
|
try {
|
|
@@ -462,7 +369,7 @@ const buildUnisatAdapter = (id, name, provider) => {
|
|
|
462
369
|
capabilities: CAPS_UNISAT,
|
|
463
370
|
chainPlatform: "bitcoin",
|
|
464
371
|
async connect(opts) {
|
|
465
|
-
if (opts?.silent) {
|
|
372
|
+
if (opts?.silent === true) {
|
|
466
373
|
if ((await provider.getAccounts()).length === 0) throw new Error("No authorized accounts for silent reconnect");
|
|
467
374
|
await refreshChain();
|
|
468
375
|
return;
|
|
@@ -470,12 +377,10 @@ const buildUnisatAdapter = (id, name, provider) => {
|
|
|
470
377
|
await provider.requestAccounts();
|
|
471
378
|
await refreshChain();
|
|
472
379
|
},
|
|
473
|
-
disconnect()
|
|
474
|
-
return Promise.resolve();
|
|
475
|
-
},
|
|
380
|
+
disconnect: () => Promise.resolve(),
|
|
476
381
|
async getAccount() {
|
|
477
382
|
const first = (await provider.getAccounts())[0];
|
|
478
|
-
if (
|
|
383
|
+
if (first === void 0) return null;
|
|
479
384
|
await refreshChain();
|
|
480
385
|
return buildAccount(first, chain);
|
|
481
386
|
},
|
|
@@ -485,20 +390,14 @@ const buildUnisatAdapter = (id, name, provider) => {
|
|
|
485
390
|
await refreshChain();
|
|
486
391
|
return accounts.map((a) => buildAccount(a, chain));
|
|
487
392
|
},
|
|
488
|
-
getBalance() {
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
},
|
|
496
|
-
getSigner() {
|
|
497
|
-
return Promise.resolve(provider);
|
|
498
|
-
},
|
|
499
|
-
getTransactionReceipt() {
|
|
500
|
-
return Promise.resolve({ status: "Pending" });
|
|
501
|
-
},
|
|
393
|
+
getBalance: () => Promise.resolve({
|
|
394
|
+
decimals: 8,
|
|
395
|
+
formatted: "0",
|
|
396
|
+
symbol: "BTC",
|
|
397
|
+
value: 0n
|
|
398
|
+
}),
|
|
399
|
+
getSigner: () => Promise.resolve(provider),
|
|
400
|
+
getTransactionReceipt: () => Promise.resolve({ status: "Pending" }),
|
|
502
401
|
icon: GENERIC_BITCOIN_ICON,
|
|
503
402
|
id,
|
|
504
403
|
name,
|
|
@@ -506,16 +405,10 @@ const buildUnisatAdapter = (id, name, provider) => {
|
|
|
506
405
|
await provider.requestAccounts();
|
|
507
406
|
await refreshChain();
|
|
508
407
|
},
|
|
509
|
-
sendTx(tx)
|
|
510
|
-
|
|
511
|
-
if (!tx || typeof tx !== "object" || typeof tx.recipient !== "string" || typeof tx.amount !== "bigint") throw new TypeError("Bitcoin sendTx expects { amount: bigint, recipient: string } — amount in satoshis");
|
|
512
|
-
const { amount, recipient } = tx;
|
|
513
|
-
const sats = Number(amount);
|
|
514
|
-
return provider.sendBitcoin(recipient, sats);
|
|
515
|
-
},
|
|
516
|
-
sendTxToChain(tx, _targetChainIdDecimal, _account, cb) {
|
|
408
|
+
sendTx: (tx) => sendBitcoinTx(tx),
|
|
409
|
+
sendTxToChain: (tx, _targetChainIdDecimal, _account, cb) => {
|
|
517
410
|
cb?.();
|
|
518
|
-
return
|
|
411
|
+
return sendBitcoinTx(tx);
|
|
519
412
|
},
|
|
520
413
|
async signMessage(msg) {
|
|
521
414
|
const text = new TextDecoder().decode(msg);
|
|
@@ -531,14 +424,14 @@ const buildUnisatAdapter = (id, name, provider) => {
|
|
|
531
424
|
subscribe(listener) {
|
|
532
425
|
listenersSet.add(listener);
|
|
533
426
|
const onAccountsChanged = (...args) => {
|
|
534
|
-
const accounts = args[0]
|
|
427
|
+
const accounts = toStringArray(args[0]);
|
|
535
428
|
if (accounts.length === 0) {
|
|
536
429
|
listener({ type: "disconnected" });
|
|
537
430
|
return;
|
|
538
431
|
}
|
|
539
432
|
const built = accounts.map((a) => buildAccount(a, chain));
|
|
540
433
|
const first = built[0];
|
|
541
|
-
if (
|
|
434
|
+
if (first === void 0) return;
|
|
542
435
|
listener({
|
|
543
436
|
account: first,
|
|
544
437
|
accounts: built,
|
|
@@ -556,7 +449,7 @@ const buildUnisatAdapter = (id, name, provider) => {
|
|
|
556
449
|
listenersSet.delete(listener);
|
|
557
450
|
};
|
|
558
451
|
},
|
|
559
|
-
switchChain(target) {
|
|
452
|
+
switchChain: (target) => {
|
|
560
453
|
if (target.namespace !== "bip122") throw new Error(`Bitcoin adapter received non-Bitcoin chain "${target.id}". Pass a chain with namespace "bip122".`);
|
|
561
454
|
chain = target;
|
|
562
455
|
refreshChain();
|
|
@@ -569,7 +462,9 @@ const buildUnisatAdapter = (id, name, provider) => {
|
|
|
569
462
|
//#region src/injected/index.ts
|
|
570
463
|
const DEFAULT_SETTLE_MS = 150;
|
|
571
464
|
const readHost = (target) => {
|
|
572
|
-
|
|
465
|
+
if (target !== void 0) return target;
|
|
466
|
+
if (typeof window === "undefined") return null;
|
|
467
|
+
return window;
|
|
573
468
|
};
|
|
574
469
|
const probeProviders = (host) => {
|
|
575
470
|
const adapters = [];
|
|
@@ -614,9 +509,9 @@ const discoverInjectedBitcoinAdapter = (onAdapter, options = {}) => {
|
|
|
614
509
|
let cancelled = false;
|
|
615
510
|
const timer = setTimeout(() => {
|
|
616
511
|
if (cancelled) return;
|
|
617
|
-
if (options.hasAnyWalletStandardAdapter?.()) return;
|
|
512
|
+
if (options.hasAnyWalletStandardAdapter?.() === true) return;
|
|
618
513
|
const host = readHost(options.target);
|
|
619
|
-
if (
|
|
514
|
+
if (host === null) return;
|
|
620
515
|
const adapters = probeProviders(host);
|
|
621
516
|
for (const adapter of adapters) onAdapter(adapter);
|
|
622
517
|
}, settleMs);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["kitSlugify"],"sources":["../src/capabilities.ts","../src/wallet-standard-adapter.ts","../src/wallet-standard.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\";\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 capability mapping.\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 * - `switchChain = false`. No portable per-call switch RPC across\n * Bitcoin wallets — Xverse/Unisat/Phantom each pick their own network.\n */\nconst resolveBitcoinCapabilities = (input: BitcoinCapabilityInput): WalletCapabilities => ({\n getBalance: false,\n getTransactionReceipt: false,\n requestAccounts: false,\n sendTransaction: input.features.sendTransfer,\n signIn: false,\n signMessage: input.features.signMessage,\n signTransaction: input.features.signPsbt,\n subscribe: input.features.events,\n switchAccount: false,\n switchChain: input.chainCount > 1,\n});\n\nexport type { BitcoinCapabilityInput };\nexport { resolveBitcoinCapabilities };\n","import type { Account, ChainBase, ConnectorEvent, WalletAdapter } from \"@usebutr/core\";\nimport { logWarn, sanitizeIcon } from \"@usebutr/core\";\nimport {\n buildAccount,\n getFeature,\n pickAccountByAddress,\n pickFirstAddress,\n slugify as kitSlugify,\n} from \"@usebutr/wallet-standard-shared\";\nimport type {\n StandardConnectFeature,\n StandardDisconnectFeature,\n StandardEventsFeature,\n WalletStandardWallet,\n} 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\nconst slugify = (name: string): string => kitSlugify(\"btc\", name);\n\nconst pickBitcoinChain = (wallet: WalletStandardWallet): string | null => {\n const mainnet = wallet.chains.find((c) => c === BITCOIN_MAINNET_ID);\n if (mainnet) {\n return mainnet;\n }\n return wallet.chains.find((c) => c.startsWith(BITCOIN_PREFIX)) ?? null;\n};\n\nconst buildBitcoinChain = (chainId: string, walletName: string): ChainBase => ({\n id: chainId,\n // Same posture as EVM/SVM/Sui: butr ships no chain-id → name table.\n name: walletName,\n namespace: \"bip122\",\n reference: chainId.slice(BITCOIN_PREFIX.length),\n});\n\nconst buildBitcoinAccount = (address: string, chain: ChainBase): Account =>\n buildAccount(address, chain);\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 bitcoinChainId = pickBitcoinChain(wallet);\n if (!bitcoinChainId) {\n return null;\n }\n const connect = getFeature<StandardConnectFeature>(wallet, \"standard:connect\");\n if (!connect) {\n return null;\n }\n\n const disconnect = getFeature<StandardDisconnectFeature>(wallet, \"standard:disconnect\");\n const events = getFeature<StandardEventsFeature>(wallet, \"standard:events\");\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 let currentChainId = bitcoinChainId;\n const currentChain = (): ChainBase => buildBitcoinChain(currentChainId, wallet.name);\n\n const listeners = new Set<(event: ConnectorEvent) => void>();\n const notifyAccountChanged = () => {\n if (wallet.accounts.length === 0) {\n return;\n }\n const chain = currentChain();\n const built = wallet.accounts.map((a) => buildBitcoinAccount(a.address, chain));\n const first = built[0];\n if (!first) {\n return;\n }\n for (const listener of listeners) {\n listener({ account: first, accounts: built, type: \"accountChanged\" });\n }\n };\n\n registerDisconnector?.(() => {\n for (const listener of listeners) {\n listener({ type: \"disconnected\" });\n }\n });\n\n return {\n capabilities: resolveBitcoinCapabilities({\n chainCount: wallet.chains.filter((c) => c.startsWith(BITCOIN_PREFIX)).length,\n features: {\n events: Boolean(events),\n sendTransfer: Boolean(sendTransfer),\n signMessage: Boolean(signMessage),\n signPsbt: Boolean(signPsbt),\n },\n }),\n chainPlatform: \"bitcoin\",\n\n async connect(opts) {\n await connect.connect(opts?.silent ? { silent: true } : undefined);\n },\n\n async disconnect() {\n if (disconnect) {\n try {\n await disconnect.disconnect();\n } catch (error) {\n logWarn(\"[butr] Bitcoin Wallet Standard disconnect threw:\", error);\n }\n }\n },\n\n getAccount() {\n const address = pickFirstAddress(wallet.accounts);\n if (!address) {\n return Promise.resolve(null);\n }\n return Promise.resolve(buildBitcoinAccount(address, currentChain()));\n },\n\n getAccounts() {\n const chain = currentChain();\n return Promise.resolve(wallet.accounts.map((a) => buildBitcoinAccount(a.address, chain)));\n },\n\n getBalance() {\n return Promise.resolve({\n decimals: BITCOIN_DECIMALS,\n formatted: \"0\",\n symbol: \"BTC\",\n value: 0n,\n });\n },\n\n getSigner() {\n // Consumers cast to whatever Bitcoin signing wrapper they use\n // (bitcoinjs-lib's Signer, scure-btc-signer's HDWallet, etc.) or\n // call butr's adapter directly for PSBT / send-transfer.\n return Promise.resolve(wallet);\n },\n\n getTransactionReceipt() {\n return Promise.resolve({ status: \"Pending\" as const });\n },\n\n icon: sanitizeIcon(wallet.icon),\n id: slugify(wallet.name),\n name: wallet.name,\n\n async requestAccounts() {\n await connect.connect();\n },\n\n async sendTx(tx, account) {\n if (!sendTransfer) {\n throw new Error(`Wallet ${wallet.name} does not advertise bitcoin:sendTransfer`);\n }\n if (\n !tx ||\n typeof tx !== \"object\" ||\n typeof (tx as { amount?: unknown }).amount !== \"bigint\" ||\n typeof (tx as { recipient?: unknown }).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 as { amount: bigint; recipient: string };\n const wsAccount = account\n ? pickAccountByAddress(wallet.accounts, account.walletAddress)\n : (wallet.accounts[0] ?? null);\n if (!wsAccount) {\n throw new Error(\"No connected account\");\n }\n const output = await sendTransfer.sendTransfer({\n account: wsAccount,\n amount,\n chain: currentChainId,\n recipient,\n });\n return output.txid;\n },\n\n sendTxToChain(tx, _targetChainId, account, cb) {\n cb?.();\n return this.sendTx(tx, account);\n },\n\n async signMessage(msg, account) {\n if (!signMessage) {\n throw new Error(`Wallet ${wallet.name} does not advertise bitcoin:signMessage`);\n }\n const wsAccount = account\n ? pickAccountByAddress(wallet.accounts, account.walletAddress)\n : (wallet.accounts[0] ?? null);\n if (!wsAccount) {\n throw new Error(\"No connected account\");\n }\n const output = await signMessage.signMessage({ account: wsAccount, message: msg });\n return { signature: output.signature, signedMessage: output.signedMessage };\n },\n\n ...(signPsbt\n ? {\n async signTransaction(tx, account) {\n const wsAccount = account\n ? pickAccountByAddress(wallet.accounts, account.walletAddress)\n : (wallet.accounts[0] ?? null);\n if (!wsAccount) {\n throw new Error(\"No connected account\");\n }\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: currentChainId,\n psbt: tx,\n });\n return output.signedPsbt;\n },\n }\n : {}),\n\n subscribe(listener) {\n listeners.add(listener);\n let unsubWallet: (() => void) | null = null;\n if (events) {\n const unsub = events.on(\"change\", (changes) => {\n if (changes.chains) {\n const next =\n changes.chains.find((c) => c === BITCOIN_MAINNET_ID) ??\n changes.chains.find((c) => c.startsWith(BITCOIN_PREFIX));\n if (next) {\n currentChainId = next;\n }\n }\n\n if (changes.accounts) {\n if (changes.accounts.length === 0) {\n listener({ type: \"disconnected\" });\n return;\n }\n const chain = currentChain();\n const built = changes.accounts.map((a) => buildBitcoinAccount(a.address, chain));\n const first = built[0];\n if (!first) {\n return;\n }\n listener({ account: first, accounts: built, type: \"accountChanged\" });\n return;\n }\n\n if (changes.chains) {\n notifyAccountChanged();\n }\n });\n unsubWallet = () => unsub();\n }\n return () => {\n listeners.delete(listener);\n unsubWallet?.();\n };\n },\n\n switchChain(chain) {\n if (chain.namespace !== \"bip122\") {\n throw new Error(\n `Bitcoin adapter received non-Bitcoin chain \"${chain.id}\". Pass a chain with namespace \"bip122\".`,\n );\n }\n if (!wallet.chains.includes(chain.id)) {\n throw new Error(\n `Wallet ${wallet.name} does not advertise chain \"${chain.id}\". Available: ${wallet.chains.join(\", \")}`,\n );\n }\n currentChainId = chain.id;\n notifyAccountChanged();\n return Promise.resolve();\n },\n };\n};\n\nexport { buildBitcoinAdapter, slugify };\n","import type { WalletAdapter } from \"@usebutr/core\";\nimport { discoverWalletStandard } from \"@usebutr/wallet-standard-shared\";\n\nimport { buildBitcoinAdapter } from \"./wallet-standard-adapter\";\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 { 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 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 return response.result as T;\n };\n\n const loadAccounts = async (): Promise<ReadonlyArray<string>> => {\n const result = await callRequest<{\n addresses?: ReadonlyArray<{ address: string; purpose?: string }>;\n }>(\"getAccounts\", {\n message: \"Connect to butr\",\n purposes: [\"payment\", \"ordinals\"],\n });\n if (!result?.addresses) {\n return [];\n }\n const addresses = result.addresses.map((a) => a.address);\n cachedAccounts = addresses;\n return addresses;\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) {\n return buildAccount(first, chain);\n }\n const accounts = await loadAccounts();\n const firstFresh = accounts[0];\n return firstFresh ? buildAccount(firstFresh, chain) : null;\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 return Promise.resolve({\n decimals: 8,\n formatted: \"0\",\n symbol: \"BTC\",\n value: 0n,\n });\n },\n\n getSigner() {\n return Promise.resolve(provider);\n },\n\n getTransactionReceipt() {\n return Promise.resolve({ status: \"Pending\" as const });\n },\n\n icon: GENERIC_BITCOIN_ICON,\n id,\n name,\n\n async requestAccounts() {\n await loadAccounts();\n },\n\n async sendTx(tx) {\n if (\n !tx ||\n typeof tx !== \"object\" ||\n typeof (tx as { recipient?: unknown }).recipient !== \"string\" ||\n typeof (tx as { amount?: unknown }).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 as { amount: bigint; recipient: string };\n const result = await callRequest<{ txid: string }>(\"sendTransfer\", {\n recipients: [{ address: recipient, amount: amount.toString() }],\n });\n return result.txid;\n },\n\n sendTxToChain(tx, _targetChainId, _account, cb) {\n cb?.();\n return this.sendTx(tx);\n },\n\n async signMessage(msg) {\n const address = cachedAccounts[0];\n if (!address) {\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 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 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 // Network probe failed — keep the current chain. The next call\n // will retry through whichever path next invokes refreshChain.\n }\n };\n\n return {\n capabilities: CAPS_UNISAT,\n chainPlatform: \"bitcoin\",\n\n async connect(opts) {\n if (opts?.silent) {\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 return Promise.resolve();\n },\n\n async getAccount() {\n const accounts = await provider.getAccounts();\n const first = accounts[0];\n if (!first) {\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 return Promise.resolve({\n decimals: 8,\n formatted: \"0\",\n symbol: \"BTC\",\n value: 0n,\n });\n },\n\n getSigner() {\n return Promise.resolve(provider);\n },\n\n getTransactionReceipt() {\n return Promise.resolve({ status: \"Pending\" as const });\n },\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) {\n if (typeof provider.sendBitcoin !== \"function\") {\n throw new TypeError(`Wallet ${name} does not expose sendBitcoin`);\n }\n if (\n !tx ||\n typeof tx !== \"object\" ||\n typeof (tx as { recipient?: unknown }).recipient !== \"string\" ||\n typeof (tx as { amount?: unknown }).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 as { amount: bigint; recipient: string };\n // UniSat's sendBitcoin takes a JS number for satoshis. Values\n // above 2^53 aren't representable; that's 90+ million BTC, which\n // would never appear in practice.\n const sats = Number(amount);\n return provider.sendBitcoin(recipient, sats);\n },\n\n sendTxToChain(tx, _targetChainIdDecimal, _account, cb) {\n cb?.();\n return this.sendTx(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 = (args[0] as ReadonlyArray<string> | undefined) ?? [];\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) {\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 const globalWindow = typeof window === \"undefined\" ? null : (window as unknown as InjectedHost);\n return target === undefined ? globalWindow : target;\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?.()) {\n return;\n }\n const host = readHost(options.target);\n if (!host) {\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\";\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 platform: \"bitcoin\",\n subscribe: discoverBitcoinAdapters,\n};\n\nexport { bitcoinDiscoverer };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["kitSlugify"],"sources":["../src/capabilities.ts","../src/wallet-standard-adapter.ts","../src/wallet-standard.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\";\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 capability mapping.\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 * - `switchChain = chainCount > 1`. No portable switch RPC across\n * Bitcoin wallets (Xverse/Unisat/Phantom each pick their own\n * network), so the switch is local-state-only and advertised only\n * when the wallet exposes more than one chain to switch between.\n */\nconst resolveBitcoinCapabilities = (input: BitcoinCapabilityInput): WalletCapabilities => ({\n getBalance: false,\n getTransactionReceipt: false,\n requestAccounts: false,\n sendTransaction: input.features.sendTransfer,\n signIn: false,\n signMessage: input.features.signMessage,\n signTransaction: input.features.signPsbt,\n subscribe: input.features.events,\n switchAccount: false,\n switchChain: input.chainCount > 1,\n});\n\nexport type { BitcoinCapabilityInput };\nexport { resolveBitcoinCapabilities };\n","import type { WalletAdapter } from \"@usebutr/core\";\nimport {\n createWalletStandardCore,\n getFeature,\n slugify as kitSlugify,\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\nconst slugify = (name: string): string => kitSlugify(\"btc\", name);\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(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\nexport { buildBitcoinAdapter, slugify };\n","import type { WalletAdapter } from \"@usebutr/core\";\nimport { discoverWalletStandard } from \"@usebutr/wallet-standard-shared\";\n\nimport { buildBitcoinAdapter } from \"./wallet-standard-adapter\";\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 { 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\";\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 platform: \"bitcoin\",\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": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
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,13 +25,13 @@
|
|
|
25
25
|
"access": "public"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@usebutr/core": "0.
|
|
29
|
-
"@usebutr/wallet-standard-shared": "0.
|
|
28
|
+
"@usebutr/core": "0.5.0",
|
|
29
|
+
"@usebutr/wallet-standard-shared": "0.3.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@wallet-standard/app": "^1.1.1",
|
|
33
33
|
"typescript": "^6.0.3",
|
|
34
|
-
"vitest": "^4.1.
|
|
34
|
+
"vitest": "^4.1.10",
|
|
35
35
|
"@repo/config-vitest": "0.0.0",
|
|
36
36
|
"@repo/typescript-config": "0.0.0"
|
|
37
37
|
},
|