@dynamic-labs-sdk/bitcoin 0.1.0-alpha.21 → 0.1.0-alpha.22
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/CHANGELOG.md +6 -0
- package/addBitcoinInjectedWalletsExtension.cjs.js +490 -198
- package/addBitcoinInjectedWalletsExtension.esm.js +494 -202
- package/index.cjs.js +1 -0
- package/index.esm.js +1 -0
- package/injected.cjs.js +1 -0
- package/injected.esm.js +1 -0
- package/package.json +5 -4
- package/src/injected/addBitcoinInjectedWalletsExtension/addBitcoinInjectedWalletsExtension.d.ts.map +1 -1
- package/src/injected/utils/magicEden/createMagicEdenBitcoinWalletProvider/createMagicEdenBitcoinWalletProvider.d.ts +11 -0
- package/src/injected/utils/magicEden/createMagicEdenBitcoinWalletProvider/createMagicEdenBitcoinWalletProvider.d.ts.map +1 -0
- package/src/injected/utils/magicEden/createMagicEdenBitcoinWalletProvider/index.d.ts +2 -0
- package/src/injected/utils/magicEden/createMagicEdenBitcoinWalletProvider/index.d.ts.map +1 -0
- package/src/injected/utils/magicEden/getMagicEdenInjectedProvider/getMagicEdenInjectedProvider.d.ts +2 -0
- package/src/injected/utils/magicEden/getMagicEdenInjectedProvider/getMagicEdenInjectedProvider.d.ts.map +1 -0
- package/src/injected/utils/magicEden/getMagicEdenInjectedProvider/index.d.ts +2 -0
- package/src/injected/utils/magicEden/getMagicEdenInjectedProvider/index.d.ts.map +1 -0
- package/src/injected/utils/phantom/createPhantomBitcoinWalletProvider/createPhantomBitcoinWalletProvider.d.ts.map +1 -1
- package/src/injected/utils/unisat/createUnisatWalletProvider/createUnisatWalletProvider.d.ts.map +1 -1
- package/src/injected/utils/xverse/createXverseBitcoinWalletProvider/createXverseBitcoinWalletProvider.d.ts.map +1 -1
- package/src/utils/parseBitcoinConnectionResult/index.d.ts +2 -0
- package/src/utils/parseBitcoinConnectionResult/index.d.ts.map +1 -0
- package/src/utils/parseBitcoinConnectionResult/parseBitcoinConnectionResult.d.ts +12 -0
- package/src/utils/parseBitcoinConnectionResult/parseBitcoinConnectionResult.d.ts.map +1 -0
|
@@ -4,10 +4,11 @@ var core = require('@dynamic-labs-sdk/client/core');
|
|
|
4
4
|
var client = require('@dynamic-labs-sdk/client');
|
|
5
5
|
var sdkApiCore = require('@dynamic-labs/sdk-api-core');
|
|
6
6
|
var bitcoinjsLib = require('bitcoinjs-lib');
|
|
7
|
+
var jsontokens = require('jsontokens');
|
|
7
8
|
var satsConnect = require('sats-connect');
|
|
8
9
|
|
|
9
10
|
var name = "@dynamic-labs-sdk/bitcoin";
|
|
10
|
-
var version = "0.1.0-alpha.
|
|
11
|
+
var version = "0.1.0-alpha.22";
|
|
11
12
|
|
|
12
13
|
function _extends() {
|
|
13
14
|
_extends = Object.assign || function assign(target) {
|
|
@@ -64,6 +65,468 @@ const registerBitcoinNetworkProviderBuilder = (client)=>{
|
|
|
64
65
|
});
|
|
65
66
|
};
|
|
66
67
|
|
|
68
|
+
class InvalidPsbtError extends client.BaseError {
|
|
69
|
+
constructor(message){
|
|
70
|
+
super({
|
|
71
|
+
cause: null,
|
|
72
|
+
code: 'invalid_psbt_error',
|
|
73
|
+
docsUrl: null,
|
|
74
|
+
name: 'InvalidPsbtError',
|
|
75
|
+
shortMessage: message
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const convertNetworkIdForPsbt = (networkId)=>{
|
|
81
|
+
if (networkId === '1') {
|
|
82
|
+
return bitcoinjsLib.networks.bitcoin;
|
|
83
|
+
}
|
|
84
|
+
if (networkId === '2') {
|
|
85
|
+
return bitcoinjsLib.networks.testnet;
|
|
86
|
+
}
|
|
87
|
+
return undefined;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const convertNetworkIdToSatsConnectNetworkType = (networkId)=>{
|
|
91
|
+
if (networkId === '2') {
|
|
92
|
+
return satsConnect.BitcoinNetworkType.Testnet;
|
|
93
|
+
}
|
|
94
|
+
// fallback to mainnet
|
|
95
|
+
return satsConnect.BitcoinNetworkType.Mainnet;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* This method will return the signature hash type for the current input.
|
|
100
|
+
* If there is a sighashType, it will return that
|
|
101
|
+
* If there is a witnessUtxo AND it is a taproot address, then it will return SIGHASH_DEFAULT
|
|
102
|
+
* Otherwise, it will return SIGHASH_ALL
|
|
103
|
+
*/ const getSigHashType = ({ input })=>{
|
|
104
|
+
var _input_witnessUtxo;
|
|
105
|
+
if (input == null ? void 0 : input.sighashType) {
|
|
106
|
+
return input.sighashType;
|
|
107
|
+
}
|
|
108
|
+
let isTaprootAddress = false;
|
|
109
|
+
if ((_input_witnessUtxo = input.witnessUtxo) == null ? void 0 : _input_witnessUtxo.script) {
|
|
110
|
+
try {
|
|
111
|
+
bitcoinjsLib.payments.p2tr({
|
|
112
|
+
output: input.witnessUtxo.script
|
|
113
|
+
});
|
|
114
|
+
isTaprootAddress = true;
|
|
115
|
+
} catch (e) {
|
|
116
|
+
// do nothing - address is not taproot
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return isTaprootAddress ? bitcoinjsLib.Transaction.SIGHASH_DEFAULT : bitcoinjsLib.Transaction.SIGHASH_ALL;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const validateSigHash = ({ allowedSigHashTypes, input })=>{
|
|
123
|
+
const sigHashType = getSigHashType({
|
|
124
|
+
input
|
|
125
|
+
});
|
|
126
|
+
// if the request has allowedSigHashTypes, then we need to make sure that the sigHashType
|
|
127
|
+
// is a member of that array before continuing
|
|
128
|
+
if ((allowedSigHashTypes == null ? void 0 : allowedSigHashTypes.length) && !allowedSigHashTypes.includes(sigHashType)) {
|
|
129
|
+
throw new InvalidPsbtError(`sigHashType ${sigHashType} not in allowed list`);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* This method extracts the address from the input data of the psbt.
|
|
135
|
+
* The address is stored as script (Buffer) in either the witnessUtxo or nonWitnessUtxo as an output
|
|
136
|
+
*/ const extractAddressFromInput = ({ index, input, psbt })=>{
|
|
137
|
+
let extractedAddress;
|
|
138
|
+
try {
|
|
139
|
+
var _input_witnessUtxo;
|
|
140
|
+
if ((_input_witnessUtxo = input.witnessUtxo) == null ? void 0 : _input_witnessUtxo.script) {
|
|
141
|
+
extractedAddress = bitcoinjsLib.address.fromOutputScript(input.witnessUtxo.script);
|
|
142
|
+
}
|
|
143
|
+
if (input.nonWitnessUtxo) {
|
|
144
|
+
const nonWitnessTxn = bitcoinjsLib.Transaction.fromBuffer(input.nonWitnessUtxo);
|
|
145
|
+
const txOut = nonWitnessTxn.outs[psbt.txInputs[index].index];
|
|
146
|
+
extractedAddress = bitcoinjsLib.address.fromOutputScript(txOut.script);
|
|
147
|
+
}
|
|
148
|
+
return extractedAddress;
|
|
149
|
+
} catch (e) {
|
|
150
|
+
return extractedAddress;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const validateSigningAddress = ({ index, inputAtIndex, psbt, signingAddress })=>{
|
|
155
|
+
// we need to extract the address from the input at the current signing index
|
|
156
|
+
// to be able to compare it to the address provided in the request for the current index
|
|
157
|
+
const extractedAddress = extractAddressFromInput({
|
|
158
|
+
index,
|
|
159
|
+
input: inputAtIndex,
|
|
160
|
+
psbt
|
|
161
|
+
});
|
|
162
|
+
if (!extractedAddress) {
|
|
163
|
+
throw new InvalidPsbtError(`Could not extract address from input at index ${index}`);
|
|
164
|
+
}
|
|
165
|
+
if (extractedAddress !== signingAddress) {
|
|
166
|
+
throw new InvalidPsbtError(`Signing address does not match with address extracted from input at index ${index}`);
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* This method will validate the psbt against the signature data provided in the request
|
|
172
|
+
* It checks 3 things:
|
|
173
|
+
* - That the signing index exists in the input
|
|
174
|
+
* - That the address provided in the request matches the address in the input,
|
|
175
|
+
* unless the disableAddressValidation flag is set
|
|
176
|
+
* - That the sigHashType of the input is a member of the allowedSigHashTypes array
|
|
177
|
+
*/ const validatePsbt = ({ allowedSigHashTypes, psbt, signatureData })=>{
|
|
178
|
+
if (!(signatureData == null ? void 0 : signatureData.length)) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
for (const input of signatureData){
|
|
182
|
+
const { address: signingAddress, signingIndexes, // request from ME in cases of multi-sig. Steven wanted this defaulted to true
|
|
183
|
+
disableAddressValidation = true } = input;
|
|
184
|
+
if (!(signingIndexes == null ? void 0 : signingIndexes.length)) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (!signingAddress) {
|
|
188
|
+
throw new InvalidPsbtError('Missing signing address');
|
|
189
|
+
}
|
|
190
|
+
for (const index of signingIndexes){
|
|
191
|
+
const inputAtIndex = psbt.data.inputs[index];
|
|
192
|
+
if (!inputAtIndex) {
|
|
193
|
+
throw new InvalidPsbtError(`Missing input for index ${index}`);
|
|
194
|
+
}
|
|
195
|
+
if (!disableAddressValidation) {
|
|
196
|
+
validateSigningAddress({
|
|
197
|
+
index,
|
|
198
|
+
inputAtIndex,
|
|
199
|
+
psbt,
|
|
200
|
+
signingAddress
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
validateSigHash({
|
|
204
|
+
allowedSigHashTypes,
|
|
205
|
+
input: inputAtIndex
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* This method will create the psbt sign options for SatsConnect for the given request
|
|
213
|
+
* It will validate the psbt against the signature data provided in the request
|
|
214
|
+
* It will return the inputs to sign if the signature data is provided
|
|
215
|
+
*/ const getPsbtInputsToSignForSatsConnect = ({ psbt, request, isLegacySatsConnect })=>{
|
|
216
|
+
if (!request.signature) {
|
|
217
|
+
return [];
|
|
218
|
+
}
|
|
219
|
+
validatePsbt({
|
|
220
|
+
allowedSigHashTypes: request.allowedSighash,
|
|
221
|
+
psbt,
|
|
222
|
+
signatureData: request.signature
|
|
223
|
+
});
|
|
224
|
+
const inputsToSign = [];
|
|
225
|
+
for (const signature of request.signature){
|
|
226
|
+
var _signature_signingIndexes;
|
|
227
|
+
if ((_signature_signingIndexes = signature.signingIndexes) == null ? void 0 : _signature_signingIndexes.length) {
|
|
228
|
+
for (const index of signature.signingIndexes){
|
|
229
|
+
inputsToSign.push({
|
|
230
|
+
address: signature.address,
|
|
231
|
+
sigHash: isLegacySatsConnect ? getSigHashType({
|
|
232
|
+
input: psbt.data.inputs[index]
|
|
233
|
+
}) : request.allowedSighash[0],
|
|
234
|
+
signingIndexes: [
|
|
235
|
+
index
|
|
236
|
+
]
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return inputsToSign;
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const getSatsConnectSigningProtocol = (protocol)=>{
|
|
245
|
+
if (!protocol) {
|
|
246
|
+
return undefined;
|
|
247
|
+
}
|
|
248
|
+
return protocol === 'ecdsa' ? satsConnect.MessageSigningProtocols.ECDSA : satsConnect.MessageSigningProtocols.BIP322;
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
const parseBitcoinConnectionResult = ({ connectedAddresses })=>{
|
|
252
|
+
// Xverse will return other addresses/types that we don't support
|
|
253
|
+
// so we filter them out
|
|
254
|
+
const filteredAddresses = connectedAddresses.filter((address)=>address.purpose === 'ordinals' || address.purpose === 'payment');
|
|
255
|
+
const addresses = filteredAddresses.map((account)=>({
|
|
256
|
+
address: account.address,
|
|
257
|
+
publicKey: account.publicKey,
|
|
258
|
+
type: account.purpose === 'payment' ? 'payment' : 'ordinals'
|
|
259
|
+
}));
|
|
260
|
+
// put ordinals addresses first, as they should be the main walletAccount address
|
|
261
|
+
addresses.sort((a)=>a.type === 'ordinals' ? -1 : 1);
|
|
262
|
+
return {
|
|
263
|
+
addresses
|
|
264
|
+
};
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
const MAGIC_EDEN_METADATA = {
|
|
268
|
+
displayName: 'Magic Eden',
|
|
269
|
+
icon: `${core.DYNAMIC_ICONIC_SPRITE_URL}#magiceden`
|
|
270
|
+
};
|
|
271
|
+
// https://docs-wallet.magiceden.io/bitcoin/detecting-the-provider
|
|
272
|
+
const createMagicEdenBitcoinWalletProvider = ({ dynamicClient, satsConnectProvider })=>{
|
|
273
|
+
const chain = 'BTC';
|
|
274
|
+
const walletProviderType = sdkApiCore.WalletProviderEnum.BrowserExtension;
|
|
275
|
+
const key = core.formatWalletProviderKey({
|
|
276
|
+
chain,
|
|
277
|
+
displayName: MAGIC_EDEN_METADATA.displayName,
|
|
278
|
+
walletProviderType
|
|
279
|
+
});
|
|
280
|
+
const getNetworkPayload = async ()=>{
|
|
281
|
+
const activeNetworkId = await getActiveNetworkId();
|
|
282
|
+
const networkType = convertNetworkIdToSatsConnectNetworkType(activeNetworkId.networkId);
|
|
283
|
+
return {
|
|
284
|
+
network: {
|
|
285
|
+
type: networkType
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
};
|
|
289
|
+
const connect = async ()=>{
|
|
290
|
+
const { network } = await getNetworkPayload();
|
|
291
|
+
return new Promise((resolve, reject)=>{
|
|
292
|
+
void satsConnect.getAddress({
|
|
293
|
+
getProvider: async ()=>satsConnectProvider,
|
|
294
|
+
onCancel: ()=>{
|
|
295
|
+
reject(new client.UserRejectedError({
|
|
296
|
+
action: 'connect'
|
|
297
|
+
}));
|
|
298
|
+
},
|
|
299
|
+
onFinish: async (response)=>{
|
|
300
|
+
const { addresses: connectedAddresses } = response;
|
|
301
|
+
const { addresses } = parseBitcoinConnectionResult({
|
|
302
|
+
connectedAddresses
|
|
303
|
+
});
|
|
304
|
+
resolve({
|
|
305
|
+
addresses
|
|
306
|
+
});
|
|
307
|
+
},
|
|
308
|
+
payload: {
|
|
309
|
+
message: 'Address for receiving Ordinals and payments',
|
|
310
|
+
network,
|
|
311
|
+
purposes: [
|
|
312
|
+
satsConnect.AddressPurpose.Ordinals,
|
|
313
|
+
satsConnect.AddressPurpose.Payment
|
|
314
|
+
]
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
});
|
|
318
|
+
};
|
|
319
|
+
const getActiveNetworkId = async ()=>core.getActiveNetworkIdFromLastKnownRegistry({
|
|
320
|
+
client: dynamicClient,
|
|
321
|
+
walletProviderKey: key
|
|
322
|
+
});
|
|
323
|
+
// there's no specific method to get connected addresses in Magic Eden
|
|
324
|
+
// so we use the connect method to get the addresses
|
|
325
|
+
const getConnectedAddresses = async ()=>{
|
|
326
|
+
const { addresses } = await connect();
|
|
327
|
+
return {
|
|
328
|
+
addresses: addresses.map((address)=>address.address)
|
|
329
|
+
};
|
|
330
|
+
};
|
|
331
|
+
const sendBitcoin = async ({ transaction, walletAccount })=>{
|
|
332
|
+
await client.assertWalletAccountSigningAvailability({
|
|
333
|
+
walletAccount
|
|
334
|
+
}, dynamicClient);
|
|
335
|
+
var _getWalletAccountAddressByType;
|
|
336
|
+
// should use payment address if available
|
|
337
|
+
const senderAddress = (_getWalletAccountAddressByType = client.getWalletAccountAddressByType({
|
|
338
|
+
type: 'payment',
|
|
339
|
+
walletAccount
|
|
340
|
+
})) != null ? _getWalletAccountAddressByType : walletAccount.address;
|
|
341
|
+
const { network } = await getNetworkPayload();
|
|
342
|
+
return new Promise((resolve, reject)=>{
|
|
343
|
+
void satsConnect.sendBtcTransaction({
|
|
344
|
+
getProvider: async ()=>satsConnectProvider,
|
|
345
|
+
onCancel: ()=>{
|
|
346
|
+
reject(new client.UserRejectedError({
|
|
347
|
+
action: 'sendBitcoin'
|
|
348
|
+
}));
|
|
349
|
+
},
|
|
350
|
+
onFinish: (response)=>{
|
|
351
|
+
resolve({
|
|
352
|
+
transactionId: response
|
|
353
|
+
});
|
|
354
|
+
},
|
|
355
|
+
payload: {
|
|
356
|
+
network,
|
|
357
|
+
recipients: [
|
|
358
|
+
{
|
|
359
|
+
address: transaction.recipientAddress,
|
|
360
|
+
amountSats: BigInt(transaction.amount)
|
|
361
|
+
}
|
|
362
|
+
],
|
|
363
|
+
senderAddress
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
});
|
|
367
|
+
};
|
|
368
|
+
const signMessage = async ({ message, walletAccount, protocol, addressType })=>{
|
|
369
|
+
core.assertDefined(walletAccount, 'Wallet account not found');
|
|
370
|
+
await client.assertWalletAccountSigningAvailability({
|
|
371
|
+
walletAccount
|
|
372
|
+
}, dynamicClient);
|
|
373
|
+
const { network } = await getNetworkPayload();
|
|
374
|
+
let addressToSignWith = walletAccount.address;
|
|
375
|
+
if (addressType) {
|
|
376
|
+
var _getWalletAccountAddressByType;
|
|
377
|
+
addressToSignWith = (_getWalletAccountAddressByType = client.getWalletAccountAddressByType({
|
|
378
|
+
type: addressType,
|
|
379
|
+
walletAccount
|
|
380
|
+
})) != null ? _getWalletAccountAddressByType : addressToSignWith;
|
|
381
|
+
}
|
|
382
|
+
return new Promise((resolve, reject)=>{
|
|
383
|
+
void satsConnect.signMessage({
|
|
384
|
+
getProvider: async ()=>satsConnectProvider,
|
|
385
|
+
onCancel: ()=>{
|
|
386
|
+
reject(new client.UserRejectedError({
|
|
387
|
+
action: 'signMessage'
|
|
388
|
+
}));
|
|
389
|
+
},
|
|
390
|
+
onFinish: async (response)=>{
|
|
391
|
+
resolve({
|
|
392
|
+
signature: response
|
|
393
|
+
});
|
|
394
|
+
},
|
|
395
|
+
payload: {
|
|
396
|
+
address: addressToSignWith,
|
|
397
|
+
message: message,
|
|
398
|
+
network,
|
|
399
|
+
protocol: getSatsConnectSigningProtocol(protocol)
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
};
|
|
404
|
+
const signPsbt = async ({ request, walletAccount })=>{
|
|
405
|
+
if (!request.allowedSighash.length) {
|
|
406
|
+
throw new InvalidPsbtError('allowedSighash cannot be an empty array');
|
|
407
|
+
}
|
|
408
|
+
await client.assertWalletAccountSigningAvailability({
|
|
409
|
+
walletAccount
|
|
410
|
+
}, dynamicClient);
|
|
411
|
+
const activeNetworkId = await getActiveNetworkId();
|
|
412
|
+
const network = convertNetworkIdForPsbt(activeNetworkId.networkId);
|
|
413
|
+
const { network: networkPayload } = await getNetworkPayload();
|
|
414
|
+
const psbtFromBase64 = bitcoinjsLib.Psbt.fromBase64(request.unsignedPsbtBase64, {
|
|
415
|
+
network
|
|
416
|
+
});
|
|
417
|
+
const inputsToSign = getPsbtInputsToSignForSatsConnect({
|
|
418
|
+
isLegacySatsConnect: true,
|
|
419
|
+
psbt: psbtFromBase64,
|
|
420
|
+
request
|
|
421
|
+
});
|
|
422
|
+
return new Promise((resolve, reject)=>{
|
|
423
|
+
void satsConnect.signTransaction({
|
|
424
|
+
getProvider: async ()=>satsConnectProvider,
|
|
425
|
+
onCancel: ()=>{
|
|
426
|
+
reject(new client.UserRejectedError({
|
|
427
|
+
action: 'signPsbt'
|
|
428
|
+
}));
|
|
429
|
+
},
|
|
430
|
+
onFinish: (response)=>{
|
|
431
|
+
resolve({
|
|
432
|
+
signedPsbt: response.psbtBase64
|
|
433
|
+
});
|
|
434
|
+
},
|
|
435
|
+
payload: {
|
|
436
|
+
broadcast: false,
|
|
437
|
+
inputsToSign,
|
|
438
|
+
message: 'Sign Pbst',
|
|
439
|
+
network: networkPayload,
|
|
440
|
+
psbtBase64: request.unsignedPsbtBase64
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
});
|
|
444
|
+
};
|
|
445
|
+
const signPsbts = async ({ requests, walletAccount })=>{
|
|
446
|
+
await client.assertWalletAccountSigningAvailability({
|
|
447
|
+
walletAccount
|
|
448
|
+
}, dynamicClient);
|
|
449
|
+
const activeNetworkId = await getActiveNetworkId();
|
|
450
|
+
const network = convertNetworkIdForPsbt(activeNetworkId.networkId);
|
|
451
|
+
const { network: networkPayload } = await getNetworkPayload();
|
|
452
|
+
const psbtsToSign = [];
|
|
453
|
+
for (const request of requests){
|
|
454
|
+
const psbtFromBase64 = bitcoinjsLib.Psbt.fromBase64(request.unsignedPsbtBase64, {
|
|
455
|
+
network
|
|
456
|
+
});
|
|
457
|
+
const inputsToSign = getPsbtInputsToSignForSatsConnect({
|
|
458
|
+
isLegacySatsConnect: true,
|
|
459
|
+
psbt: psbtFromBase64,
|
|
460
|
+
request
|
|
461
|
+
});
|
|
462
|
+
psbtsToSign.push({
|
|
463
|
+
inputsToSign,
|
|
464
|
+
psbtBase64: request.unsignedPsbtBase64
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
const signMultipleTransactionPayload = {
|
|
468
|
+
message: 'Sign Pbsts',
|
|
469
|
+
network: networkPayload,
|
|
470
|
+
psbts: psbtsToSign
|
|
471
|
+
};
|
|
472
|
+
// Magic Eden has implemented their own signTransactions feature, and have requested us
|
|
473
|
+
// to ensure we use it rather than the general Sats Connect API for this method
|
|
474
|
+
const request = jsontokens.createUnsecuredToken(signMultipleTransactionPayload);
|
|
475
|
+
const response = await satsConnectProvider.signMultipleTransactions(request);
|
|
476
|
+
return {
|
|
477
|
+
signedPsbts: response.map((response)=>response.psbtBase64)
|
|
478
|
+
};
|
|
479
|
+
};
|
|
480
|
+
const { getEventEmitter, cleanupEventEmitter } = core.createWalletProviderEventEmitter({
|
|
481
|
+
removeEventListeners: ({ setupReturnValue })=>{
|
|
482
|
+
core.assertDefined(setupReturnValue, 'Setup return value not defined');
|
|
483
|
+
setupReturnValue.removeAccountChangeListener();
|
|
484
|
+
},
|
|
485
|
+
setupEventListeners: ({ handleAccountsChanged })=>{
|
|
486
|
+
const removeAccountChangeListener = satsConnect.addListener('accountsChanged', (accounts)=>{
|
|
487
|
+
if (!accounts) {
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
handleAccountsChanged({
|
|
491
|
+
addresses: accounts.map((account)=>account.address)
|
|
492
|
+
});
|
|
493
|
+
});
|
|
494
|
+
return {
|
|
495
|
+
removeAccountChangeListener
|
|
496
|
+
};
|
|
497
|
+
},
|
|
498
|
+
supportedEvents: [
|
|
499
|
+
'accountsChanged'
|
|
500
|
+
]
|
|
501
|
+
});
|
|
502
|
+
const terminate = async ()=>{
|
|
503
|
+
cleanupEventEmitter();
|
|
504
|
+
};
|
|
505
|
+
return {
|
|
506
|
+
chain,
|
|
507
|
+
connect,
|
|
508
|
+
get events () {
|
|
509
|
+
return getEventEmitter();
|
|
510
|
+
},
|
|
511
|
+
getActiveNetworkId,
|
|
512
|
+
getConnectedAddresses,
|
|
513
|
+
groupKey: core.formatWalletProviderGroupKey(MAGIC_EDEN_METADATA.displayName),
|
|
514
|
+
key,
|
|
515
|
+
metadata: MAGIC_EDEN_METADATA,
|
|
516
|
+
sendBitcoin,
|
|
517
|
+
signMessage,
|
|
518
|
+
signPsbt,
|
|
519
|
+
signPsbts,
|
|
520
|
+
terminate,
|
|
521
|
+
walletProviderType
|
|
522
|
+
};
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
const MAGIC_EDEN_BITCOIN_PROVIDER_LOCATOR = 'magicEden.bitcoin';
|
|
526
|
+
const getMagicEdenInjectedProvider = ()=>{
|
|
527
|
+
return core.getInjectedProviderFromWindow(MAGIC_EDEN_BITCOIN_PROVIDER_LOCATOR);
|
|
528
|
+
};
|
|
529
|
+
|
|
67
530
|
const PHANTOM_METADATA = {
|
|
68
531
|
displayName: 'Phantom',
|
|
69
532
|
icon: `${core.DYNAMIC_ICONIC_SPRITE_URL}#phantom`
|
|
@@ -169,7 +632,10 @@ const createPhantomBitcoinWalletProvider = ({ dynamicClient, injectedProvider })
|
|
|
169
632
|
addresses: accounts.map((account)=>account.address)
|
|
170
633
|
});
|
|
171
634
|
});
|
|
172
|
-
}
|
|
635
|
+
},
|
|
636
|
+
supportedEvents: [
|
|
637
|
+
'accountsChanged'
|
|
638
|
+
]
|
|
173
639
|
});
|
|
174
640
|
const terminate = async ()=>{
|
|
175
641
|
cleanupEventEmitter();
|
|
@@ -198,131 +664,6 @@ const getPhantomInjectedProvider = ()=>{
|
|
|
198
664
|
return core.getInjectedProviderFromWindow(PHANTOM_BITCOIN_PROVIDER_LOCATOR);
|
|
199
665
|
};
|
|
200
666
|
|
|
201
|
-
class InvalidPsbtError extends client.BaseError {
|
|
202
|
-
constructor(message){
|
|
203
|
-
super({
|
|
204
|
-
cause: null,
|
|
205
|
-
code: 'invalid_psbt_error',
|
|
206
|
-
docsUrl: null,
|
|
207
|
-
name: 'InvalidPsbtError',
|
|
208
|
-
shortMessage: message
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* This method will return the signature hash type for the current input.
|
|
215
|
-
* If there is a sighashType, it will return that
|
|
216
|
-
* If there is a witnessUtxo AND it is a taproot address, then it will return SIGHASH_DEFAULT
|
|
217
|
-
* Otherwise, it will return SIGHASH_ALL
|
|
218
|
-
*/ const getSigHashType = ({ input })=>{
|
|
219
|
-
var _input_witnessUtxo;
|
|
220
|
-
if (input == null ? void 0 : input.sighashType) {
|
|
221
|
-
return input.sighashType;
|
|
222
|
-
}
|
|
223
|
-
let isTaprootAddress = false;
|
|
224
|
-
if ((_input_witnessUtxo = input.witnessUtxo) == null ? void 0 : _input_witnessUtxo.script) {
|
|
225
|
-
try {
|
|
226
|
-
bitcoinjsLib.payments.p2tr({
|
|
227
|
-
output: input.witnessUtxo.script
|
|
228
|
-
});
|
|
229
|
-
isTaprootAddress = true;
|
|
230
|
-
} catch (e) {
|
|
231
|
-
// do nothing - address is not taproot
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
return isTaprootAddress ? bitcoinjsLib.Transaction.SIGHASH_DEFAULT : bitcoinjsLib.Transaction.SIGHASH_ALL;
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
const validateSigHash = ({ allowedSigHashTypes, input })=>{
|
|
238
|
-
const sigHashType = getSigHashType({
|
|
239
|
-
input
|
|
240
|
-
});
|
|
241
|
-
// if the request has allowedSigHashTypes, then we need to make sure that the sigHashType
|
|
242
|
-
// is a member of that array before continuing
|
|
243
|
-
if ((allowedSigHashTypes == null ? void 0 : allowedSigHashTypes.length) && !allowedSigHashTypes.includes(sigHashType)) {
|
|
244
|
-
throw new InvalidPsbtError(`sigHashType ${sigHashType} not in allowed list`);
|
|
245
|
-
}
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* This method extracts the address from the input data of the psbt.
|
|
250
|
-
* The address is stored as script (Buffer) in either the witnessUtxo or nonWitnessUtxo as an output
|
|
251
|
-
*/ const extractAddressFromInput = ({ index, input, psbt })=>{
|
|
252
|
-
let extractedAddress;
|
|
253
|
-
try {
|
|
254
|
-
var _input_witnessUtxo;
|
|
255
|
-
if ((_input_witnessUtxo = input.witnessUtxo) == null ? void 0 : _input_witnessUtxo.script) {
|
|
256
|
-
extractedAddress = bitcoinjsLib.address.fromOutputScript(input.witnessUtxo.script);
|
|
257
|
-
}
|
|
258
|
-
if (input.nonWitnessUtxo) {
|
|
259
|
-
const nonWitnessTxn = bitcoinjsLib.Transaction.fromBuffer(input.nonWitnessUtxo);
|
|
260
|
-
const txOut = nonWitnessTxn.outs[psbt.txInputs[index].index];
|
|
261
|
-
extractedAddress = bitcoinjsLib.address.fromOutputScript(txOut.script);
|
|
262
|
-
}
|
|
263
|
-
return extractedAddress;
|
|
264
|
-
} catch (e) {
|
|
265
|
-
return extractedAddress;
|
|
266
|
-
}
|
|
267
|
-
};
|
|
268
|
-
|
|
269
|
-
const validateSigningAddress = ({ index, inputAtIndex, psbt, signingAddress })=>{
|
|
270
|
-
// we need to extract the address from the input at the current signing index
|
|
271
|
-
// to be able to compare it to the address provided in the request for the current index
|
|
272
|
-
const extractedAddress = extractAddressFromInput({
|
|
273
|
-
index,
|
|
274
|
-
input: inputAtIndex,
|
|
275
|
-
psbt
|
|
276
|
-
});
|
|
277
|
-
if (!extractedAddress) {
|
|
278
|
-
throw new InvalidPsbtError(`Could not extract address from input at index ${index}`);
|
|
279
|
-
}
|
|
280
|
-
if (extractedAddress !== signingAddress) {
|
|
281
|
-
throw new InvalidPsbtError(`Signing address does not match with address extracted from input at index ${index}`);
|
|
282
|
-
}
|
|
283
|
-
};
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* This method will validate the psbt against the signature data provided in the request
|
|
287
|
-
* It checks 3 things:
|
|
288
|
-
* - That the signing index exists in the input
|
|
289
|
-
* - That the address provided in the request matches the address in the input,
|
|
290
|
-
* unless the disableAddressValidation flag is set
|
|
291
|
-
* - That the sigHashType of the input is a member of the allowedSigHashTypes array
|
|
292
|
-
*/ const validatePsbt = ({ allowedSigHashTypes, psbt, signatureData })=>{
|
|
293
|
-
if (!(signatureData == null ? void 0 : signatureData.length)) {
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
|
-
for (const input of signatureData){
|
|
297
|
-
const { address: signingAddress, signingIndexes, // request from ME in cases of multi-sig. Steven wanted this defaulted to true
|
|
298
|
-
disableAddressValidation = true } = input;
|
|
299
|
-
if (!(signingIndexes == null ? void 0 : signingIndexes.length)) {
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
if (!signingAddress) {
|
|
303
|
-
throw new InvalidPsbtError('Missing signing address');
|
|
304
|
-
}
|
|
305
|
-
for (const index of signingIndexes){
|
|
306
|
-
const inputAtIndex = psbt.data.inputs[index];
|
|
307
|
-
if (!inputAtIndex) {
|
|
308
|
-
throw new InvalidPsbtError(`Missing input for index ${index}`);
|
|
309
|
-
}
|
|
310
|
-
if (!disableAddressValidation) {
|
|
311
|
-
validateSigningAddress({
|
|
312
|
-
index,
|
|
313
|
-
inputAtIndex,
|
|
314
|
-
psbt,
|
|
315
|
-
signingAddress
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
|
-
validateSigHash({
|
|
319
|
-
allowedSigHashTypes,
|
|
320
|
-
input: inputAtIndex
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
};
|
|
325
|
-
|
|
326
667
|
/**
|
|
327
668
|
* This method will create the psbt sign options for the given request
|
|
328
669
|
* It will validate the psbt against the signature data provided in the request
|
|
@@ -456,7 +797,11 @@ const createUnisatWalletProvider = ({ dynamicClient, injectedProvider })=>{
|
|
|
456
797
|
networkId: network
|
|
457
798
|
});
|
|
458
799
|
});
|
|
459
|
-
}
|
|
800
|
+
},
|
|
801
|
+
supportedEvents: [
|
|
802
|
+
'accountsChanged',
|
|
803
|
+
'networkChanged'
|
|
804
|
+
]
|
|
460
805
|
});
|
|
461
806
|
const terminate = async ()=>{
|
|
462
807
|
cleanupEventEmitter();
|
|
@@ -487,64 +832,6 @@ const getUnisatInjectedProvider = ()=>{
|
|
|
487
832
|
return core.getInjectedProviderFromWindow(UNISAT_PROVIDER_LOCATOR);
|
|
488
833
|
};
|
|
489
834
|
|
|
490
|
-
const convertNetworkIdForPsbt = (networkId)=>{
|
|
491
|
-
if (networkId === '1') {
|
|
492
|
-
return bitcoinjsLib.networks.bitcoin;
|
|
493
|
-
}
|
|
494
|
-
if (networkId === '2') {
|
|
495
|
-
return bitcoinjsLib.networks.testnet;
|
|
496
|
-
}
|
|
497
|
-
return undefined;
|
|
498
|
-
};
|
|
499
|
-
|
|
500
|
-
const convertNetworkIdToSatsConnectNetworkType = (networkId)=>{
|
|
501
|
-
if (networkId === '2') {
|
|
502
|
-
return satsConnect.BitcoinNetworkType.Testnet;
|
|
503
|
-
}
|
|
504
|
-
// fallback to mainnet
|
|
505
|
-
return satsConnect.BitcoinNetworkType.Mainnet;
|
|
506
|
-
};
|
|
507
|
-
|
|
508
|
-
/**
|
|
509
|
-
* This method will create the psbt sign options for SatsConnect for the given request
|
|
510
|
-
* It will validate the psbt against the signature data provided in the request
|
|
511
|
-
* It will return the inputs to sign if the signature data is provided
|
|
512
|
-
*/ const getPsbtInputsToSignForSatsConnect = ({ psbt, request, isLegacySatsConnect })=>{
|
|
513
|
-
if (!request.signature) {
|
|
514
|
-
return [];
|
|
515
|
-
}
|
|
516
|
-
validatePsbt({
|
|
517
|
-
allowedSigHashTypes: request.allowedSighash,
|
|
518
|
-
psbt,
|
|
519
|
-
signatureData: request.signature
|
|
520
|
-
});
|
|
521
|
-
const inputsToSign = [];
|
|
522
|
-
for (const signature of request.signature){
|
|
523
|
-
var _signature_signingIndexes;
|
|
524
|
-
if ((_signature_signingIndexes = signature.signingIndexes) == null ? void 0 : _signature_signingIndexes.length) {
|
|
525
|
-
for (const index of signature.signingIndexes){
|
|
526
|
-
inputsToSign.push({
|
|
527
|
-
address: signature.address,
|
|
528
|
-
sigHash: isLegacySatsConnect ? getSigHashType({
|
|
529
|
-
input: psbt.data.inputs[index]
|
|
530
|
-
}) : request.allowedSighash[0],
|
|
531
|
-
signingIndexes: [
|
|
532
|
-
index
|
|
533
|
-
]
|
|
534
|
-
});
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
return inputsToSign;
|
|
539
|
-
};
|
|
540
|
-
|
|
541
|
-
const getSatsConnectSigningProtocol = (protocol)=>{
|
|
542
|
-
if (!protocol) {
|
|
543
|
-
return undefined;
|
|
544
|
-
}
|
|
545
|
-
return protocol === 'ecdsa' ? satsConnect.MessageSigningProtocols.ECDSA : satsConnect.MessageSigningProtocols.BIP322;
|
|
546
|
-
};
|
|
547
|
-
|
|
548
835
|
const signMultipleTransactionsWithSatsConnect = async ({ network, psbts, provider })=>{
|
|
549
836
|
const response = await new Promise((resolve, reject)=>{
|
|
550
837
|
void satsConnect.signMultipleTransactions({
|
|
@@ -598,16 +885,9 @@ const createXverseBitcoinWalletProvider = ({ dynamicClient, satsConnectProvider
|
|
|
598
885
|
throw response.error;
|
|
599
886
|
}
|
|
600
887
|
const { addresses: connectedAddresses } = response.result;
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
const addresses = filteredAddresses.map((account)=>({
|
|
605
|
-
address: account.address,
|
|
606
|
-
publicKey: account.publicKey,
|
|
607
|
-
type: account.purpose === 'payment' ? 'payment' : 'ordinals'
|
|
608
|
-
}));
|
|
609
|
-
// put ordinals addresses first, as they should be the main walletAccount address
|
|
610
|
-
addresses.sort((a)=>a.type === 'ordinals' ? -1 : 1);
|
|
888
|
+
const { addresses } = parseBitcoinConnectionResult({
|
|
889
|
+
connectedAddresses
|
|
890
|
+
});
|
|
611
891
|
return {
|
|
612
892
|
addresses
|
|
613
893
|
};
|
|
@@ -769,7 +1049,11 @@ const createXverseBitcoinWalletProvider = ({ dynamicClient, satsConnectProvider
|
|
|
769
1049
|
accountDisconnectedListener,
|
|
770
1050
|
removeAccountChangeListener
|
|
771
1051
|
};
|
|
772
|
-
}
|
|
1052
|
+
},
|
|
1053
|
+
supportedEvents: [
|
|
1054
|
+
'accountsChanged',
|
|
1055
|
+
'disconnected'
|
|
1056
|
+
]
|
|
773
1057
|
});
|
|
774
1058
|
const terminate = async ()=>{
|
|
775
1059
|
cleanupEventEmitter();
|
|
@@ -846,7 +1130,15 @@ const BITCOIN_INJECTED_WALLETS_EXTENSION_KEY = 'bitcoinInjectedWallets';
|
|
|
846
1130
|
});
|
|
847
1131
|
injectedWalletProviders.push(xverseWalletProvider);
|
|
848
1132
|
}
|
|
849
|
-
//
|
|
1133
|
+
// Create and register MagicEden wallet provider
|
|
1134
|
+
const magicEdenInjectedProvider = getMagicEdenInjectedProvider();
|
|
1135
|
+
if (magicEdenInjectedProvider) {
|
|
1136
|
+
const magicEdenWalletProvider = createMagicEdenBitcoinWalletProvider({
|
|
1137
|
+
dynamicClient: client,
|
|
1138
|
+
satsConnectProvider: magicEdenInjectedProvider
|
|
1139
|
+
});
|
|
1140
|
+
injectedWalletProviders.push(magicEdenWalletProvider);
|
|
1141
|
+
}
|
|
850
1142
|
// TODO: create and register OKX wallet provider
|
|
851
1143
|
// TODO: create and register Bitget wallet provider
|
|
852
1144
|
// TODO: create and register OneKey wallet provider
|