bitcoin-wallet-connector 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/README.md +208 -0
  2. package/lib/BitcoinConnectionProvider.d.ts +23 -0
  3. package/lib/BitcoinWalletAdapterConnector-Bq835yj0.mjs +123 -0
  4. package/lib/BitcoinWalletAdapterConnector-Bq835yj0.mjs.map +1 -0
  5. package/lib/BitcoinWalletAdapterConnector-DMef0iHV.js +2 -0
  6. package/lib/BitcoinWalletAdapterConnector-DMef0iHV.js.map +1 -0
  7. package/lib/BitcoinWalletAdapterConnector.d.ts +30 -0
  8. package/lib/BitgetWalletAdapter.impl-C_HLO7Oi.mjs +10 -0
  9. package/lib/BitgetWalletAdapter.impl-C_HLO7Oi.mjs.map +1 -0
  10. package/lib/BitgetWalletAdapter.impl-CxnKMf7U.js +2 -0
  11. package/lib/BitgetWalletAdapter.impl-CxnKMf7U.js.map +1 -0
  12. package/lib/LeatherWalletAdapter.impl-B2QgX_tO.js +2 -0
  13. package/lib/LeatherWalletAdapter.impl-B2QgX_tO.js.map +1 -0
  14. package/lib/LeatherWalletAdapter.impl-RUYx555r.mjs +184 -0
  15. package/lib/LeatherWalletAdapter.impl-RUYx555r.mjs.map +1 -0
  16. package/lib/MagicEdenWalletAdapter.impl-CrA6SGvG.mjs +235 -0
  17. package/lib/MagicEdenWalletAdapter.impl-CrA6SGvG.mjs.map +1 -0
  18. package/lib/MagicEdenWalletAdapter.impl-Di3Nu2S5.js +2 -0
  19. package/lib/MagicEdenWalletAdapter.impl-Di3Nu2S5.js.map +1 -0
  20. package/lib/OkxWalletAdapter.impl-BepoUL1B.mjs +67 -0
  21. package/lib/OkxWalletAdapter.impl-BepoUL1B.mjs.map +1 -0
  22. package/lib/OkxWalletAdapter.impl-C8kesjGu.js +2 -0
  23. package/lib/OkxWalletAdapter.impl-C8kesjGu.js.map +1 -0
  24. package/lib/UnisatCompatibleWalletAdapterImpl-Cq2Oqk1b.js +2 -0
  25. package/lib/UnisatCompatibleWalletAdapterImpl-Cq2Oqk1b.js.map +1 -0
  26. package/lib/UnisatCompatibleWalletAdapterImpl-M38FqkZI.mjs +137 -0
  27. package/lib/UnisatCompatibleWalletAdapterImpl-M38FqkZI.mjs.map +1 -0
  28. package/lib/UnisatWalletAdapter.impl-CJB22se8.mjs +14 -0
  29. package/lib/UnisatWalletAdapter.impl-CJB22se8.mjs.map +1 -0
  30. package/lib/UnisatWalletAdapter.impl-EISvxdpc.js +2 -0
  31. package/lib/UnisatWalletAdapter.impl-EISvxdpc.js.map +1 -0
  32. package/lib/WalletAdapters.types-CnvOqHFH.mjs +32 -0
  33. package/lib/WalletAdapters.types-CnvOqHFH.mjs.map +1 -0
  34. package/lib/WalletAdapters.types-De_x1lzr.js +2 -0
  35. package/lib/WalletAdapters.types-De_x1lzr.js.map +1 -0
  36. package/lib/WalletAdapters.types.d.ts +110 -0
  37. package/lib/XverseCompatibleWalletAdapterImpl-Bf-BK5VK.js +2 -0
  38. package/lib/XverseCompatibleWalletAdapterImpl-Bf-BK5VK.js.map +1 -0
  39. package/lib/XverseCompatibleWalletAdapterImpl-DXKnO_-V.mjs +151 -0
  40. package/lib/XverseCompatibleWalletAdapterImpl-DXKnO_-V.mjs.map +1 -0
  41. package/lib/XverseWalletAdapter.impl-CZO0RQva.mjs +105 -0
  42. package/lib/XverseWalletAdapter.impl-CZO0RQva.mjs.map +1 -0
  43. package/lib/XverseWalletAdapter.impl-lJwMi-Iv.js +2 -0
  44. package/lib/XverseWalletAdapter.impl-lJwMi-Iv.js.map +1 -0
  45. package/lib/adapters/BitgetWalletAdapter.d.ts +2 -0
  46. package/lib/adapters/BitgetWalletAdapter.impl.d.ts +8 -0
  47. package/lib/adapters/LeatherWalletAdapter.d.ts +2 -0
  48. package/lib/adapters/LeatherWalletAdapter.impl.d.ts +41 -0
  49. package/lib/adapters/MagicEdenWalletAdapter.d.ts +11 -0
  50. package/lib/adapters/MagicEdenWalletAdapter.impl.d.ts +22 -0
  51. package/lib/adapters/MockAddressWalletAdapter.d.ts +33 -0
  52. package/lib/adapters/OkxWalletAdapter.d.ts +2 -0
  53. package/lib/adapters/OkxWalletAdapter.impl.d.ts +51 -0
  54. package/lib/adapters/UnisatWalletAdapter.d.ts +2 -0
  55. package/lib/adapters/UnisatWalletAdapter.impl.d.ts +14 -0
  56. package/lib/adapters/XverseWalletAdapter.d.ts +3 -0
  57. package/lib/adapters/XverseWalletAdapter.impl.d.ts +14 -0
  58. package/lib/adapters/index.d.ts +7 -0
  59. package/lib/adapters.js +2 -0
  60. package/lib/adapters.js.map +1 -0
  61. package/lib/adapters.mjs +11 -0
  62. package/lib/adapters.mjs.map +1 -0
  63. package/lib/bitget-C7oB4Ffq.mjs +5 -0
  64. package/lib/bitget-C7oB4Ffq.mjs.map +1 -0
  65. package/lib/bitget-DXnsxx_y.js +2 -0
  66. package/lib/bitget-DXnsxx_y.js.map +1 -0
  67. package/lib/index-CaV3F1Nm.js +424 -0
  68. package/lib/index-CaV3F1Nm.js.map +1 -0
  69. package/lib/index-CcQUdePc.mjs +12224 -0
  70. package/lib/index-CcQUdePc.mjs.map +1 -0
  71. package/lib/index-D7YwhNAG.mjs +3946 -0
  72. package/lib/index-D7YwhNAG.mjs.map +1 -0
  73. package/lib/index-Zx0KcpYx.js +2 -0
  74. package/lib/index-Zx0KcpYx.js.map +1 -0
  75. package/lib/index.d.ts +3 -0
  76. package/lib/index.js +2 -0
  77. package/lib/index.js.map +1 -0
  78. package/lib/index.mjs +20 -0
  79. package/lib/index.mjs.map +1 -0
  80. package/lib/leather-BoQG_CPn.mjs +5 -0
  81. package/lib/leather-BoQG_CPn.mjs.map +1 -0
  82. package/lib/leather-DJ8nWmM8.js +2 -0
  83. package/lib/leather-DJ8nWmM8.js.map +1 -0
  84. package/lib/magiceden-B36CEQa6.js +2 -0
  85. package/lib/magiceden-B36CEQa6.js.map +1 -0
  86. package/lib/magiceden-Cg7d3agI.mjs +5 -0
  87. package/lib/magiceden-Cg7d3agI.mjs.map +1 -0
  88. package/lib/misc-B5EWO_dn.mjs +10 -0
  89. package/lib/misc-B5EWO_dn.mjs.map +1 -0
  90. package/lib/misc-CigR0RqC.js +2 -0
  91. package/lib/misc-CigR0RqC.js.map +1 -0
  92. package/lib/okx-ChwzM0dK.js +2 -0
  93. package/lib/okx-ChwzM0dK.js.map +1 -0
  94. package/lib/okx-DWbHwazu.mjs +5 -0
  95. package/lib/okx-DWbHwazu.mjs.map +1 -0
  96. package/lib/react.d.ts +2 -0
  97. package/lib/react.js +2 -0
  98. package/lib/react.js.map +1 -0
  99. package/lib/react.mjs +128 -0
  100. package/lib/react.mjs.map +1 -0
  101. package/lib/transaction-CiLOYSE_.mjs +1063 -0
  102. package/lib/transaction-CiLOYSE_.mjs.map +1 -0
  103. package/lib/transaction-CzdnbXSo.js +2 -0
  104. package/lib/transaction-CzdnbXSo.js.map +1 -0
  105. package/lib/unisat-BvZW5h0U.js +2 -0
  106. package/lib/unisat-BvZW5h0U.js.map +1 -0
  107. package/lib/unisat-pLgab4nG.mjs +5 -0
  108. package/lib/unisat-pLgab4nG.mjs.map +1 -0
  109. package/lib/utils/StateChannel.d.ts +14 -0
  110. package/lib/utils/UnisatCompatibleWalletAdapterImpl.d.ts +99 -0
  111. package/lib/utils/XverseCompatibleWalletAdapterImpl.d.ts +80 -0
  112. package/lib/utils/XverseCompatibleWalletAdapterImpl_legacy.d.ts +44 -0
  113. package/lib/utils/bitcoinAddressHelpers.d.ts +14 -0
  114. package/lib/utils/bitcoinNetworkHelpers.d.ts +4 -0
  115. package/lib/utils/createAdapterAvailability.d.ts +15 -0
  116. package/lib/utils/error.d.ts +6 -0
  117. package/lib/utils/misc.d.ts +3 -0
  118. package/lib/xverse-IKOHyGi-.js +2 -0
  119. package/lib/xverse-IKOHyGi-.js.map +1 -0
  120. package/lib/xverse-iHLNanCB.mjs +5 -0
  121. package/lib/xverse-iHLNanCB.mjs.map +1 -0
  122. package/package.json +86 -0
  123. package/src/BitcoinConnectionProvider.stories.tsx +329 -0
  124. package/src/BitcoinConnectionProvider.tsx +234 -0
  125. package/src/BitcoinWalletAdapterConnector.ts +166 -0
  126. package/src/WalletAdapters.types.ts +154 -0
  127. package/src/_/bitget.png +0 -0
  128. package/src/_/leather.svg +4 -0
  129. package/src/_/magiceden.png +0 -0
  130. package/src/_/okx.png +0 -0
  131. package/src/_/unisat.svg +31 -0
  132. package/src/_/xverse.png +0 -0
  133. package/src/adapters/BitgetWalletAdapter.impl.ts +22 -0
  134. package/src/adapters/BitgetWalletAdapter.ts +44 -0
  135. package/src/adapters/LeatherWalletAdapter.impl.ts +324 -0
  136. package/src/adapters/LeatherWalletAdapter.ts +35 -0
  137. package/src/adapters/MagicEdenWalletAdapter.impl.ts +139 -0
  138. package/src/adapters/MagicEdenWalletAdapter.ts +51 -0
  139. package/src/adapters/MockAddressWalletAdapter.ts +199 -0
  140. package/src/adapters/OkxWalletAdapter.impl.ts +168 -0
  141. package/src/adapters/OkxWalletAdapter.ts +37 -0
  142. package/src/adapters/UnisatWalletAdapter.impl.ts +32 -0
  143. package/src/adapters/UnisatWalletAdapter.ts +50 -0
  144. package/src/adapters/XverseWalletAdapter.impl.ts +150 -0
  145. package/src/adapters/XverseWalletAdapter.ts +37 -0
  146. package/src/adapters/index.ts +7 -0
  147. package/src/env.d.ts +9 -0
  148. package/src/index.ts +3 -0
  149. package/src/react.ts +9 -0
  150. package/src/utils/StateChannel.ts +39 -0
  151. package/src/utils/UnisatCompatibleWalletAdapterImpl.ts +342 -0
  152. package/src/utils/XverseCompatibleWalletAdapterImpl.ts +288 -0
  153. package/src/utils/XverseCompatibleWalletAdapterImpl_legacy.ts +278 -0
  154. package/src/utils/bitcoinAddressHelpers.ts +132 -0
  155. package/src/utils/bitcoinNetworkHelpers.ts +17 -0
  156. package/src/utils/createAdapterAvailability.ts +92 -0
  157. package/src/utils/error.ts +13 -0
  158. package/src/utils/misc.ts +10 -0
