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,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
|
+
}
|