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,324 @@
1
+ import type { RequestFn, RpcErrorBody, RpcErrorResponse } from "@leather.io/rpc"
2
+ import { hex } from "@scure/base"
3
+ import {
4
+ addressToScriptPubKey,
5
+ getTapInternalKeyOf_P2TR_publicKey,
6
+ } from "../utils/bitcoinAddressHelpers"
7
+ import { getBitcoinNetwork } from "../utils/bitcoinNetworkHelpers"
8
+ import { UserRejectError, BitcoinWalletAdapterError } from "../utils/error"
9
+ import {
10
+ SignMessageAlgorithm,
11
+ SignMessageResult,
12
+ WalletAdapter,
13
+ WalletAdapter_onAddressesChanged_callback,
14
+ WalletAdapterAddress,
15
+ WalletAdapterAddressPurpose,
16
+ WalletAdapterAddressType,
17
+ WalletAdapterBitcoinNetwork,
18
+ WalletAdapterNotConnectedError,
19
+ } from "../WalletAdapters.types"
20
+
21
+ type GetAddressesResponseData = any
22
+
23
+ /**
24
+ * https://github.com/leather-io/mono/blob/a664e64040fed1c25abef1f8864a1c7fae5444c1/packages/rpc/src/rpc/schemas.ts#L64-L78
25
+ */
26
+ enum RpcErrorCode {
27
+ // Spec defined server errors
28
+ PARSE_ERROR = -32700,
29
+ INVALID_REQUEST = -32600,
30
+ METHOD_NOT_FOUND = -32601,
31
+ INVALID_PARAMS = -32602,
32
+ INTERNAL_ERROR = -32603,
33
+ SERVER_ERROR = -32000,
34
+ // Client defined errors
35
+ USER_REJECTION = 4001,
36
+ METHOD_NOT_SUPPORTED = 4002,
37
+ // gRPC spec borrowed
38
+ PERMISSION_DENIED = 7,
39
+ UNAUTHENTICATED = 16,
40
+ }
41
+
42
+ type Addresses = (WalletAdapterAddress & { publicKey: string })[]
43
+
44
+ const LEATHER_PROVIDER_ID = "LeatherProvider.BitcoinProvider"
45
+
46
+ const localStorageKey = `app:${LEATHER_PROVIDER_ID}:`
47
+
48
+ /**
49
+ * Derivation path (BIP-84): m/84'/0'/ account_index '/0/0
50
+ */
51
+ export class LeatherWalletAdapterImpl implements WalletAdapter {
52
+ constructor(private request: RequestFn) {}
53
+
54
+ private readonly walletDisplayName = "Leather"
55
+
56
+ private retrieveConnectedAddress(): GetAddressesResponseData | undefined {
57
+ let resp: GetAddressesResponseData | undefined
58
+ const stored = localStorage.getItem(localStorageKey) || undefined
59
+ if (stored != null) {
60
+ try {
61
+ resp = JSON.parse(stored)
62
+ if (
63
+ resp == null ||
64
+ !("addresses" in resp) ||
65
+ !Array.isArray(resp.addresses)
66
+ ) {
67
+ throw new Error("Invalid stored addresses")
68
+ }
69
+ } catch {
70
+ localStorage.removeItem(localStorageKey)
71
+ }
72
+ }
73
+ return resp
74
+ }
75
+
76
+ async connect(): Promise<void> {
77
+ if (this.retrieveConnectedAddress() == null) {
78
+ const resp = await handleRpcError(
79
+ /**
80
+ * https://leather.gitbook.io/developers/bitcoin/connect-users/get-addresses
81
+ */
82
+ this.request("getAddresses"),
83
+ )
84
+ localStorage.setItem(localStorageKey, JSON.stringify(resp))
85
+ }
86
+ }
87
+
88
+ async disconnect(): Promise<void> {
89
+ localStorage.removeItem(localStorageKey)
90
+ return Promise.resolve()
91
+ }
92
+
93
+ async getAddresses(): Promise<Addresses> {
94
+ const resp = this.retrieveConnectedAddress()
95
+
96
+ if (resp == null) {
97
+ throw new WalletAdapterNotConnectedError(this.walletDisplayName)
98
+ }
99
+
100
+ const addresses = resp.addresses.filter(
101
+ (a: any) => (a as any).symbol === "BTC" && a.type,
102
+ )
103
+
104
+ const isMainnet = addresses
105
+ .filter((a: any) => a.type === "p2tr")[0]
106
+ .address.startsWith("bc")
107
+
108
+ return addresses.map((a: any) => ({
109
+ network: isMainnet
110
+ ? WalletAdapterBitcoinNetwork.MAINNET
111
+ : WalletAdapterBitcoinNetwork.TESTNET,
112
+ purposes:
113
+ a.type === "p2tr"
114
+ ? [
115
+ WalletAdapterAddressPurpose.Ordinals,
116
+ WalletAdapterAddressPurpose.BRC20,
117
+ WalletAdapterAddressPurpose.Runes,
118
+ ]
119
+ : [WalletAdapterAddressPurpose.Bitcoin],
120
+ addressType:
121
+ a.type === "p2tr"
122
+ ? WalletAdapterAddressType.P2TR
123
+ : WalletAdapterAddressType.P2WPKH,
124
+ address: a.address,
125
+ scriptPubKey: hex.encode(
126
+ addressToScriptPubKey(
127
+ getBitcoinNetwork(isMainnet ? "mainnet" : "testnet"),
128
+ a.address,
129
+ ),
130
+ ),
131
+ publicKey: a.publicKey!,
132
+ tapInternalKey:
133
+ a.type === "p2tr"
134
+ ? hex.encode(
135
+ getTapInternalKeyOf_P2TR_publicKey(
136
+ getBitcoinNetwork(isMainnet ? "mainnet" : "testnet"),
137
+ hex.decode(a.publicKey!),
138
+ ),
139
+ )
140
+ : undefined,
141
+ }))
142
+ }
143
+
144
+ async signMessage(
145
+ address: string,
146
+ message: string,
147
+ ): Promise<SignMessageResult> {
148
+ const accounts = await this.getAddresses()
149
+
150
+ const addressType = accounts.find(a => a.address === address)?.addressType
151
+
152
+ // prettier-ignore
153
+ const paymentType =
154
+ addressType === WalletAdapterAddressType.P2WPKH ? 'p2wpkh' as const :
155
+ addressType === WalletAdapterAddressType.P2TR ? 'p2tr' as const :
156
+ undefined
157
+ if (paymentType == null) {
158
+ throw new BitcoinWalletAdapterError("Address is not supported")
159
+ }
160
+
161
+ const resp: any = await handleRpcError(
162
+ /**
163
+ * https://leather.gitbook.io/developers/bitcoin-methods/signmessage
164
+ */
165
+ this.request("signMessage", {
166
+ message,
167
+ paymentType,
168
+ }),
169
+ )
170
+
171
+ return {
172
+ algorithm:
173
+ resp.algorithm === "ecdsa"
174
+ ? SignMessageAlgorithm.ECDSA
175
+ : SignMessageAlgorithm.BIP322,
176
+ result: resp.signature,
177
+ address,
178
+ publicKey: resp.publicKey,
179
+ }
180
+ }
181
+
182
+ sendBitcoinFeeRateCapability = "required" as const
183
+ async sendBitcoin(
184
+ fromAddress: string,
185
+ receiverAddress: string,
186
+ satoshiAmount: bigint,
187
+ options?: { feeRate?: number },
188
+ ): Promise<{ txid: string }> {
189
+ const resp: any = await handleRpcError(
190
+ /**
191
+ * https://leather.gitbook.io/developers/bitcoin-methods/sendbitcoin
192
+ */
193
+ (this.request as any)("sendBitcoin", {
194
+ origin: fromAddress,
195
+ destination: receiverAddress,
196
+ amount: satoshiAmount,
197
+ feeRate: options?.feeRate,
198
+ }),
199
+ )
200
+ return { txid: resp.txid }
201
+ }
202
+
203
+ sendInscriptionFeeRateCapability = "available" as const
204
+
205
+ async sendInscription(
206
+ fromAddress: string,
207
+ receiverAddress: string,
208
+ inscriptionId: string,
209
+ options?: { feeRate?: number },
210
+ ): Promise<{ txid: string }> {
211
+ const { txid }: any = await handleRpcError(
212
+ /**
213
+ * https://leather.gitbook.io/developers/ordinals/send-ordinals
214
+ */
215
+ (this.request as any)("sendOrdinals", {
216
+ ordinals: [
217
+ {
218
+ destination: receiverAddress,
219
+ id: inscriptionId,
220
+ },
221
+ ],
222
+ paymentType: "p2wpkh",
223
+ sender: fromAddress,
224
+ feeRate: options?.feeRate,
225
+ }),
226
+ )
227
+ return { txid }
228
+ }
229
+
230
+ async signAndFinalizePsbt(
231
+ psbtHex: string,
232
+ signIndices: [address: string, signIndex: number][],
233
+ ): Promise<{
234
+ signedPsbtHex: string
235
+ }> {
236
+ const accounts = await this.getAddresses()
237
+ const signingAccount = accounts.find(
238
+ account => account.address === signIndices[0]?.[0],
239
+ )
240
+ if (signingAccount == null) {
241
+ throw new WalletAdapterNotConnectedError(this.walletDisplayName)
242
+ }
243
+
244
+ const resp: any = await handleRpcError(
245
+ /**
246
+ * https://leather.gitbook.io/developers/bitcoin-methods/signandfinalizepsbt
247
+ */
248
+ (this.request as any)("signAndFinalizePsbt", {
249
+ hex: psbtHex,
250
+ inputsToSign: signIndices.map(([address, signIndex]) => ({
251
+ address,
252
+ signingIndexes: [signIndex],
253
+ signatureHash: undefined,
254
+ disableTweakSigner: undefined,
255
+ })),
256
+ paymentType:
257
+ signingAccount.addressType === WalletAdapterAddressType.P2TR
258
+ ? "p2tr"
259
+ : "p2wpkh",
260
+ }),
261
+ )
262
+
263
+ return { signedPsbtHex: resp.hex }
264
+ }
265
+
266
+ onAddressesChanged(callback: WalletAdapter_onAddressesChanged_callback): {
267
+ unsubscribe: () => void
268
+ } {
269
+ // Leather doesn't provide event, fallback to polling
270
+ let stopped = false
271
+
272
+ const tick = async (): Promise<void> => {
273
+ if (stopped) return
274
+ try {
275
+ const addresses = await this.getAddresses()
276
+ callback({ addresses })
277
+ } catch (error) {
278
+ if (error instanceof WalletAdapterNotConnectedError) {
279
+ stopped = true
280
+ return
281
+ }
282
+ console.warn("[Leather] Failed to get addresses on change:", error)
283
+ } finally {
284
+ if (!stopped) {
285
+ setTimeout(tick, 1000)
286
+ }
287
+ }
288
+ }
289
+
290
+ setTimeout(tick, 1000)
291
+
292
+ return {
293
+ unsubscribe: () => {
294
+ stopped = true
295
+ },
296
+ }
297
+ }
298
+ }
299
+
300
+ export class LeatherWalletAdapterError extends BitcoinWalletAdapterError {
301
+ constructor(rpcError: RpcErrorResponse<RpcErrorBody>) {
302
+ super("Leather wallet error: " + rpcError.error.message, {
303
+ cause: rpcError,
304
+ })
305
+ }
306
+ }
307
+
308
+ const handleRpcError = async <T>(
309
+ promise: Promise<T>,
310
+ ): Promise<NonNullable<T>> => {
311
+ try {
312
+ return (await promise) as NonNullable<T>
313
+ } catch (e: any) {
314
+ if (e instanceof UserRejectError) {
315
+ throw e
316
+ }
317
+
318
+ if (e.error?.code === RpcErrorCode.USER_REJECTION) {
319
+ throw new UserRejectError()
320
+ }
321
+
322
+ throw new LeatherWalletAdapterError(e)
323
+ }
324
+ }
@@ -0,0 +1,35 @@
1
+ import type { RequestFn } from "@leather.io/rpc"
2
+ import { createAvailability } from "../utils/createAdapterAvailability"
3
+ import type {
4
+ WalletAdapter,
5
+ WalletAdapterStatic,
6
+ } from "../WalletAdapters.types"
7
+
8
+ const adapterId = "LeatherProvider.BitcoinProvider"
9
+
10
+ const metadata = {
11
+ name: "Leather",
12
+ iconUrl: import("../_/leather.svg").then(m => m.default),
13
+ websiteUrl: "https://leather.io/",
14
+ downloadUrl: "https://leather.io/wallet",
15
+ }
16
+
17
+ const availability = createAvailability<RequestFn, WalletAdapter>({
18
+ getPrecondition: () => {
19
+ const request = (window as any).LeatherProvider?.request as
20
+ | RequestFn
21
+ | undefined
22
+ return request == null ? null : { value: request }
23
+ },
24
+ initializer: async request => {
25
+ const { LeatherWalletAdapterImpl } =
26
+ await import("./LeatherWalletAdapter.impl")
27
+ return new LeatherWalletAdapterImpl(request)
28
+ },
29
+ })
30
+
31
+ export const LeatherWalletAdapter: WalletAdapterStatic<WalletAdapter> = {
32
+ adapterId,
33
+ metadata,
34
+ getAdapter: () => availability,
35
+ }
@@ -0,0 +1,139 @@
1
+ import { hex } from "@scure/base"
2
+ import { addressToScriptPubKey } from "../utils/bitcoinAddressHelpers"
3
+ import { getBitcoinNetwork } from "../utils/bitcoinNetworkHelpers"
4
+ import {
5
+ WalletAdapter,
6
+ WalletAdapter_onAddressesChanged_callback,
7
+ WalletAdapterAddressPurpose,
8
+ WalletAdapterAddressType,
9
+ WalletAdapterBitcoinNetwork,
10
+ } from "../WalletAdapters.types"
11
+ import {
12
+ XverseCompatibleWalletAdapterImpl_legacy,
13
+ XverseCompatibleWalletAdapterImplAddress,
14
+ } from "../utils/XverseCompatibleWalletAdapterImpl_legacy"
15
+ import { MAGICEDEN_PROVIDER_ID } from "./MagicEdenWalletAdapter"
16
+
17
+ interface MagicEdenBitcoinProviderEvents {
18
+ accountsChanged: [
19
+ event: {
20
+ accounts: import("sats-connect").GetAddressResponse["addresses"]
21
+ },
22
+ ]
23
+ }
24
+
25
+ export interface MagicEdenBitcoinProvider {
26
+ isMagicEden: boolean
27
+ on: <K extends keyof MagicEdenBitcoinProviderEvents>(
28
+ event: K,
29
+ handler: (...args: MagicEdenBitcoinProviderEvents[K]) => void,
30
+ ) => void
31
+ removeListener: <K extends keyof MagicEdenBitcoinProviderEvents>(
32
+ event: K,
33
+ handler: (...args: MagicEdenBitcoinProviderEvents[K]) => void,
34
+ ) => void
35
+ }
36
+
37
+ const localStorageKey = `app:${MAGICEDEN_PROVIDER_ID}:`
38
+
39
+ export class MagicEdenWalletAdapterImpl
40
+ extends XverseCompatibleWalletAdapterImpl_legacy
41
+ implements WalletAdapter
42
+ {
43
+ private magicEden: MagicEdenBitcoinProvider
44
+
45
+ constructor(
46
+ network: WalletAdapterBitcoinNetwork,
47
+ provider: MagicEdenBitcoinProvider,
48
+ ) {
49
+ super({
50
+ network,
51
+ localStorageKey,
52
+ walletDisplayName: "Magic Eden",
53
+ getProvider: async () => {
54
+ return provider as any
55
+ },
56
+ parseAddresses: async ({ sdk, addresses }) => {
57
+ const isMainnet = addresses
58
+ .filter(a => a.purpose === sdk.AddressPurpose.Ordinals)[0]
59
+ .address.startsWith("bc")
60
+
61
+ return addresses.flatMap(
62
+ (a): XverseCompatibleWalletAdapterImplAddress[] => {
63
+ if (a.purpose === sdk.AddressPurpose.Payment) {
64
+ return [
65
+ {
66
+ addressType: WalletAdapterAddressType.P2WPKH,
67
+ purposes: [WalletAdapterAddressPurpose.Bitcoin],
68
+ address: a.address,
69
+ network: this.network,
70
+ scriptPubKey: hex.encode(
71
+ addressToScriptPubKey(
72
+ getBitcoinNetwork(isMainnet ? "mainnet" : "testnet"),
73
+ a.address,
74
+ ),
75
+ ),
76
+ publicKey: a.publicKey,
77
+ },
78
+ ]
79
+ }
80
+
81
+ if (a.purpose === sdk.AddressPurpose.Ordinals) {
82
+ return [
83
+ {
84
+ addressType: WalletAdapterAddressType.P2TR,
85
+ purposes: [
86
+ WalletAdapterAddressPurpose.Ordinals,
87
+ WalletAdapterAddressPurpose.BRC20,
88
+ WalletAdapterAddressPurpose.Runes,
89
+ ],
90
+ address: a.address,
91
+ network: this.network,
92
+ scriptPubKey: hex.encode(
93
+ addressToScriptPubKey(
94
+ getBitcoinNetwork(isMainnet ? "mainnet" : "testnet"),
95
+ a.address,
96
+ ),
97
+ ),
98
+ publicKey: a.publicKey,
99
+ tapInternalKey: a.publicKey,
100
+ },
101
+ ]
102
+ }
103
+
104
+ return []
105
+ },
106
+ )
107
+ },
108
+ })
109
+ this.magicEden = provider
110
+ }
111
+
112
+ onAddressesChanged(callback: WalletAdapter_onAddressesChanged_callback): {
113
+ unsubscribe: () => void
114
+ } {
115
+ // MagicEden uses accountsChanged event
116
+ // https://docs-wallet.magiceden.io/bitcoin/provider-events
117
+ const provider = this.magicEden
118
+
119
+ const handler = async (event: {
120
+ accounts: import("sats-connect").GetAddressResponse["addresses"]
121
+ }): Promise<void> => {
122
+ try {
123
+ await this.updateConnectedAddress(event.accounts)
124
+ const addresses = await this.getAddresses()
125
+ callback({ addresses })
126
+ } catch (error) {
127
+ console.warn("[Magic Eden] Failed to get addresses on change:", error)
128
+ }
129
+ }
130
+
131
+ provider.on("accountsChanged", handler)
132
+
133
+ return {
134
+ unsubscribe: () => {
135
+ provider.removeListener("accountsChanged", handler)
136
+ },
137
+ }
138
+ }
139
+ }
@@ -0,0 +1,51 @@
1
+ import type {
2
+ WalletAdapter,
3
+ WalletAdapterBitcoinNetwork,
4
+ WalletAdapterStatic,
5
+ } from "../WalletAdapters.types"
6
+ import { createAvailability } from "../utils/createAdapterAvailability"
7
+ import type { MagicEdenBitcoinProvider } from "./MagicEdenWalletAdapter.impl"
8
+
9
+ declare global {
10
+ interface Window {
11
+ magicEden?: {
12
+ bitcoin?: MagicEdenBitcoinProvider
13
+ }
14
+ }
15
+ }
16
+
17
+ export const MAGICEDEN_PROVIDER_ID = "magiceden.bitcoin"
18
+
19
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
20
+ const buildAvailability = (network: WalletAdapterBitcoinNetwork) =>
21
+ createAvailability<MagicEdenBitcoinProvider, WalletAdapter>({
22
+ getPrecondition: () => {
23
+ const provider = window.magicEden?.bitcoin
24
+ return provider?.isMagicEden ? { value: provider } : null
25
+ },
26
+ initializer: async provider => {
27
+ const { MagicEdenWalletAdapterImpl } =
28
+ await import("./MagicEdenWalletAdapter.impl")
29
+ return new MagicEdenWalletAdapterImpl(network, provider)
30
+ },
31
+ })
32
+
33
+ export const MagicEdenWalletAdapterFactory = (
34
+ network: WalletAdapterBitcoinNetwork,
35
+ ): WalletAdapterStatic<WalletAdapter> => {
36
+ const availability = buildAvailability(network)
37
+
38
+ return {
39
+ adapterId: MAGICEDEN_PROVIDER_ID,
40
+ /**
41
+ * https://docs-wallet.magiceden.io/resources/logos-and-brand-assets
42
+ */
43
+ metadata: {
44
+ name: "Magic Eden",
45
+ iconUrl: import("../_/magiceden.png").then(m => m.default),
46
+ websiteUrl: "https://wallet.magiceden.io/",
47
+ downloadUrl: "https://wallet.magiceden.io/download",
48
+ },
49
+ getAdapter: () => availability,
50
+ }
51
+ }