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.
- package/README.md +208 -0
- package/lib/BitcoinConnectionProvider.d.ts +23 -0
- package/lib/BitcoinWalletAdapterConnector-Bq835yj0.mjs +123 -0
- package/lib/BitcoinWalletAdapterConnector-Bq835yj0.mjs.map +1 -0
- package/lib/BitcoinWalletAdapterConnector-DMef0iHV.js +2 -0
- package/lib/BitcoinWalletAdapterConnector-DMef0iHV.js.map +1 -0
- package/lib/BitcoinWalletAdapterConnector.d.ts +30 -0
- package/lib/BitgetWalletAdapter.impl-C_HLO7Oi.mjs +10 -0
- package/lib/BitgetWalletAdapter.impl-C_HLO7Oi.mjs.map +1 -0
- package/lib/BitgetWalletAdapter.impl-CxnKMf7U.js +2 -0
- package/lib/BitgetWalletAdapter.impl-CxnKMf7U.js.map +1 -0
- package/lib/LeatherWalletAdapter.impl-B2QgX_tO.js +2 -0
- package/lib/LeatherWalletAdapter.impl-B2QgX_tO.js.map +1 -0
- package/lib/LeatherWalletAdapter.impl-RUYx555r.mjs +184 -0
- package/lib/LeatherWalletAdapter.impl-RUYx555r.mjs.map +1 -0
- package/lib/MagicEdenWalletAdapter.impl-CrA6SGvG.mjs +235 -0
- package/lib/MagicEdenWalletAdapter.impl-CrA6SGvG.mjs.map +1 -0
- package/lib/MagicEdenWalletAdapter.impl-Di3Nu2S5.js +2 -0
- package/lib/MagicEdenWalletAdapter.impl-Di3Nu2S5.js.map +1 -0
- package/lib/OkxWalletAdapter.impl-BepoUL1B.mjs +67 -0
- package/lib/OkxWalletAdapter.impl-BepoUL1B.mjs.map +1 -0
- package/lib/OkxWalletAdapter.impl-C8kesjGu.js +2 -0
- package/lib/OkxWalletAdapter.impl-C8kesjGu.js.map +1 -0
- package/lib/UnisatCompatibleWalletAdapterImpl-Cq2Oqk1b.js +2 -0
- package/lib/UnisatCompatibleWalletAdapterImpl-Cq2Oqk1b.js.map +1 -0
- package/lib/UnisatCompatibleWalletAdapterImpl-M38FqkZI.mjs +137 -0
- package/lib/UnisatCompatibleWalletAdapterImpl-M38FqkZI.mjs.map +1 -0
- package/lib/UnisatWalletAdapter.impl-CJB22se8.mjs +14 -0
- package/lib/UnisatWalletAdapter.impl-CJB22se8.mjs.map +1 -0
- package/lib/UnisatWalletAdapter.impl-EISvxdpc.js +2 -0
- package/lib/UnisatWalletAdapter.impl-EISvxdpc.js.map +1 -0
- package/lib/WalletAdapters.types-CnvOqHFH.mjs +32 -0
- package/lib/WalletAdapters.types-CnvOqHFH.mjs.map +1 -0
- package/lib/WalletAdapters.types-De_x1lzr.js +2 -0
- package/lib/WalletAdapters.types-De_x1lzr.js.map +1 -0
- package/lib/WalletAdapters.types.d.ts +110 -0
- package/lib/XverseCompatibleWalletAdapterImpl-Bf-BK5VK.js +2 -0
- package/lib/XverseCompatibleWalletAdapterImpl-Bf-BK5VK.js.map +1 -0
- package/lib/XverseCompatibleWalletAdapterImpl-DXKnO_-V.mjs +151 -0
- package/lib/XverseCompatibleWalletAdapterImpl-DXKnO_-V.mjs.map +1 -0
- package/lib/XverseWalletAdapter.impl-CZO0RQva.mjs +105 -0
- package/lib/XverseWalletAdapter.impl-CZO0RQva.mjs.map +1 -0
- package/lib/XverseWalletAdapter.impl-lJwMi-Iv.js +2 -0
- package/lib/XverseWalletAdapter.impl-lJwMi-Iv.js.map +1 -0
- package/lib/adapters/BitgetWalletAdapter.d.ts +2 -0
- package/lib/adapters/BitgetWalletAdapter.impl.d.ts +8 -0
- package/lib/adapters/LeatherWalletAdapter.d.ts +2 -0
- package/lib/adapters/LeatherWalletAdapter.impl.d.ts +41 -0
- package/lib/adapters/MagicEdenWalletAdapter.d.ts +11 -0
- package/lib/adapters/MagicEdenWalletAdapter.impl.d.ts +22 -0
- package/lib/adapters/MockAddressWalletAdapter.d.ts +33 -0
- package/lib/adapters/OkxWalletAdapter.d.ts +2 -0
- package/lib/adapters/OkxWalletAdapter.impl.d.ts +51 -0
- package/lib/adapters/UnisatWalletAdapter.d.ts +2 -0
- package/lib/adapters/UnisatWalletAdapter.impl.d.ts +14 -0
- package/lib/adapters/XverseWalletAdapter.d.ts +3 -0
- package/lib/adapters/XverseWalletAdapter.impl.d.ts +14 -0
- package/lib/adapters/index.d.ts +7 -0
- package/lib/adapters.js +2 -0
- package/lib/adapters.js.map +1 -0
- package/lib/adapters.mjs +11 -0
- package/lib/adapters.mjs.map +1 -0
- package/lib/bitget-C7oB4Ffq.mjs +5 -0
- package/lib/bitget-C7oB4Ffq.mjs.map +1 -0
- package/lib/bitget-DXnsxx_y.js +2 -0
- package/lib/bitget-DXnsxx_y.js.map +1 -0
- package/lib/index-CaV3F1Nm.js +424 -0
- package/lib/index-CaV3F1Nm.js.map +1 -0
- package/lib/index-CcQUdePc.mjs +12224 -0
- package/lib/index-CcQUdePc.mjs.map +1 -0
- package/lib/index-D7YwhNAG.mjs +3946 -0
- package/lib/index-D7YwhNAG.mjs.map +1 -0
- package/lib/index-Zx0KcpYx.js +2 -0
- package/lib/index-Zx0KcpYx.js.map +1 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -0
- package/lib/index.mjs +20 -0
- package/lib/index.mjs.map +1 -0
- package/lib/leather-BoQG_CPn.mjs +5 -0
- package/lib/leather-BoQG_CPn.mjs.map +1 -0
- package/lib/leather-DJ8nWmM8.js +2 -0
- package/lib/leather-DJ8nWmM8.js.map +1 -0
- package/lib/magiceden-B36CEQa6.js +2 -0
- package/lib/magiceden-B36CEQa6.js.map +1 -0
- package/lib/magiceden-Cg7d3agI.mjs +5 -0
- package/lib/magiceden-Cg7d3agI.mjs.map +1 -0
- package/lib/misc-B5EWO_dn.mjs +10 -0
- package/lib/misc-B5EWO_dn.mjs.map +1 -0
- package/lib/misc-CigR0RqC.js +2 -0
- package/lib/misc-CigR0RqC.js.map +1 -0
- package/lib/okx-ChwzM0dK.js +2 -0
- package/lib/okx-ChwzM0dK.js.map +1 -0
- package/lib/okx-DWbHwazu.mjs +5 -0
- package/lib/okx-DWbHwazu.mjs.map +1 -0
- package/lib/react.d.ts +2 -0
- package/lib/react.js +2 -0
- package/lib/react.js.map +1 -0
- package/lib/react.mjs +128 -0
- package/lib/react.mjs.map +1 -0
- package/lib/transaction-CiLOYSE_.mjs +1063 -0
- package/lib/transaction-CiLOYSE_.mjs.map +1 -0
- package/lib/transaction-CzdnbXSo.js +2 -0
- package/lib/transaction-CzdnbXSo.js.map +1 -0
- package/lib/unisat-BvZW5h0U.js +2 -0
- package/lib/unisat-BvZW5h0U.js.map +1 -0
- package/lib/unisat-pLgab4nG.mjs +5 -0
- package/lib/unisat-pLgab4nG.mjs.map +1 -0
- package/lib/utils/StateChannel.d.ts +14 -0
- package/lib/utils/UnisatCompatibleWalletAdapterImpl.d.ts +99 -0
- package/lib/utils/XverseCompatibleWalletAdapterImpl.d.ts +80 -0
- package/lib/utils/XverseCompatibleWalletAdapterImpl_legacy.d.ts +44 -0
- package/lib/utils/bitcoinAddressHelpers.d.ts +14 -0
- package/lib/utils/bitcoinNetworkHelpers.d.ts +4 -0
- package/lib/utils/createAdapterAvailability.d.ts +15 -0
- package/lib/utils/error.d.ts +6 -0
- package/lib/utils/misc.d.ts +3 -0
- package/lib/xverse-IKOHyGi-.js +2 -0
- package/lib/xverse-IKOHyGi-.js.map +1 -0
- package/lib/xverse-iHLNanCB.mjs +5 -0
- package/lib/xverse-iHLNanCB.mjs.map +1 -0
- package/package.json +86 -0
- package/src/BitcoinConnectionProvider.stories.tsx +329 -0
- package/src/BitcoinConnectionProvider.tsx +234 -0
- package/src/BitcoinWalletAdapterConnector.ts +166 -0
- package/src/WalletAdapters.types.ts +154 -0
- package/src/_/bitget.png +0 -0
- package/src/_/leather.svg +4 -0
- package/src/_/magiceden.png +0 -0
- package/src/_/okx.png +0 -0
- package/src/_/unisat.svg +31 -0
- package/src/_/xverse.png +0 -0
- package/src/adapters/BitgetWalletAdapter.impl.ts +22 -0
- package/src/adapters/BitgetWalletAdapter.ts +44 -0
- package/src/adapters/LeatherWalletAdapter.impl.ts +324 -0
- package/src/adapters/LeatherWalletAdapter.ts +35 -0
- package/src/adapters/MagicEdenWalletAdapter.impl.ts +139 -0
- package/src/adapters/MagicEdenWalletAdapter.ts +51 -0
- package/src/adapters/MockAddressWalletAdapter.ts +199 -0
- package/src/adapters/OkxWalletAdapter.impl.ts +168 -0
- package/src/adapters/OkxWalletAdapter.ts +37 -0
- package/src/adapters/UnisatWalletAdapter.impl.ts +32 -0
- package/src/adapters/UnisatWalletAdapter.ts +50 -0
- package/src/adapters/XverseWalletAdapter.impl.ts +150 -0
- package/src/adapters/XverseWalletAdapter.ts +37 -0
- package/src/adapters/index.ts +7 -0
- package/src/env.d.ts +9 -0
- package/src/index.ts +3 -0
- package/src/react.ts +9 -0
- package/src/utils/StateChannel.ts +39 -0
- package/src/utils/UnisatCompatibleWalletAdapterImpl.ts +342 -0
- package/src/utils/XverseCompatibleWalletAdapterImpl.ts +288 -0
- package/src/utils/XverseCompatibleWalletAdapterImpl_legacy.ts +278 -0
- package/src/utils/bitcoinAddressHelpers.ts +132 -0
- package/src/utils/bitcoinNetworkHelpers.ts +17 -0
- package/src/utils/createAdapterAvailability.ts +92 -0
- package/src/utils/error.ts +13 -0
- 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
|
+
}
|