@@ -0,0 +1,278 @@
1
+ import { base64, hex } from "@scure/base"
2
+ import * as btc from "@scure/btc-signer"
3
+ import { UserRejectError } from "../utils/error"
4
+ import {
5
+ SignMessageAlgorithm,
6
+ SignMessageResult,
7
+ WalletAdapter,
8
+ WalletAdapter_onAddressesChanged_callback,
9
+ WalletAdapterAddress,
10
+ WalletAdapterAddressPurpose,
11
+ WalletAdapterBitcoinNetwork,
12
+ WalletAdapterNotConnectedError,
13
+ } from "../WalletAdapters.types"
14
+ import {
15
+ XverseCompatibleProviderError,
16
+ XverseRpcErrorCode,
17
+ } from "./XverseCompatibleWalletAdapterImpl"
18
+
19
+ type GetAddressResponse = import("sats-connect").GetAddressResponse
20
+
21
+ export type XverseCompatibleWalletAdapterGetProviderFn = () => Promise<
22
+ import("sats-connect").BitcoinProvider
23
+ >
24
+
25
+ export type XverseCompatibleWalletAdapterParsedAddressesFn = (info: {
26
+ sdk: typeof import("sats-connect")
27
+ addresses: import("sats-connect").Address[]
28
+ }) => Promise<XverseCompatibleWalletAdapterImplAddress[]>
29
+
30
+ export type XverseCompatibleWalletAdapterImplAddress = WalletAdapterAddress & {
31
+ publicKey: string
32
+ }
33
+
34
+ export class XverseCompatibleWalletAdapterImpl_legacy implements WalletAdapter {
35
+ protected readonly network: WalletAdapterBitcoinNetwork
36
+ protected readonly localStorageKey: string
37
+ protected readonly walletDisplayName: string
38
+ protected readonly getProvider: XverseCompatibleWalletAdapterGetProviderFn
39
+ protected readonly parseAddresses: XverseCompatibleWalletAdapterParsedAddressesFn
40
+
41
+ constructor(info: {
42
+ network: WalletAdapterBitcoinNetwork
43
+ localStorageKey: string
44
+ walletDisplayName: string
45
+ getProvider: XverseCompatibleWalletAdapterGetProviderFn
46
+ parseAddresses: XverseCompatibleWalletAdapterParsedAddressesFn
47
+ }) {
48
+ this.network = info.network
49
+ this.localStorageKey = info.localStorageKey
50
+ this.walletDisplayName = info.walletDisplayName
51
+ this.getProvider = info.getProvider
52
+ this.parseAddresses = info.parseAddresses
53
+ }
54
+
55
+ private async getSdk(): Promise<typeof import("sats-connect")> {
56
+ return import("sats-connect")
57
+ }
58
+
59
+ protected retrieveConnectedAddress(): GetAddressResponse | undefined {
60
+ let resp: GetAddressResponse | undefined
61
+ const stored = localStorage.getItem(this.localStorageKey) || undefined
62
+ if (stored != null) {
63
+ try {
64
+ resp = JSON.parse(stored)
65
+ if (
66
+ resp == null ||
67
+ !("addresses" in resp) ||
68
+ !Array.isArray(resp.addresses)
69
+ ) {
70
+ throw new Error("Invalid stored addresses")
71
+ }
72
+ } catch {
73
+ localStorage.removeItem(this.localStorageKey)
74
+ }
75
+ }
76
+ return resp
77
+ }
78
+ protected async updateConnectedAddress(
79
+ addresses: GetAddressResponse["addresses"],
80
+ ): Promise<void> {
81
+ localStorage.setItem(this.localStorageKey, JSON.stringify({ addresses }))
82
+ }
83
+
84
+ async connect(): Promise<void> {
85
+ if (this.retrieveConnectedAddress() == null) {
86
+ const resp = await this.connectImpl()
87
+ await this.updateConnectedAddress(resp.addresses)
88
+ }
89
+ }
90
+ private async connectImpl(): Promise<GetAddressResponse> {
91
+ const sdk = await this.getSdk()
92
+
93
+ const networkType =
94
+ this.network === WalletAdapterBitcoinNetwork.MAINNET
95
+ ? sdk.BitcoinNetworkType.Mainnet
96
+ : sdk.BitcoinNetworkType.Testnet
97
+
98
+ return new Promise<GetAddressResponse>((resolve, reject) => {
99
+ void sdk
100
+ .getAddress({
101
+ getProvider: () => this.getProvider(),
102
+ payload: {
103
+ purposes: [sdk.AddressPurpose.Ordinals, sdk.AddressPurpose.Payment],
104
+ message: "Address for receiving Ordinals and payments",
105
+ network: { type: networkType },
106
+ },
107
+ onFinish(resp) {
108
+ resolve(resp)
109
+ },
110
+ onCancel() {
111
+ reject(new UserRejectError())
112
+ },
113
+ })
114
+ .catch(reject)
115
+ })
116
+ }
117
+
118
+ async disconnect(): Promise<void> {
119
+ localStorage.removeItem(this.localStorageKey)
120
+ return Promise.resolve()
121
+ }
122
+
123
+ async getAddresses(): Promise<XverseCompatibleWalletAdapterImplAddress[]> {
124
+ const resp = this.retrieveConnectedAddress()
125
+
126
+ if (resp == null) {
127
+ throw new WalletAdapterNotConnectedError(this.walletDisplayName)
128
+ }
129
+
130
+ const sdk = await this.getSdk()
131
+
132
+ return this.parseAddresses({ sdk, addresses: resp.addresses })
133
+ }
134
+
135
+ async signMessage(
136
+ address: string,
137
+ message: string,
138
+ ): Promise<SignMessageResult> {
139
+ const sdk = await this.getSdk()
140
+
141
+ return new Promise((resolve, reject) => {
142
+ /**
143
+ * https://docs.xverse.app/sats-connect/methods/signmessage
144
+ */
145
+ void sdk.signMessage({
146
+ getProvider: () => this.getProvider(),
147
+ payload: {
148
+ network: {
149
+ type:
150
+ this.network === "mainnet"
151
+ ? sdk.BitcoinNetworkType.Mainnet
152
+ : sdk.BitcoinNetworkType.Testnet,
153
+ },
154
+ address,
155
+ message,
156
+ protocol: sdk.MessageSigningProtocols.BIP322,
157
+ },
158
+ onFinish(resp) {
159
+ resolve({
160
+ result: resp,
161
+ address,
162
+ algorithm: SignMessageAlgorithm.BIP322,
163
+ })
164
+ },
165
+ onCancel() {
166
+ reject(new UserRejectError())
167
+ },
168
+ })
169
+ })
170
+ }
171
+
172
+ sendBitcoinFeeRateCapability = "unavailable" as const
173
+ async sendBitcoin(
174
+ fromAddress: string,
175
+ receiverAddress: string,
176
+ satoshiAmount: bigint,
177
+ ): Promise<{ txid: string }> {
178
+ const senderAddress = (await this.getAddresses()).find(a =>
179
+ a.purposes.includes(WalletAdapterAddressPurpose.Bitcoin),
180
+ )
181
+ if (senderAddress == null) {
182
+ throw new XverseCompatibleProviderError({
183
+ code: XverseRpcErrorCode.INVALID_PARAMS,
184
+ message: "Bitcoin address not found",
185
+ })
186
+ }
187
+
188
+ const sdk = await this.getSdk()
189
+
190
+ return new Promise((resolve, reject) => {
191
+ void sdk
192
+ .sendBtcTransaction({
193
+ getProvider: () => this.getProvider(),
194
+ payload: {
195
+ network: {
196
+ type:
197
+ this.network === WalletAdapterBitcoinNetwork.MAINNET
198
+ ? sdk.BitcoinNetworkType.Mainnet
199
+ : sdk.BitcoinNetworkType.Testnet,
200
+ },
201
+ message: "Send Bitcoin",
202
+ recipients: [
203
+ {
204
+ address: receiverAddress,
205
+ amountSats: BigInt(satoshiAmount),
206
+ },
207
+ ],
208
+ senderAddress: senderAddress.address,
209
+ },
210
+ onFinish(resp) {
211
+ return resolve({ txid: resp })
212
+ },
213
+ onCancel() {
214
+ reject(new UserRejectError())
215
+ },
216
+ })
217
+ .catch(reject)
218
+ })
219
+ }
220
+
221
+ sendInscriptionFeeRateCapability = "unavailable" as const
222
+
223
+ async signAndFinalizePsbt(
224
+ psbtHex: string,
225
+ signIndices: [address: string, signIndex: number][],
226
+ ): Promise<{
227
+ signedPsbtHex: string
228
+ }> {
229
+ const sdk = await this.getSdk()
230
+
231
+ const psbtBase64 = base64.encode(hex.decode(psbtHex))
232
+
233
+ const signedPsbt = await new Promise<Uint8Array>((resolve, reject) => {
234
+ void sdk
235
+ .signTransaction({
236
+ getProvider: () => this.getProvider(),
237
+ payload: {
238
+ network: {
239
+ type:
240
+ this.network === WalletAdapterBitcoinNetwork.MAINNET
241
+ ? sdk.BitcoinNetworkType.Mainnet
242
+ : sdk.BitcoinNetworkType.Testnet,
243
+ },
244
+ message: "Sign transaction",
245
+ psbtBase64,
246
+ inputsToSign: signIndices.map(([address, signIndex]) => ({
247
+ address,
248
+ signingIndexes: [signIndex],
249
+ })),
250
+ broadcast: false,
251
+ },
252
+ onFinish(resp) {
253
+ resolve(base64.decode(resp.psbtBase64))
254
+ },
255
+ onCancel() {
256
+ reject(new UserRejectError())
257
+ },
258
+ })
259
+ .catch(reject)
260
+ })
261
+
262
+ const tx = btc.Transaction.fromPSBT(signedPsbt, {
263
+ allowUnknownInputs: true,
264
+ allowUnknownOutputs: true,
265
+ disableScriptCheck: true,
266
+ allowLegacyWitnessUtxo: true,
267
+ })
268
+ tx.finalize()
269
+
270
+ return { signedPsbtHex: hex.encode(tx.toPSBT()) }
271
+ }
272
+
273
+ onAddressesChanged(callback: WalletAdapter_onAddressesChanged_callback): {
274
+ unsubscribe: () => void
275
+ } {
276
+ throw new Error("Not implemented")
277
+ }
278
+ }
@@ -0,0 +1,132 @@
1
+ import { hex } from "@scure/base"
2
+ import * as btc from "@scure/btc-signer"
3
+ import { Address, OutScript } from "@scure/btc-signer"
4
+ import { BitcoinNetwork, isMainnet } from "./bitcoinNetworkHelpers"
5
+
6
+ export type AddressTypeKnown = "p2pkh" | "p2sh" | "p2wpkh" | "p2wsh" | "p2tr"
7
+ export type AddressType = AddressTypeKnown | "unknown"
8
+
9
+ export function getAddressType(
10
+ network: BitcoinNetwork,
11
+ address: string,
12
+ ): AddressType {
13
+ if (isP2PKHAddress(network, address)) {
14
+ return "p2pkh"
15
+ } else if (isP2SHAddress(network, address)) {
16
+ return "p2sh"
17
+ } else if (isP2WPKHAddress(network, address)) {
18
+ return "p2wpkh"
19
+ } else if (isP2WSHAddress(network, address)) {
20
+ return "p2wsh"
21
+ } else if (isP2TRAddress(network, address)) {
22
+ return "p2tr"
23
+ } else {
24
+ return "unknown"
25
+ }
26
+ }
27
+
28
+ export function isP2PKHAddress(
29
+ network: BitcoinNetwork,
30
+ address: string,
31
+ ): boolean {
32
+ if (isMainnet(network)) {
33
+ return address.startsWith("1")
34
+ } else {
35
+ return address.startsWith("m") || address.startsWith("n")
36
+ }
37
+ }
38
+
39
+ export function isP2SHAddress(
40
+ network: BitcoinNetwork,
41
+ address: string,
42
+ ): boolean {
43
+ if (isMainnet(network)) {
44
+ return address.startsWith("3")
45
+ } else {
46
+ return address.startsWith("2")
47
+ }
48
+ }
49
+
50
+ export function isP2WPKHAddress(
51
+ network: BitcoinNetwork,
52
+ address: string,
53
+ ): boolean {
54
+ if (isMainnet(network)) {
55
+ return address.startsWith("bc1q")
56
+ } else {
57
+ return address.startsWith("tb1q")
58
+ }
59
+ }
60
+
61
+ export function isP2TRAddress(
62
+ network: BitcoinNetwork,
63
+ address: string,
64
+ ): boolean {
65
+ if (isMainnet(network)) {
66
+ return address.startsWith("bc1p")
67
+ } else {
68
+ return address.startsWith("tb1p")
69
+ }
70
+ }
71
+
72
+ export function isP2WSHAddress(
73
+ network: BitcoinNetwork,
74
+ address: string,
75
+ ): boolean {
76
+ if (isP2TRAddress(network, address)) {
77
+ return false
78
+ }
79
+ if (isP2WPKHAddress(network, address)) {
80
+ return false
81
+ }
82
+
83
+ if (isMainnet(network)) {
84
+ return address.startsWith("bc1")
85
+ } else {
86
+ return address.startsWith("tb1")
87
+ }
88
+ }
89
+
90
+ export function getP2TRInternalPublicKey(
91
+ network: BitcoinNetwork,
92
+ publicKey: Uint8Array,
93
+ ): Uint8Array {
94
+ const ecdsaPublicKeyLength = 33
95
+ if (publicKey.byteLength !== ecdsaPublicKeyLength) {
96
+ throw new Error("Invalid public key length")
97
+ }
98
+ return publicKey.slice(1)
99
+ }
100
+
101
+ export function getTapInternalKeyOf_P2TR_publicKey(
102
+ network: BitcoinNetwork,
103
+ publicKey: Uint8Array,
104
+ ): Uint8Array {
105
+ return btc.p2tr(
106
+ getP2TRInternalPublicKey(network, publicKey),
107
+ undefined,
108
+ network,
109
+ ).tapInternalKey
110
+ }
111
+
112
+ export function getRedeemScriptOf_P2SH_P2WPKH_publicKey(
113
+ network: BitcoinNetwork,
114
+ publicKey: Uint8Array,
115
+ ): Uint8Array {
116
+ return btc.p2sh(btc.p2wpkh(publicKey, network), network).redeemScript!
117
+ }
118
+
119
+ export function addressToScriptPubKey(
120
+ network: BitcoinNetwork,
121
+ address: string,
122
+ ): Uint8Array {
123
+ const addr = Address(network).decode(address)
124
+ return OutScript.encode(addr)
125
+ }
126
+
127
+ export function addressToScriptPubKeyHex(
128
+ network: BitcoinNetwork,
129
+ address: string,
130
+ ): string {
131
+ return hex.encode(addressToScriptPubKey(network, address))
132
+ }
@@ -0,0 +1,17 @@
1
+ import * as btc from "@scure/btc-signer"
2
+ import { checkNever } from "./misc"
3
+
4
+ export type BitcoinNetwork = typeof btc.NETWORK
5
+
6
+ export function getBitcoinNetwork(
7
+ network: "mainnet" | "testnet",
8
+ ): BitcoinNetwork {
9
+ if (network === "mainnet") return btc.NETWORK
10
+ if (network === "testnet") return btc.TEST_NETWORK
11
+ checkNever(network as never)
12
+ return btc.NETWORK
13
+ }
14
+
15
+ export function isMainnet(network: BitcoinNetwork): boolean {
16
+ return network.bech32 === "bc"
17
+ }
@@ -0,0 +1,92 @@
1
+ export type GetPreconditionFn<T> = () => null | { value: T }
2
+
3
+ export type InitializerFn<I, T> = (precondition: I) => T | Promise<T>
4
+
5
+ export interface AvailabilitySubscription {
6
+ unsubscribe: () => void
7
+ }
8
+
9
+ export interface Availability<T> {
10
+ subscribe: (listener: (adapter: T) => void) => AvailabilitySubscription
11
+ }
12
+
13
+ export function createAvailability<I, T>({
14
+ getPrecondition,
15
+ initializer,
16
+ pollIntervalMs = 300,
17
+ }: {
18
+ getPrecondition: GetPreconditionFn<I>
19
+ initializer: InitializerFn<I, T>
20
+ pollIntervalMs?: number
21
+ }): Availability<T> {
22
+ let cachedAdapter: T | null = null
23
+ let polling: ReturnType<typeof setInterval> | null = null
24
+ let creating = false
25
+
26
+ const listeners = new Set<(adapter: T) => void>()
27
+
28
+ const stopPolling = (): void => {
29
+ if (polling != null) {
30
+ clearInterval(polling)
31
+ polling = null
32
+ }
33
+ }
34
+
35
+ const emit = (adapter: T): void => {
36
+ cachedAdapter = adapter
37
+ for (const listener of listeners) {
38
+ listener(adapter)
39
+ }
40
+ }
41
+
42
+ const attemptCreate = async (): Promise<void> => {
43
+ if (cachedAdapter != null || creating) return
44
+ const precondition = getPrecondition()
45
+ if (precondition == null) return
46
+
47
+ creating = true
48
+ try {
49
+ const adapter = await initializer(precondition.value)
50
+ emit(adapter)
51
+ stopPolling()
52
+ } catch (error) {
53
+ console.warn("[WalletAdapter] Failed to initialize adapter", error)
54
+ } finally {
55
+ creating = false
56
+ }
57
+ }
58
+
59
+ const ensurePolling = (): void => {
60
+ if (cachedAdapter != null) return
61
+
62
+ void attemptCreate()
63
+ if (cachedAdapter != null || polling != null) return
64
+
65
+ polling = setInterval(() => {
66
+ void attemptCreate()
67
+ }, pollIntervalMs)
68
+ }
69
+
70
+ return {
71
+ subscribe: listener => {
72
+ if (cachedAdapter != null) {
73
+ listener(cachedAdapter)
74
+ return { unsubscribe: () => {} }
75
+ }
76
+
77
+ listeners.add(listener)
78
+ ensurePolling()
79
+
80
+ const subscription: AvailabilitySubscription = {
81
+ unsubscribe: () => {
82
+ listeners.delete(listener)
83
+ if (listeners.size === 0) {
84
+ stopPolling()
85
+ }
86
+ },
87
+ }
88
+
89
+ return subscription
90
+ },
91
+ }
92
+ }
@@ -0,0 +1,13 @@
1
+ export class BitcoinWalletAdapterError extends Error {
2
+ constructor(message?: string, options?: ErrorOptions) {
3
+ super(message, options)
4
+ this.name = "BitcoinWalletAdapterError"
5
+ }
6
+ }
7
+
8
+ export class UserRejectError extends BitcoinWalletAdapterError {
9
+ constructor(message?: string, options?: ErrorOptions) {
10
+ super(message, options)
11
+ this.name = "UserRejectError"
12
+ }
13
+ }
@@ -0,0 +1,10 @@
1
+ export function hasAny<T>(ary: T[]): ary is [T, ...T[]]
2
+ export function hasAny<T>(ary: readonly T[]): ary is readonly [T, ...T[]]
3
+ export function hasAny<T>(ary: readonly T[]): ary is readonly [T, ...T[]] {
4
+ return ary.length > 0
5
+ }
6
+
7
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
8
+ export function checkNever(x: never): undefined {
9
+ return
10
+ }