@dynamic-labs-wallet/btc 0.0.229 → 0.0.230
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/index.cjs.js +1135 -2
- package/index.esm.js +1117 -2
- package/package.json +8 -2
- package/src/client/client.d.ts +158 -0
- package/src/client/client.d.ts.map +1 -0
- package/src/index.d.ts +1 -1
- package/src/index.d.ts.map +1 -1
- package/src/types/index.d.ts +0 -21
- package/src/types/index.d.ts.map +1 -1
- package/src/utils/calculateBip322Hash/calculateBip322Hash.d.ts +16 -0
- package/src/utils/calculateBip322Hash/calculateBip322Hash.d.ts.map +1 -0
- package/src/utils/calculateBip322Hash/index.d.ts +2 -0
- package/src/utils/calculateBip322Hash/index.d.ts.map +1 -0
- package/src/utils/convertSignatureToDER/convertSignatureToDER.d.ts +9 -0
- package/src/utils/convertSignatureToDER/convertSignatureToDER.d.ts.map +1 -0
- package/src/utils/convertSignatureToDER/index.d.ts +2 -0
- package/src/utils/convertSignatureToDER/index.d.ts.map +1 -0
- package/src/utils/createLegacyAddress/createLegacyAddress.d.ts +10 -0
- package/src/utils/createLegacyAddress/createLegacyAddress.d.ts.map +1 -0
- package/src/utils/createLegacyAddress/index.d.ts +2 -0
- package/src/utils/createLegacyAddress/index.d.ts.map +1 -0
- package/src/utils/createNativeSegWitAddress/createNativeSegWitAddress.d.ts +10 -0
- package/src/utils/createNativeSegWitAddress/createNativeSegWitAddress.d.ts.map +1 -0
- package/src/utils/createNativeSegWitAddress/index.d.ts +2 -0
- package/src/utils/createNativeSegWitAddress/index.d.ts.map +1 -0
- package/src/utils/createSegWitAddress/createSegWitAddress.d.ts +10 -0
- package/src/utils/createSegWitAddress/createSegWitAddress.d.ts.map +1 -0
- package/src/utils/createSegWitAddress/index.d.ts +2 -0
- package/src/utils/createSegWitAddress/index.d.ts.map +1 -0
- package/src/utils/createTaprootAddress/createTaprootAddress.d.ts +10 -0
- package/src/utils/createTaprootAddress/createTaprootAddress.d.ts.map +1 -0
- package/src/utils/createTaprootAddress/index.d.ts +2 -0
- package/src/utils/createTaprootAddress/index.d.ts.map +1 -0
- package/src/utils/encodeBip322Signature/encodeBip322Signature.d.ts +13 -0
- package/src/utils/encodeBip322Signature/encodeBip322Signature.d.ts.map +1 -0
- package/src/utils/encodeBip322Signature/index.d.ts +2 -0
- package/src/utils/encodeBip322Signature/index.d.ts.map +1 -0
- package/src/utils/getAddressTypeFromDerivationPath/getAddressTypeFromDerivationPath.d.ts +9 -0
- package/src/utils/getAddressTypeFromDerivationPath/getAddressTypeFromDerivationPath.d.ts.map +1 -0
- package/src/utils/getAddressTypeFromDerivationPath/index.d.ts +2 -0
- package/src/utils/getAddressTypeFromDerivationPath/index.d.ts.map +1 -0
- package/src/utils/getBitcoinNetwork/getBitcoinNetwork.d.ts +10 -0
- package/src/utils/getBitcoinNetwork/getBitcoinNetwork.d.ts.map +1 -0
- package/src/utils/getBitcoinNetwork/index.d.ts +2 -0
- package/src/utils/getBitcoinNetwork/index.d.ts.map +1 -0
- package/src/utils/getDefaultRpcUrl/getDefaultRpcUrl.d.ts +9 -0
- package/src/utils/getDefaultRpcUrl/getDefaultRpcUrl.d.ts.map +1 -0
- package/src/utils/getDefaultRpcUrl/index.d.ts +2 -0
- package/src/utils/getDefaultRpcUrl/index.d.ts.map +1 -0
- package/src/utils/getFeeRates/getFeeRates.d.ts +18 -0
- package/src/utils/getFeeRates/getFeeRates.d.ts.map +1 -0
- package/src/utils/getFeeRates/index.d.ts +2 -0
- package/src/utils/getFeeRates/index.d.ts.map +1 -0
- package/src/utils/getUTXOs/getUTXOs.d.ts +15 -0
- package/src/utils/getUTXOs/getUTXOs.d.ts.map +1 -0
- package/src/utils/getUTXOs/index.d.ts +2 -0
- package/src/utils/getUTXOs/index.d.ts.map +1 -0
- package/src/utils/index.d.ts +12 -0
- package/src/utils/index.d.ts.map +1 -0
- package/src/utils/initEccLib/index.d.ts +2 -0
- package/src/utils/initEccLib/index.d.ts.map +1 -0
- package/src/utils/initEccLib/initEccLib.d.ts +5 -0
- package/src/utils/initEccLib/initEccLib.d.ts.map +1 -0
- package/src/utils/normalizeForCompressed/index.d.ts +2 -0
- package/src/utils/normalizeForCompressed/index.d.ts.map +1 -0
- package/src/utils/normalizeForCompressed/normalizeForCompressed.d.ts +9 -0
- package/src/utils/normalizeForCompressed/normalizeForCompressed.d.ts.map +1 -0
- package/src/utils/normalizeForTaproot/index.d.ts +2 -0
- package/src/utils/normalizeForTaproot/index.d.ts.map +1 -0
- package/src/utils/normalizeForTaproot/normalizeForTaproot.d.ts +9 -0
- package/src/utils/normalizeForTaproot/normalizeForTaproot.d.ts.map +1 -0
- package/src/utils/normalizePublicKey/index.d.ts +2 -0
- package/src/utils/normalizePublicKey/index.d.ts.map +1 -0
- package/src/utils/normalizePublicKey/normalizePublicKey.d.ts +14 -0
- package/src/utils/normalizePublicKey/normalizePublicKey.d.ts.map +1 -0
- package/src/utils/privateKeyToWIF/index.d.ts +2 -0
- package/src/utils/privateKeyToWIF/index.d.ts.map +1 -0
- package/src/utils/privateKeyToWIF/privateKeyToWIF.d.ts +24 -0
- package/src/utils/privateKeyToWIF/privateKeyToWIF.d.ts.map +1 -0
- package/src/utils/publicKeyToBitcoinAddress/index.d.ts +2 -0
- package/src/utils/publicKeyToBitcoinAddress/index.d.ts.map +1 -0
- package/src/utils/publicKeyToBitcoinAddress/publicKeyToBitcoinAddress.d.ts +12 -0
- package/src/utils/publicKeyToBitcoinAddress/publicKeyToBitcoinAddress.d.ts.map +1 -0
- package/src/utils/selectUTXOs/index.d.ts +2 -0
- package/src/utils/selectUTXOs/index.d.ts.map +1 -0
- package/src/utils/selectUTXOs/selectUTXOs.d.ts +19 -0
- package/src/utils/selectUTXOs/selectUTXOs.d.ts.map +1 -0
- package/src/utils/toBuffer/index.d.ts +2 -0
- package/src/utils/toBuffer/index.d.ts.map +1 -0
- package/src/utils/toBuffer/toBuffer.d.ts +8 -0
- package/src/utils/toBuffer/toBuffer.d.ts.map +1 -0
package/index.cjs.js
CHANGED
|
@@ -1,5 +1,1138 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
var browser = require('@dynamic-labs-wallet/browser');
|
|
4
|
+
var bitcoin = require('bitcoinjs-lib');
|
|
5
|
+
var ecc = require('@bitcoinerlab/secp256k1');
|
|
6
|
+
var bip322Js = require('bip322-js');
|
|
7
|
+
var bs58 = require('bs58');
|
|
8
|
+
var sha256 = require('@noble/hashes/sha256');
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
function _interopNamespaceDefault(e) {
|
|
11
|
+
var n = Object.create(null);
|
|
12
|
+
if (e) {
|
|
13
|
+
Object.keys(e).forEach(function (k) {
|
|
14
|
+
if (k !== 'default') {
|
|
15
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
16
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
17
|
+
enumerable: true,
|
|
18
|
+
get: function () { return e[k]; }
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
n.default = e;
|
|
24
|
+
return Object.freeze(n);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
var bitcoin__namespace = /*#__PURE__*/_interopNamespaceDefault(bitcoin);
|
|
28
|
+
|
|
29
|
+
function _extends() {
|
|
30
|
+
_extends = Object.assign || function assign(target) {
|
|
31
|
+
for(var i = 1; i < arguments.length; i++){
|
|
32
|
+
var source = arguments[i];
|
|
33
|
+
for(var key in source)if (Object.prototype.hasOwnProperty.call(source, key)) target[key] = source[key];
|
|
34
|
+
}
|
|
35
|
+
return target;
|
|
36
|
+
};
|
|
37
|
+
return _extends.apply(this, arguments);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Helper to handle Buffer/Uint8Array compatibility issues
|
|
42
|
+
*
|
|
43
|
+
* @param data - The data to convert to a Buffer
|
|
44
|
+
* @returns The Buffer representation of the data
|
|
45
|
+
*/ const toBuffer = (data)=>{
|
|
46
|
+
if (Buffer.isBuffer(data)) return data;
|
|
47
|
+
return Buffer.from(data);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Maps a BitcoinNetwork string to a bitcoinjs-lib Network object.
|
|
52
|
+
*
|
|
53
|
+
* @param network - The network identifier ('mainnet' or 'testnet').
|
|
54
|
+
* @returns The corresponding bitcoinjs-lib Network object. Defaults to bitcoin mainnet if the network is not recognized.
|
|
55
|
+
*/ function getBitcoinNetwork(network) {
|
|
56
|
+
switch(network){
|
|
57
|
+
case 'mainnet':
|
|
58
|
+
return bitcoin__namespace.networks.bitcoin;
|
|
59
|
+
case 'testnet':
|
|
60
|
+
return bitcoin__namespace.networks.testnet;
|
|
61
|
+
default:
|
|
62
|
+
return bitcoin__namespace.networks.bitcoin;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Creates a native SegWit P2WPKH address
|
|
68
|
+
*
|
|
69
|
+
* @param pubKey - The public key
|
|
70
|
+
* @param networkName - The network name
|
|
71
|
+
* @returns The native SegWit address
|
|
72
|
+
*/ const createNativeSegWitAddress = (pubKey, networkName)=>{
|
|
73
|
+
const network = getBitcoinNetwork(networkName);
|
|
74
|
+
// Convert to Uint8Array
|
|
75
|
+
let pubKeyArray = Uint8Array.from(pubKey);
|
|
76
|
+
// If it's an uncompressed key (65 bytes), compress it (33 bytes)
|
|
77
|
+
if (pubKeyArray.length === 65) {
|
|
78
|
+
// Uncompressed format: 0x04 || x (32 bytes) || y (32 bytes)
|
|
79
|
+
// Compressed format: 0x02 or 0x03 || x (32 bytes)
|
|
80
|
+
const y = pubKeyArray[64];
|
|
81
|
+
const compressedX = pubKeyArray.slice(1, 33); // Skip the 0x04 prefix, take x (32 bytes)
|
|
82
|
+
const compressed = new Uint8Array(33);
|
|
83
|
+
compressed[0] = y % 2 === 0 ? 0x02 : 0x03; // Even y -> 0x02, Odd y -> 0x03
|
|
84
|
+
compressed.set(compressedX, 1);
|
|
85
|
+
pubKeyArray = compressed;
|
|
86
|
+
}
|
|
87
|
+
const { address } = bitcoin.payments.p2wpkh({
|
|
88
|
+
pubkey: toBuffer(pubKeyArray),
|
|
89
|
+
network
|
|
90
|
+
});
|
|
91
|
+
return address || '';
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
let isInitialized = false;
|
|
95
|
+
/**
|
|
96
|
+
* Initializes the ECC library if not already initialized
|
|
97
|
+
*/ const initEccLib = ()=>{
|
|
98
|
+
if (isInitialized) return;
|
|
99
|
+
bitcoin.initEccLib(ecc);
|
|
100
|
+
isInitialized = true;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Creates a Taproot P2TR address
|
|
105
|
+
*
|
|
106
|
+
* @param pubKey - The public key
|
|
107
|
+
* @param networkName - The network name
|
|
108
|
+
* @returns The Taproot address
|
|
109
|
+
*/ const createTaprootAddress = (pubKey, networkName)=>{
|
|
110
|
+
initEccLib();
|
|
111
|
+
const network = getBitcoinNetwork(networkName);
|
|
112
|
+
// For Taproot, we need the 32-byte x-only public key
|
|
113
|
+
const xOnlyPubKey = pubKey.subarray(-32);
|
|
114
|
+
const xOnlyPubKeyArray = Uint8Array.from(xOnlyPubKey);
|
|
115
|
+
const { address } = bitcoin.payments.p2tr({
|
|
116
|
+
internalPubkey: toBuffer(xOnlyPubKeyArray),
|
|
117
|
+
network
|
|
118
|
+
});
|
|
119
|
+
if (!address) {
|
|
120
|
+
throw new Error('Failed to create Taproot address');
|
|
121
|
+
}
|
|
122
|
+
return address;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Converts a public key to a Bitcoin address
|
|
127
|
+
*
|
|
128
|
+
* @param publicKey - The public key
|
|
129
|
+
* @param addressType - The address type (default: TAPROOT)
|
|
130
|
+
* @param network - The Bitcoin network (default: 'mainnet')
|
|
131
|
+
* @returns The Bitcoin address
|
|
132
|
+
*/ const publicKeyToBitcoinAddress = (publicKey, addressType = browser.BitcoinAddressType.TAPROOT, network = 'mainnet')=>{
|
|
133
|
+
initEccLib();
|
|
134
|
+
// Convert to Uint8Array to Buffer if needed
|
|
135
|
+
const pubKeyBuffer = toBuffer(publicKey);
|
|
136
|
+
switch(addressType){
|
|
137
|
+
case browser.BitcoinAddressType.NATIVE_SEGWIT:
|
|
138
|
+
return createNativeSegWitAddress(pubKeyBuffer, network);
|
|
139
|
+
case browser.BitcoinAddressType.TAPROOT:
|
|
140
|
+
return createTaprootAddress(pubKeyBuffer, network);
|
|
141
|
+
default:
|
|
142
|
+
throw new Error(`Unsupported address type: ${addressType}`);
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Converts a public key input (various formats) to a Buffer.
|
|
148
|
+
* Handles objects with `pubkey` property, numeric key maps, `serializeUncompressed` methods, etc.
|
|
149
|
+
*
|
|
150
|
+
* @param pubkey - The raw public key input
|
|
151
|
+
* @returns The public key as a Buffer
|
|
152
|
+
*/ const convertPublicKeyToBuffer = (pubkey)=>{
|
|
153
|
+
const actualPubkey = pubkey;
|
|
154
|
+
if (Buffer.isBuffer(actualPubkey)) {
|
|
155
|
+
return actualPubkey;
|
|
156
|
+
}
|
|
157
|
+
if (actualPubkey instanceof Uint8Array) {
|
|
158
|
+
return Buffer.from(actualPubkey);
|
|
159
|
+
}
|
|
160
|
+
if (typeof actualPubkey === 'string') {
|
|
161
|
+
const result = Buffer.from(actualPubkey, 'hex');
|
|
162
|
+
if (result.length === 0) {
|
|
163
|
+
throw new Error('Invalid public key format');
|
|
164
|
+
}
|
|
165
|
+
return result;
|
|
166
|
+
}
|
|
167
|
+
if (typeof actualPubkey === 'object' && actualPubkey !== null) {
|
|
168
|
+
return convertObjectToBuffer(actualPubkey);
|
|
169
|
+
}
|
|
170
|
+
throw new Error('Invalid public key format');
|
|
171
|
+
};
|
|
172
|
+
const convertObjectToBuffer = (actualPubkey)=>{
|
|
173
|
+
if ('pubkey' in actualPubkey) {
|
|
174
|
+
return convertPublicKeyToBuffer(actualPubkey.pubkey);
|
|
175
|
+
}
|
|
176
|
+
if ('serializeUncompressed' in actualPubkey && typeof actualPubkey.serializeUncompressed === 'function') {
|
|
177
|
+
return Buffer.from(actualPubkey.serializeUncompressed());
|
|
178
|
+
}
|
|
179
|
+
const keys = Object.keys(actualPubkey).map(Number).filter((k)=>!Number.isNaN(k)).sort((a, b)=>a - b);
|
|
180
|
+
if (keys.length > 0) {
|
|
181
|
+
return Buffer.from(keys.map((k)=>actualPubkey[k]));
|
|
182
|
+
}
|
|
183
|
+
throw new Error('Invalid public key object');
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Normalizes a public key buffer for Taproot (P2TR).
|
|
188
|
+
* Ensures the result is a 32-byte x-only key.
|
|
189
|
+
*
|
|
190
|
+
* @param buffer - The public key buffer
|
|
191
|
+
* @returns The 32-byte x-only public key buffer
|
|
192
|
+
*/ const normalizeForTaproot = (buffer)=>{
|
|
193
|
+
if (buffer.length === 32) return buffer;
|
|
194
|
+
if (buffer.length === 33) return buffer.subarray(1, 33);
|
|
195
|
+
if (buffer.length === 65) return buffer.subarray(1, 33);
|
|
196
|
+
if (buffer.length === 64) return buffer.subarray(0, 32);
|
|
197
|
+
throw new Error(`Unexpected public key length for Taproot: ${buffer.length}`);
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Normalizes a public key buffer for Compressed formats (Legacy, SegWit, Native SegWit).
|
|
202
|
+
* Ensures the result is a 33-byte compressed key.
|
|
203
|
+
*
|
|
204
|
+
* @param buffer - The public key buffer
|
|
205
|
+
* @returns The 33-byte compressed public key buffer
|
|
206
|
+
*/ const normalizeForCompressed = (buffer)=>{
|
|
207
|
+
if (buffer.length === 33) {
|
|
208
|
+
return buffer;
|
|
209
|
+
}
|
|
210
|
+
if (buffer.length === 65 && buffer[0] === 0x04) {
|
|
211
|
+
// Uncompressed standard (04 + X + Y)
|
|
212
|
+
const x = buffer.subarray(1, 33);
|
|
213
|
+
const yLastByte = buffer[64];
|
|
214
|
+
return compressPublicKey(x, yLastByte);
|
|
215
|
+
}
|
|
216
|
+
if (buffer.length === 64) {
|
|
217
|
+
// Raw X + Y (missing 04 prefix)
|
|
218
|
+
const x = buffer.subarray(0, 32);
|
|
219
|
+
const yLastByte = buffer[63];
|
|
220
|
+
return compressPublicKey(x, yLastByte);
|
|
221
|
+
}
|
|
222
|
+
throw new Error(`Unexpected public key length: ${buffer.length}`);
|
|
223
|
+
};
|
|
224
|
+
const compressPublicKey = (x, yLastByte)=>{
|
|
225
|
+
const prefix = yLastByte % 2 === 0 ? 0x02 : 0x03;
|
|
226
|
+
return Buffer.concat([
|
|
227
|
+
Buffer.from([
|
|
228
|
+
prefix
|
|
229
|
+
]),
|
|
230
|
+
x
|
|
231
|
+
]);
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Normalizes a public key to a standard Buffer format
|
|
236
|
+
* Handles various input types (Uint8Array, Buffer, object with pubKeyAsHex, etc.)
|
|
237
|
+
* and formats (32-byte x-only, 33-byte compressed, 65-byte uncompressed)
|
|
238
|
+
* based on the target address type.
|
|
239
|
+
*
|
|
240
|
+
* @param pubkey - The raw public key input
|
|
241
|
+
* @param addressType - The target Bitcoin address type
|
|
242
|
+
* @returns The normalized public key as a Buffer
|
|
243
|
+
*/ const normalizePublicKey = (pubkey, addressType)=>{
|
|
244
|
+
const buffer = convertPublicKeyToBuffer(pubkey);
|
|
245
|
+
if (addressType === browser.BitcoinAddressType.TAPROOT) {
|
|
246
|
+
return normalizeForTaproot(buffer);
|
|
247
|
+
}
|
|
248
|
+
return normalizeForCompressed(buffer);
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Infers the Bitcoin address type from a BIP-44 derivation path
|
|
253
|
+
*
|
|
254
|
+
* @param derivationPathStr - The derivation path string (e.g. "m/44'/0'/0'/0/0" as JSON or string)
|
|
255
|
+
* @returns The inferred BitcoinAddressType
|
|
256
|
+
*/ const getAddressTypeFromDerivationPath = (derivationPathStr)=>{
|
|
257
|
+
const derivationPathObj = JSON.parse(derivationPathStr);
|
|
258
|
+
const path = Object.values(derivationPathObj).map(Number);
|
|
259
|
+
if (path.length < 2) throw new Error('Invalid derivation path length');
|
|
260
|
+
const purpose = path[0];
|
|
261
|
+
// Handle both raw and hardened just in case, or just raw if strictly non-hardened
|
|
262
|
+
// 44 = 0x2C (Legacy), 49 = 0x31 (SegWit P2SH), 84 = 0x54 (Native SegWit), 86 = 0x56 (Taproot)
|
|
263
|
+
const purposeIndex = purpose >= 0x80000000 ? purpose - 0x80000000 : purpose;
|
|
264
|
+
let addressType;
|
|
265
|
+
switch(purposeIndex){
|
|
266
|
+
case 84:
|
|
267
|
+
addressType = browser.BitcoinAddressType.NATIVE_SEGWIT;
|
|
268
|
+
break;
|
|
269
|
+
case 86:
|
|
270
|
+
addressType = browser.BitcoinAddressType.TAPROOT;
|
|
271
|
+
break;
|
|
272
|
+
default:
|
|
273
|
+
throw new Error(`Unknown derivation path purpose: ${purposeIndex}`);
|
|
274
|
+
}
|
|
275
|
+
return addressType;
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Calculates the hash to be signed for a BIP-322 message verification
|
|
280
|
+
*
|
|
281
|
+
* @param message - The message to sign
|
|
282
|
+
* @param pubKey - The public key of the signer
|
|
283
|
+
* @param addressType - The address type (e.g. TAPROOT, SEGWIT)
|
|
284
|
+
* @param network - The Bitcoin network
|
|
285
|
+
* @returns The formatted message hash and the PSBT to be signed
|
|
286
|
+
*/ const calculateBip322Hash = (message, pubKey, addressType, network)=>{
|
|
287
|
+
// Normalize key and derive address
|
|
288
|
+
const normalizedKey = normalizePublicKey(pubKey, addressType);
|
|
289
|
+
const address = publicKeyToBitcoinAddress(normalizedKey, addressType, network);
|
|
290
|
+
if (!address) {
|
|
291
|
+
throw new Error('Failed to generate address for BIP-322');
|
|
292
|
+
}
|
|
293
|
+
const scriptPubKey = bip322Js.Address.convertAdressToScriptPubkey(address);
|
|
294
|
+
const toSpendTx = bip322Js.BIP322.buildToSpendTx(message, scriptPubKey);
|
|
295
|
+
const toSignPsbt = bip322Js.BIP322.buildToSignTx(toSpendTx.getId(), scriptPubKey);
|
|
296
|
+
// Calculate Hash to Sign
|
|
297
|
+
const txToSign = toSignPsbt.__CACHE.__TX; // Access underlying TX like prototype
|
|
298
|
+
let hashToSign;
|
|
299
|
+
if (addressType === browser.BitcoinAddressType.TAPROOT) {
|
|
300
|
+
// Taproot (BIP-341) Signing
|
|
301
|
+
const prevOutScripts = [
|
|
302
|
+
scriptPubKey
|
|
303
|
+
];
|
|
304
|
+
const values = [
|
|
305
|
+
0
|
|
306
|
+
]; // BIP-322 value
|
|
307
|
+
// Cast to any to avoid type issues if definition is missing
|
|
308
|
+
hashToSign = Buffer.from(txToSign.hashForWitnessV1(0, prevOutScripts, values, bitcoin__namespace.Transaction.SIGHASH_DEFAULT // Default sighash for Taproot
|
|
309
|
+
));
|
|
310
|
+
} else {
|
|
311
|
+
// SegWit (BIP-143) Signing (P2WPKH)
|
|
312
|
+
const txToSignSegwit = new bitcoin__namespace.Transaction();
|
|
313
|
+
txToSignSegwit.version = 0;
|
|
314
|
+
txToSignSegwit.locktime = 0;
|
|
315
|
+
const prevTxId = toSpendTx.getId();
|
|
316
|
+
const prevHash = Buffer.from(prevTxId, 'hex').reverse();
|
|
317
|
+
txToSignSegwit.addInput(prevHash, 0, 0);
|
|
318
|
+
txToSignSegwit.addOutput(Buffer.from([
|
|
319
|
+
0x6a
|
|
320
|
+
]), BigInt(0));
|
|
321
|
+
const p2pkh = bitcoin__namespace.payments.p2pkh({
|
|
322
|
+
pubkey: normalizedKey,
|
|
323
|
+
network: network === 'testnet' ? bitcoin__namespace.networks.testnet : bitcoin__namespace.networks.bitcoin
|
|
324
|
+
});
|
|
325
|
+
const scriptCode = p2pkh.output;
|
|
326
|
+
if (!scriptCode) throw new Error('Failed to generate scriptCode');
|
|
327
|
+
hashToSign = txToSignSegwit.hashForWitnessV0(0, scriptCode, BigInt(0), bitcoin__namespace.Transaction.SIGHASH_ALL);
|
|
328
|
+
}
|
|
329
|
+
return {
|
|
330
|
+
formattedMessage: new Uint8Array(hashToSign),
|
|
331
|
+
toSignPsbt
|
|
332
|
+
};
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Encodes the signature into the BIP-322 witness format
|
|
337
|
+
*
|
|
338
|
+
* @param toSignPsbt - The PSBT that was signed
|
|
339
|
+
* @param pubKey - The public key of the signer
|
|
340
|
+
* @param signature - The signature produced by the signer
|
|
341
|
+
* @param addressType - The address type
|
|
342
|
+
* @returns The base64 encoded BIP-322 signature
|
|
343
|
+
*/ const encodeBip322Signature = (toSignPsbt, pubKey, signature, addressType)=>{
|
|
344
|
+
const normalizedKey = normalizePublicKey(pubKey, addressType);
|
|
345
|
+
if (addressType === browser.BitcoinAddressType.TAPROOT) {
|
|
346
|
+
// Prototype logic: direct update
|
|
347
|
+
// Signature from MPC should be 64 bytes (r|s)
|
|
348
|
+
let sigBuffer;
|
|
349
|
+
if (signature instanceof Uint8Array || Buffer.isBuffer(signature)) {
|
|
350
|
+
sigBuffer = Buffer.from(signature);
|
|
351
|
+
} else {
|
|
352
|
+
// ECDSA signature object? Should be raw bytes for Schnorr/BIP340
|
|
353
|
+
// If it's EcdsaSignature (r,s), concat them.
|
|
354
|
+
const r = signature.r;
|
|
355
|
+
const s = signature.s;
|
|
356
|
+
const rBuf = Buffer.isBuffer(r) ? r : Buffer.from(r);
|
|
357
|
+
const sBuf = Buffer.isBuffer(s) ? s : Buffer.from(s);
|
|
358
|
+
sigBuffer = Buffer.concat([
|
|
359
|
+
rBuf,
|
|
360
|
+
sBuf
|
|
361
|
+
]);
|
|
362
|
+
}
|
|
363
|
+
toSignPsbt.updateInput(0, {
|
|
364
|
+
tapKeySig: sigBuffer
|
|
365
|
+
});
|
|
366
|
+
} else {
|
|
367
|
+
// Serialize the signature (COMPACT SIGNATURE)
|
|
368
|
+
const r = signature.r;
|
|
369
|
+
const s = signature.s;
|
|
370
|
+
const compactSignature = Buffer.concat([
|
|
371
|
+
Buffer.from(r),
|
|
372
|
+
Buffer.from(s)
|
|
373
|
+
]);
|
|
374
|
+
// Apply Signature
|
|
375
|
+
const mpcSignerForPsbt = {
|
|
376
|
+
publicKey: normalizedKey,
|
|
377
|
+
sign: (_hash)=>{
|
|
378
|
+
return compactSignature;
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
// Sign the input
|
|
382
|
+
toSignPsbt.signInput(0, mpcSignerForPsbt);
|
|
383
|
+
}
|
|
384
|
+
// Finalize the inputs
|
|
385
|
+
toSignPsbt.finalizeAllInputs();
|
|
386
|
+
// Encode Final Result
|
|
387
|
+
return bip322Js.BIP322.encodeWitness(toSignPsbt);
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
initEccLib();
|
|
391
|
+
/**
|
|
392
|
+
* Converts a private key (hex string) to Bitcoin WIF (Wallet Import Format)
|
|
393
|
+
* WIF is the standard format that Bitcoin wallets expect for importing private keys
|
|
394
|
+
*
|
|
395
|
+
* WIF encoding steps:
|
|
396
|
+
* 1. Add network prefix (0x80 for mainnet, 0xef for testnet/regtest)
|
|
397
|
+
* 2. Add compression flag (0x01) for compressed public keys (used for SegWit)
|
|
398
|
+
* 3. Calculate checksum (double SHA-256, take first 4 bytes)
|
|
399
|
+
* 4. Base58 encode the result
|
|
400
|
+
*
|
|
401
|
+
* @param privateKey - Private key as hex string (with or without 0x prefix)
|
|
402
|
+
* @param network - Bitcoin network (mainnet, testnet)
|
|
403
|
+
* @param options - Optional configuration
|
|
404
|
+
* @param options.compressed - Whether to use compressed format (default: true for SegWit compatibility)
|
|
405
|
+
* @param options.onWarning - Optional callback for warnings (e.g., unexpected key length)
|
|
406
|
+
* @returns WIF-encoded private key string
|
|
407
|
+
* @throws Error if conversion fails
|
|
408
|
+
*/ const privateKeyToWIF = (privateKey, network = 'mainnet', options)=>{
|
|
409
|
+
const { compressed = true, onWarning } = options || {};
|
|
410
|
+
try {
|
|
411
|
+
// Remove 0x prefix if present
|
|
412
|
+
const cleanPrivateKey = privateKey.startsWith('0x') ? privateKey.slice(2) : privateKey;
|
|
413
|
+
// Validate hex string length (should be 64 characters = 32 bytes)
|
|
414
|
+
if (cleanPrivateKey.length !== 64) {
|
|
415
|
+
onWarning == null ? void 0 : onWarning('Unexpected private key length', {
|
|
416
|
+
length: cleanPrivateKey.length,
|
|
417
|
+
expected: 64
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
// Convert hex to Uint8Array
|
|
421
|
+
const privateKeyBytes = new Uint8Array(cleanPrivateKey.match(/.{1,2}/g).map((byte)=>Number.parseInt(byte, 16)));
|
|
422
|
+
// Step 1: Get network prefix from bitcoinjs-lib network
|
|
423
|
+
const networkConfig = getBitcoinNetwork(network);
|
|
424
|
+
const networkPrefix = networkConfig.wif;
|
|
425
|
+
// Step 2: Build extended key with network prefix and optional compression flag
|
|
426
|
+
// For Native SegWit and SegWit, we use compressed format (0x01 flag)
|
|
427
|
+
const extendedKeyLength = compressed ? 34 : 33; // 1 byte prefix + 32 bytes key + (optional) 1 byte compression
|
|
428
|
+
const extendedKey = new Uint8Array(extendedKeyLength);
|
|
429
|
+
extendedKey[0] = networkPrefix;
|
|
430
|
+
extendedKey.set(privateKeyBytes, 1);
|
|
431
|
+
if (compressed) {
|
|
432
|
+
extendedKey[33] = 0x01; // Compression flag for SegWit addresses
|
|
433
|
+
}
|
|
434
|
+
// Step 3: Calculate checksum (double SHA-256, take first 4 bytes)
|
|
435
|
+
const firstHash = sha256.sha256(extendedKey);
|
|
436
|
+
const secondHash = sha256.sha256(firstHash);
|
|
437
|
+
const checksum = secondHash.slice(0, 4);
|
|
438
|
+
// Step 4: Append checksum and Base58 encode
|
|
439
|
+
const wifBytes = new Uint8Array(extendedKey.length + checksum.length);
|
|
440
|
+
wifBytes.set(extendedKey, 0);
|
|
441
|
+
wifBytes.set(checksum, extendedKey.length);
|
|
442
|
+
const wif = bs58.encode(wifBytes);
|
|
443
|
+
return wif;
|
|
444
|
+
} catch (error) {
|
|
445
|
+
// Re-throw with more context
|
|
446
|
+
throw new Error(`Failed to convert private key to WIF: ${error instanceof Error ? error.message : String(error)}`);
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Converts an ECDSA signature to DER format
|
|
452
|
+
*
|
|
453
|
+
* @param signature - The ECDSA signature
|
|
454
|
+
* @returns The DER encoded signature
|
|
455
|
+
*/ const convertSignatureToDER = (signature)=>{
|
|
456
|
+
// Construct raw 64-byte signature (r + s) using Uint8Array to ensure compatibility
|
|
457
|
+
const r = signature.r instanceof Uint8Array ? signature.r : new Uint8Array(signature.r);
|
|
458
|
+
const s = signature.s instanceof Uint8Array ? signature.s : new Uint8Array(signature.s);
|
|
459
|
+
// Enforce 32-byte length (padding or slicing if needed)
|
|
460
|
+
const r32 = new Uint8Array(32);
|
|
461
|
+
const rSource = r.length > 32 ? r.slice(-32) : r;
|
|
462
|
+
r32.set(rSource, 32 - rSource.length);
|
|
463
|
+
const s32 = new Uint8Array(32);
|
|
464
|
+
const sSource = s.length > 32 ? s.slice(-32) : s;
|
|
465
|
+
s32.set(sSource, 32 - sSource.length);
|
|
466
|
+
const rawSignature = new Uint8Array(64);
|
|
467
|
+
rawSignature.set(r32, 0);
|
|
468
|
+
rawSignature.set(s32, 32);
|
|
469
|
+
const derSignature = bitcoin__namespace.script.signature.encode(rawSignature, bitcoin__namespace.Transaction.SIGHASH_ALL);
|
|
470
|
+
return derSignature;
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Helper to get the default RPC URL
|
|
475
|
+
*
|
|
476
|
+
* @param network - The Bitcoin network
|
|
477
|
+
* @returns The default RPC URL
|
|
478
|
+
*/ const getDefaultRpcUrl = (network)=>{
|
|
479
|
+
return network === 'mainnet' ? 'https://mempool.space/api' : 'https://mempool.space/testnet/api';
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Fetches UTXOs for a given address using mempool.space API
|
|
484
|
+
*
|
|
485
|
+
* @param address - The address to fetch UTXOs for
|
|
486
|
+
* @param network - The Bitcoin network (default: 'mainnet')
|
|
487
|
+
* @param rpcUrl - Optional custom RPC URL
|
|
488
|
+
* @returns The list of UTXOs
|
|
489
|
+
*/ const getUTXOs = async ({ address, network = 'mainnet', rpcUrl })=>{
|
|
490
|
+
const baseUrl = rpcUrl || getDefaultRpcUrl(network);
|
|
491
|
+
try {
|
|
492
|
+
const response = await fetch(`${baseUrl}/address/${address}/utxo`);
|
|
493
|
+
if (!response.ok) {
|
|
494
|
+
throw new Error(`Failed to fetch UTXOs: ${response.statusText}`);
|
|
495
|
+
}
|
|
496
|
+
const utxos = await response.json();
|
|
497
|
+
return utxos.map((utxo)=>({
|
|
498
|
+
txid: utxo.txid,
|
|
499
|
+
vout: utxo.vout,
|
|
500
|
+
value: BigInt(utxo.value)
|
|
501
|
+
}));
|
|
502
|
+
} catch (error) {
|
|
503
|
+
throw new Error(`Error fetching UTXOs: ${error}`);
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Simple greedy coin selection
|
|
509
|
+
*
|
|
510
|
+
* @param utxos - The list of UTXOs to select from
|
|
511
|
+
* @param amount - The amount to spend
|
|
512
|
+
* @param feeRate - The fee rate in sat/vByte
|
|
513
|
+
* @returns The selected inputs, change amount, and fee
|
|
514
|
+
*/ const selectUTXOs = ({ utxos, amount, feeRate })=>{
|
|
515
|
+
// Sort by value descending
|
|
516
|
+
const sorted = [
|
|
517
|
+
...utxos
|
|
518
|
+
].sort((a, b)=>Number(b.value - a.value));
|
|
519
|
+
const inputs = [];
|
|
520
|
+
let totalInput = 0n;
|
|
521
|
+
// Basic fee estimation constants
|
|
522
|
+
// Optimized for Native SegWit (P2WPKH) only
|
|
523
|
+
// P2WPKH: ~68 vbytes per input, ~31 vbytes per output
|
|
524
|
+
// Overhead: ~10 vbytes
|
|
525
|
+
const INPUT_SIZE = 68;
|
|
526
|
+
const OUTPUT_SIZE = 31;
|
|
527
|
+
const OVERHEAD = 10;
|
|
528
|
+
for (const utxo of sorted){
|
|
529
|
+
inputs.push(utxo);
|
|
530
|
+
totalInput += utxo.value;
|
|
531
|
+
// Calculate fee for current set + 2 outputs (recipient + change)
|
|
532
|
+
const vbytes = OVERHEAD + inputs.length * INPUT_SIZE + 2 * OUTPUT_SIZE;
|
|
533
|
+
const fee = BigInt(Math.ceil(vbytes * feeRate));
|
|
534
|
+
if (totalInput >= amount + fee) {
|
|
535
|
+
return {
|
|
536
|
+
inputs,
|
|
537
|
+
change: totalInput - amount - fee,
|
|
538
|
+
fee
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
throw new Error('Insufficient funds');
|
|
543
|
+
};
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Fetches current fee rates from mempool.space
|
|
547
|
+
* Returns fees in sat/vByte
|
|
548
|
+
*
|
|
549
|
+
* @param network - The Bitcoin network (default: 'mainnet')
|
|
550
|
+
* @param rpcUrl - Optional custom RPC URL
|
|
551
|
+
* @returns The fee rates for fast, medium, and slow transactions
|
|
552
|
+
*/ const getFeeRates = async ({ network = 'mainnet', rpcUrl })=>{
|
|
553
|
+
const baseUrl = rpcUrl || getDefaultRpcUrl(network);
|
|
554
|
+
const response = await fetch(`${baseUrl}/v1/fees/recommended`);
|
|
555
|
+
if (!response.ok) {
|
|
556
|
+
throw new Error(`Failed to fetch fee rates: ${response.statusText}`);
|
|
557
|
+
}
|
|
558
|
+
const fees = await response.json();
|
|
559
|
+
return {
|
|
560
|
+
fast: fees.fastestFee,
|
|
561
|
+
medium: fees.halfHourFee,
|
|
562
|
+
slow: fees.hourFee
|
|
563
|
+
};
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
class DynamicBtcWalletClient extends browser.DynamicWalletClient {
|
|
567
|
+
/**
|
|
568
|
+
* Creates a Bitcoin wallet account
|
|
569
|
+
* @param thresholdSignatureScheme - The threshold signature scheme to use for the wallet
|
|
570
|
+
* @param password - The password to use for the wallet
|
|
571
|
+
* @param onError - The function to call if an error occurs
|
|
572
|
+
* @param signedSessionId - The signed session ID to use for the wallet
|
|
573
|
+
* @param addressType - The type of address to use for the wallet
|
|
574
|
+
* @returns The account address, public key hex, and raw public key
|
|
575
|
+
*/ async createWalletAccount({ thresholdSignatureScheme, password = undefined, onError, signedSessionId, bitcoinConfig }) {
|
|
576
|
+
const network = browser.BitcoinNetwork.MAINNET;
|
|
577
|
+
const { addressType } = bitcoinConfig;
|
|
578
|
+
if (!addressType) {
|
|
579
|
+
throw new Error('addressType is required for BTC');
|
|
580
|
+
}
|
|
581
|
+
this.logger.debug('[BTC Client] Creating wallet account', {
|
|
582
|
+
package: 'packages/btc/src/client/client.ts',
|
|
583
|
+
method: 'createWalletAccount',
|
|
584
|
+
addressType,
|
|
585
|
+
network,
|
|
586
|
+
thresholdSignatureScheme,
|
|
587
|
+
signedSessionId
|
|
588
|
+
});
|
|
589
|
+
try {
|
|
590
|
+
// Create a promise that will resolve when the ceremony is complete
|
|
591
|
+
let ceremonyCompleteResolver;
|
|
592
|
+
const ceremonyCompletePromise = new Promise((resolve)=>{
|
|
593
|
+
ceremonyCompleteResolver = resolve;
|
|
594
|
+
});
|
|
595
|
+
// Validate address type is provided
|
|
596
|
+
if (!addressType) {
|
|
597
|
+
throw new Error('Address type is required for BTC');
|
|
598
|
+
}
|
|
599
|
+
const buildBitcoinConfig = {
|
|
600
|
+
addressType,
|
|
601
|
+
network
|
|
602
|
+
};
|
|
603
|
+
// Use 'BTC' as chain name, derivation path and signing algorithm will be resolved from addressType
|
|
604
|
+
const { rawPublicKey, clientKeyShares } = await this.keyGen({
|
|
605
|
+
chainName: this.chainName,
|
|
606
|
+
thresholdSignatureScheme,
|
|
607
|
+
bitcoinConfig: buildBitcoinConfig,
|
|
608
|
+
onError: (error)=>{
|
|
609
|
+
this.logger.error(browser.ERROR_CREATE_WALLET_ACCOUNT, error);
|
|
610
|
+
onError == null ? void 0 : onError(error);
|
|
611
|
+
},
|
|
612
|
+
onCeremonyComplete: (accountAddress, walletId)=>{
|
|
613
|
+
const chainConfig = browser.getMPCChainConfig(this.chainName, bitcoinConfig);
|
|
614
|
+
// Update wallet map with the derived account address
|
|
615
|
+
this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress], {
|
|
616
|
+
accountAddress: accountAddress,
|
|
617
|
+
walletId,
|
|
618
|
+
chainName: this.chainName,
|
|
619
|
+
thresholdSignatureScheme,
|
|
620
|
+
derivationPath: JSON.stringify(Object.fromEntries(chainConfig.derivationPath.map((value, index)=>[
|
|
621
|
+
index,
|
|
622
|
+
value
|
|
623
|
+
]))),
|
|
624
|
+
addressType: addressType,
|
|
625
|
+
clientKeySharesBackupInfo: browser.getClientKeyShareBackupInfo()
|
|
626
|
+
});
|
|
627
|
+
this.logger.debug('[BTC Client] Updated walletMap in onCeremonyComplete', {
|
|
628
|
+
accountAddress: accountAddress,
|
|
629
|
+
walletId,
|
|
630
|
+
addressType: addressType
|
|
631
|
+
});
|
|
632
|
+
// Resolve the promise when ceremony is complete
|
|
633
|
+
ceremonyCompleteResolver(undefined);
|
|
634
|
+
}
|
|
635
|
+
});
|
|
636
|
+
// Wait for the ceremony to complete before proceeding
|
|
637
|
+
await ceremonyCompletePromise;
|
|
638
|
+
if (!rawPublicKey || !clientKeyShares) {
|
|
639
|
+
throw new Error('Key generation failed');
|
|
640
|
+
}
|
|
641
|
+
// Derive account address from raw public key
|
|
642
|
+
const { accountAddress } = this.deriveAccountAddress({
|
|
643
|
+
rawPublicKey,
|
|
644
|
+
addressType: addressType,
|
|
645
|
+
network
|
|
646
|
+
});
|
|
647
|
+
// Store client key shares to localStorage
|
|
648
|
+
await this.setClientKeySharesToLocalStorage({
|
|
649
|
+
accountAddress,
|
|
650
|
+
clientKeyShares,
|
|
651
|
+
overwriteOrMerge: 'overwrite'
|
|
652
|
+
});
|
|
653
|
+
// Store encrypted backup
|
|
654
|
+
await this.storeEncryptedBackupByWalletWithRetry({
|
|
655
|
+
accountAddress,
|
|
656
|
+
clientKeyShares,
|
|
657
|
+
password,
|
|
658
|
+
signedSessionId
|
|
659
|
+
});
|
|
660
|
+
const publicKeyHex = this.extractPublicKeyHex(rawPublicKey);
|
|
661
|
+
return {
|
|
662
|
+
accountAddress,
|
|
663
|
+
publicKeyHex,
|
|
664
|
+
rawPublicKey
|
|
665
|
+
};
|
|
666
|
+
} catch (error) {
|
|
667
|
+
if (onError) {
|
|
668
|
+
onError(error);
|
|
669
|
+
}
|
|
670
|
+
throw new Error(browser.ERROR_CREATE_WALLET_ACCOUNT);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Automatically determines the chain config based on address type
|
|
675
|
+
* @param rawPublicKey - The raw public key from the server
|
|
676
|
+
* @returns The public key hex
|
|
677
|
+
*/ extractPublicKeyHex(rawPublicKey) {
|
|
678
|
+
if (rawPublicKey instanceof Uint8Array) {
|
|
679
|
+
return Buffer.from(rawPublicKey).toString('hex');
|
|
680
|
+
}
|
|
681
|
+
if (rawPublicKey && typeof rawPublicKey === 'object' && typeof rawPublicKey.pubKeyAsHex === 'function') {
|
|
682
|
+
return rawPublicKey.pubKeyAsHex();
|
|
683
|
+
}
|
|
684
|
+
if (typeof rawPublicKey === 'string') {
|
|
685
|
+
return rawPublicKey;
|
|
686
|
+
}
|
|
687
|
+
throw new Error('Invalid public key format');
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Derives the Bitcoin account address
|
|
691
|
+
* - BIP340 keys (32 bytes x-only): Only for Taproot addresses
|
|
692
|
+
* - ECDSA keys (33/65 bytes): For all other address types (Legacy, SegWit, Native SegWit)
|
|
693
|
+
* - Algorithm selection is automatic based on addressType
|
|
694
|
+
* @param rawPublicKey - The raw public key to derive the account address from
|
|
695
|
+
* @param addressType - The address type to derive the account address for
|
|
696
|
+
* @param network - The network to derive the account address for
|
|
697
|
+
* @returns The account address
|
|
698
|
+
*/ deriveAccountAddress({ rawPublicKey, addressType, network }) {
|
|
699
|
+
// Derive address based on the chosen address type and network
|
|
700
|
+
const normalizedKey = normalizePublicKey(rawPublicKey, addressType);
|
|
701
|
+
const accountAddress = publicKeyToBitcoinAddress(normalizedKey, addressType, network);
|
|
702
|
+
return {
|
|
703
|
+
accountAddress
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Signs a message (BIP-322)
|
|
708
|
+
* @param message - The message to sign
|
|
709
|
+
* @param accountAddress - The account address
|
|
710
|
+
* @param network - The network (mainnet/testnet)
|
|
711
|
+
* @param password - The wallet password (optional)
|
|
712
|
+
* @param signedSessionId - The signed session ID
|
|
713
|
+
* @param mfaToken - The MFA token (optional)
|
|
714
|
+
* @param context - Additional context
|
|
715
|
+
* @param onError - Error callback
|
|
716
|
+
* @returns The BIP-322 signature
|
|
717
|
+
*/ async signMessage({ message, accountAddress, network, password = undefined, signedSessionId, mfaToken, context, onError }) {
|
|
718
|
+
await this.verifyPassword({
|
|
719
|
+
accountAddress,
|
|
720
|
+
password,
|
|
721
|
+
walletOperation: browser.WalletOperation.SIGN_MESSAGE,
|
|
722
|
+
signedSessionId
|
|
723
|
+
});
|
|
724
|
+
try {
|
|
725
|
+
if (!accountAddress) {
|
|
726
|
+
throw new Error(browser.ERROR_ACCOUNT_ADDRESS_REQUIRED);
|
|
727
|
+
}
|
|
728
|
+
const walletProperties = this.walletMap[accountAddress];
|
|
729
|
+
if (!walletProperties) {
|
|
730
|
+
throw new Error('Wallet not found in walletMap');
|
|
731
|
+
}
|
|
732
|
+
const derivationPath = walletProperties.derivationPath;
|
|
733
|
+
if (!derivationPath) {
|
|
734
|
+
throw new Error('Derivation path missing in walletMap');
|
|
735
|
+
}
|
|
736
|
+
let addressType = walletProperties.addressType;
|
|
737
|
+
if (!addressType) {
|
|
738
|
+
addressType = getAddressTypeFromDerivationPath(derivationPath);
|
|
739
|
+
}
|
|
740
|
+
const clientKeyShares = await this.getClientKeySharesFromLocalStorage({
|
|
741
|
+
accountAddress
|
|
742
|
+
});
|
|
743
|
+
if (!clientKeyShares || clientKeyShares.length === 0) {
|
|
744
|
+
throw new Error('No key shares found');
|
|
745
|
+
}
|
|
746
|
+
const bitcoinConfig = {
|
|
747
|
+
addressType,
|
|
748
|
+
network
|
|
749
|
+
};
|
|
750
|
+
const derivedPublicKey = await this.derivePublicKey({
|
|
751
|
+
chainName: this.chainName,
|
|
752
|
+
keyShare: clientKeyShares[0],
|
|
753
|
+
derivationPath: new Uint32Array(Object.values(JSON.parse(derivationPath))),
|
|
754
|
+
bitcoinConfig
|
|
755
|
+
});
|
|
756
|
+
if (!derivedPublicKey) {
|
|
757
|
+
throw new Error('Failed to derive public key');
|
|
758
|
+
}
|
|
759
|
+
const pubKey = normalizePublicKey(derivedPublicKey, addressType);
|
|
760
|
+
this.verifyWalletAddress(derivedPublicKey, addressType, network, accountAddress);
|
|
761
|
+
// Prepare BIP-322 Transactions and calculate hash
|
|
762
|
+
const { formattedMessage, toSignPsbt } = calculateBip322Hash(message, pubKey, addressType, network);
|
|
763
|
+
// Prepare tweak for Taproot in case of BIP340
|
|
764
|
+
let tweak;
|
|
765
|
+
if (addressType === browser.BitcoinAddressType.TAPROOT) {
|
|
766
|
+
const tweakHash = bitcoin__namespace.crypto.taggedHash('TapTweak', pubKey);
|
|
767
|
+
tweak = Buffer.from(tweakHash).toString('hex');
|
|
768
|
+
}
|
|
769
|
+
// Build complete bitcoinConfig with addressType and tweak
|
|
770
|
+
const completeBitcoinConfig = _extends({}, bitcoinConfig, {
|
|
771
|
+
addressType: addressType,
|
|
772
|
+
tweak
|
|
773
|
+
});
|
|
774
|
+
// Sign the message using MPC
|
|
775
|
+
const signature = await this.sign({
|
|
776
|
+
message: formattedMessage,
|
|
777
|
+
accountAddress,
|
|
778
|
+
chainName: this.chainName,
|
|
779
|
+
password,
|
|
780
|
+
signedSessionId,
|
|
781
|
+
mfaToken,
|
|
782
|
+
isFormatted: true,
|
|
783
|
+
context: context != null ? context : {
|
|
784
|
+
btcMessage: message
|
|
785
|
+
},
|
|
786
|
+
bitcoinConfig: completeBitcoinConfig,
|
|
787
|
+
onError
|
|
788
|
+
});
|
|
789
|
+
// Encode Final Result
|
|
790
|
+
const bip322Signature = encodeBip322Signature(toSignPsbt, pubKey, signature, addressType);
|
|
791
|
+
this.logger.debug('[BTC Client] signMessage - bip322Signature', {
|
|
792
|
+
bip322Signature
|
|
793
|
+
});
|
|
794
|
+
return bip322Signature;
|
|
795
|
+
} catch (error) {
|
|
796
|
+
if (onError) {
|
|
797
|
+
onError(error);
|
|
798
|
+
}
|
|
799
|
+
throw new Error(browser.ERROR_SIGN_MESSAGE);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
/**
|
|
803
|
+
* Verifies that the derived address matches the expected address
|
|
804
|
+
* @param rawPublicKey - The raw public key
|
|
805
|
+
* @param addressType - The address type
|
|
806
|
+
* @param network - The network
|
|
807
|
+
* @param expectedAddress - The expected address
|
|
808
|
+
*/ verifyWalletAddress(rawPublicKey, addressType, network, expectedAddress) {
|
|
809
|
+
const normalizedKey = normalizePublicKey(rawPublicKey, addressType);
|
|
810
|
+
const derivedAddress = publicKeyToBitcoinAddress(normalizedKey, addressType, network);
|
|
811
|
+
if (derivedAddress !== expectedAddress) {
|
|
812
|
+
throw new Error(`Address verification failed: expected ${expectedAddress}, got ${derivedAddress}`);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* Exports the private key for a given account
|
|
817
|
+
* @param accountAddress - The account address to export the private key for
|
|
818
|
+
* @param password - The password to use for the private key
|
|
819
|
+
* @param signedSessionId - The signed session ID to use for the private key
|
|
820
|
+
* @param mfaToken - The MFA token to use for the private key
|
|
821
|
+
* @param network - The network to use for the private key
|
|
822
|
+
* @returns The private key
|
|
823
|
+
*/ async exportPrivateKey({ accountAddress, password = undefined, signedSessionId, mfaToken }) {
|
|
824
|
+
await this.verifyPassword({
|
|
825
|
+
accountAddress,
|
|
826
|
+
password,
|
|
827
|
+
walletOperation: browser.WalletOperation.EXPORT_PRIVATE_KEY,
|
|
828
|
+
signedSessionId
|
|
829
|
+
});
|
|
830
|
+
// Get wallet properties to determine addressType
|
|
831
|
+
const walletProperties = this.walletMap[accountAddress];
|
|
832
|
+
if (!walletProperties) {
|
|
833
|
+
throw new Error('Wallet not found in walletMap');
|
|
834
|
+
}
|
|
835
|
+
const derivationPath = walletProperties.derivationPath;
|
|
836
|
+
if (!derivationPath) {
|
|
837
|
+
throw new Error('Derivation path missing in walletMap');
|
|
838
|
+
}
|
|
839
|
+
let addressType = walletProperties.addressType;
|
|
840
|
+
if (!addressType && walletProperties.derivationPath) {
|
|
841
|
+
try {
|
|
842
|
+
addressType = getAddressTypeFromDerivationPath(walletProperties.derivationPath);
|
|
843
|
+
} catch (e) {
|
|
844
|
+
this.logger.warn('Failed to infer address type from derivation path', e);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
if (!addressType) {
|
|
848
|
+
throw new Error('Address type not found in walletMap');
|
|
849
|
+
}
|
|
850
|
+
const { derivedPrivateKey } = await this.exportKey({
|
|
851
|
+
accountAddress: accountAddress,
|
|
852
|
+
chainName: this.chainName,
|
|
853
|
+
bitcoinConfig: {
|
|
854
|
+
addressType
|
|
855
|
+
},
|
|
856
|
+
password,
|
|
857
|
+
signedSessionId,
|
|
858
|
+
mfaToken
|
|
859
|
+
});
|
|
860
|
+
if (!derivedPrivateKey) {
|
|
861
|
+
throw new Error('Failed to derive private key');
|
|
862
|
+
}
|
|
863
|
+
// Convert MPC private key to Bitcoin WIF format
|
|
864
|
+
return this.convertPrivateKeyToBitcoinFormat(derivedPrivateKey, browser.BitcoinNetwork.MAINNET);
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* Converts MPC private key to Bitcoin WIF (Wallet Import Format)
|
|
868
|
+
* Uses the utility function from utils.ts for the core conversion logic
|
|
869
|
+
* @param privateKey - The private key to convert to a Bitcoin WIF format
|
|
870
|
+
* @param network - The network to convert the private key to a Bitcoin WIF format for
|
|
871
|
+
* @returns The Bitcoin WIF format
|
|
872
|
+
*/ convertPrivateKeyToBitcoinFormat(privateKey, network) {
|
|
873
|
+
try {
|
|
874
|
+
const wif = privateKeyToWIF(privateKey, network, {
|
|
875
|
+
compressed: true,
|
|
876
|
+
onWarning: (message, data)=>{
|
|
877
|
+
this.logger.warn(`[BTC Client] convertPrivateKeyToBitcoinFormat - ${message}`, data);
|
|
878
|
+
}
|
|
879
|
+
});
|
|
880
|
+
this.logger.debug('[BTC Client] convertPrivateKeyToBitcoinFormat - converted to WIF', {
|
|
881
|
+
network,
|
|
882
|
+
wifLength: wif.length,
|
|
883
|
+
wifPrefix: wif.substring(0, 1)
|
|
884
|
+
});
|
|
885
|
+
return wif;
|
|
886
|
+
} catch (error) {
|
|
887
|
+
this.logger.error('[BTC Client] convertPrivateKeyToBitcoinFormat - error converting to WIF', {
|
|
888
|
+
error,
|
|
889
|
+
privateKeyLength: privateKey.length,
|
|
890
|
+
network
|
|
891
|
+
});
|
|
892
|
+
const cleanPrivateKey = privateKey.startsWith('0x') ? privateKey.slice(2) : privateKey;
|
|
893
|
+
return cleanPrivateKey;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
/**
|
|
897
|
+
* Sign a Bitcoin Transaction (PSBT)
|
|
898
|
+
*
|
|
899
|
+
* @param transaction - The PSBT to sign in hex format
|
|
900
|
+
* @param senderAddress - The address of the sender
|
|
901
|
+
* @param password - The password to use for the transaction
|
|
902
|
+
* @param signedSessionId - The signed session ID to use for the transaction
|
|
903
|
+
* @param authToken - The auth token to use for the transaction
|
|
904
|
+
* @param mfaToken - The MFA token to use for the transaction
|
|
905
|
+
* @param context - The context to use for the transaction
|
|
906
|
+
* @param onError - The error handler
|
|
907
|
+
* @returns The signed PSBT
|
|
908
|
+
*/ async signTransaction({ transaction, senderAddress, network, password, signedSessionId, mfaToken, context, onError }) {
|
|
909
|
+
try {
|
|
910
|
+
await this.verifyPassword({
|
|
911
|
+
accountAddress: senderAddress,
|
|
912
|
+
password,
|
|
913
|
+
walletOperation: browser.WalletOperation.SIGN_TRANSACTION,
|
|
914
|
+
signedSessionId
|
|
915
|
+
});
|
|
916
|
+
if (!senderAddress) {
|
|
917
|
+
throw new Error(browser.ERROR_ACCOUNT_ADDRESS_REQUIRED);
|
|
918
|
+
}
|
|
919
|
+
const walletProperties = this.walletMap[senderAddress];
|
|
920
|
+
if (!walletProperties) {
|
|
921
|
+
throw new Error('Wallet not found in walletMap');
|
|
922
|
+
}
|
|
923
|
+
const derivationPath = walletProperties.derivationPath;
|
|
924
|
+
if (!derivationPath) {
|
|
925
|
+
throw new Error('Derivation path missing in walletMap');
|
|
926
|
+
}
|
|
927
|
+
let addressType = walletProperties.addressType;
|
|
928
|
+
if (!addressType) {
|
|
929
|
+
addressType = getAddressTypeFromDerivationPath(derivationPath);
|
|
930
|
+
}
|
|
931
|
+
const bitcoinConfig = {
|
|
932
|
+
addressType,
|
|
933
|
+
network
|
|
934
|
+
};
|
|
935
|
+
const psbt = bitcoin__namespace.Psbt.fromBase64(transaction);
|
|
936
|
+
const clientKeyShares = await this.getClientKeySharesFromLocalStorage({
|
|
937
|
+
accountAddress: senderAddress
|
|
938
|
+
});
|
|
939
|
+
if (!clientKeyShares || clientKeyShares.length === 0) {
|
|
940
|
+
throw new Error('No key shares found');
|
|
941
|
+
}
|
|
942
|
+
const derivedPublicKey = await this.derivePublicKey({
|
|
943
|
+
chainName: this.chainName,
|
|
944
|
+
keyShare: clientKeyShares[0],
|
|
945
|
+
derivationPath: new Uint32Array(Object.values(JSON.parse(derivationPath))),
|
|
946
|
+
bitcoinConfig
|
|
947
|
+
});
|
|
948
|
+
if (!derivedPublicKey) {
|
|
949
|
+
throw new Error('Failed to derive public key');
|
|
950
|
+
}
|
|
951
|
+
const pubKey = normalizePublicKey(derivedPublicKey, addressType);
|
|
952
|
+
// Iterate and sign inputs in parallel for better performance
|
|
953
|
+
await Promise.all(psbt.data.inputs.map(async (input, i)=>{
|
|
954
|
+
if (!input.witnessUtxo) {
|
|
955
|
+
throw new Error(`Input ${i} missing witnessUtxo`);
|
|
956
|
+
}
|
|
957
|
+
const { script, value } = input.witnessUtxo;
|
|
958
|
+
const p2pkh = bitcoin__namespace.payments.p2pkh({
|
|
959
|
+
hash: script.slice(2),
|
|
960
|
+
network: getBitcoinNetwork(network)
|
|
961
|
+
});
|
|
962
|
+
const scriptCode = p2pkh.output;
|
|
963
|
+
if (!scriptCode) throw new Error('Failed to generate scriptCode');
|
|
964
|
+
const tx = psbt.__CACHE.__TX;
|
|
965
|
+
const hash = tx.hashForWitnessV0(i, scriptCode, value, bitcoin__namespace.Transaction.SIGHASH_ALL);
|
|
966
|
+
const signature = await this.sign({
|
|
967
|
+
message: new Uint8Array(hash),
|
|
968
|
+
accountAddress: senderAddress,
|
|
969
|
+
chainName: this.chainName,
|
|
970
|
+
password,
|
|
971
|
+
signedSessionId,
|
|
972
|
+
mfaToken,
|
|
973
|
+
isFormatted: true,
|
|
974
|
+
context: context,
|
|
975
|
+
bitcoinConfig,
|
|
976
|
+
onError
|
|
977
|
+
});
|
|
978
|
+
const derSignature = convertSignatureToDER(signature);
|
|
979
|
+
psbt.updateInput(i, {
|
|
980
|
+
partialSig: [
|
|
981
|
+
{
|
|
982
|
+
pubkey: pubKey,
|
|
983
|
+
signature: new Uint8Array(derSignature)
|
|
984
|
+
}
|
|
985
|
+
]
|
|
986
|
+
});
|
|
987
|
+
}));
|
|
988
|
+
psbt.finalizeAllInputs();
|
|
989
|
+
const transactionHex = psbt.extractTransaction().toHex();
|
|
990
|
+
this.logger.debug('[BTC Client] signTransaction - transactionHex', {
|
|
991
|
+
transactionHex
|
|
992
|
+
});
|
|
993
|
+
return transactionHex;
|
|
994
|
+
} catch (error) {
|
|
995
|
+
if (onError) {
|
|
996
|
+
onError(error);
|
|
997
|
+
}
|
|
998
|
+
throw error;
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
/**
|
|
1002
|
+
* Creates a PSBT for a transaction
|
|
1003
|
+
* @param receiverAddress - The address to send funds to
|
|
1004
|
+
* @param amount - The amount to send in satoshis
|
|
1005
|
+
* @param senderAddress - The address to send funds from
|
|
1006
|
+
* @param network - The network to use (mainnet/testnet)
|
|
1007
|
+
* @param feeRateLevel - The fee rate level to use (fast, medium, slow)
|
|
1008
|
+
* @returns The PSBT in hex format
|
|
1009
|
+
*/ async createTransaction({ receiverAddress, amount, senderAddress, network, feeRateLevel = 'medium' }) {
|
|
1010
|
+
this.logger.debug('[BTC Client] Creating transaction', {
|
|
1011
|
+
receiverAddress,
|
|
1012
|
+
amount,
|
|
1013
|
+
senderAddress,
|
|
1014
|
+
network,
|
|
1015
|
+
feeRateLevel
|
|
1016
|
+
});
|
|
1017
|
+
if (!senderAddress) {
|
|
1018
|
+
throw new Error(browser.ERROR_ACCOUNT_ADDRESS_REQUIRED);
|
|
1019
|
+
}
|
|
1020
|
+
const walletProperties = this.walletMap[senderAddress];
|
|
1021
|
+
if (!walletProperties) {
|
|
1022
|
+
throw new Error('Wallet not found in walletMap');
|
|
1023
|
+
}
|
|
1024
|
+
let addressType = walletProperties.addressType;
|
|
1025
|
+
if (!addressType && walletProperties.derivationPath) {
|
|
1026
|
+
addressType = getAddressTypeFromDerivationPath(walletProperties.derivationPath);
|
|
1027
|
+
}
|
|
1028
|
+
if (!addressType) {
|
|
1029
|
+
throw new Error('Address type not determined');
|
|
1030
|
+
}
|
|
1031
|
+
if (addressType === browser.BitcoinAddressType.TAPROOT) {
|
|
1032
|
+
throw new Error('Taproot transaction creation not yet supported');
|
|
1033
|
+
}
|
|
1034
|
+
const bitcoinNetwork = network === 'testnet' ? bitcoin__namespace.networks.testnet : bitcoin__namespace.networks.bitcoin;
|
|
1035
|
+
// Fetch fee rates
|
|
1036
|
+
const feeRates = await getFeeRates({
|
|
1037
|
+
network
|
|
1038
|
+
});
|
|
1039
|
+
const feeRate = feeRates[feeRateLevel];
|
|
1040
|
+
// Fetch UTXOs
|
|
1041
|
+
const utxos = await getUTXOs({
|
|
1042
|
+
address: senderAddress,
|
|
1043
|
+
network
|
|
1044
|
+
});
|
|
1045
|
+
// Select UTXOs
|
|
1046
|
+
const { inputs, change, fee } = selectUTXOs({
|
|
1047
|
+
utxos,
|
|
1048
|
+
amount: BigInt(amount),
|
|
1049
|
+
feeRate
|
|
1050
|
+
});
|
|
1051
|
+
const psbt = new bitcoin__namespace.Psbt({
|
|
1052
|
+
network: bitcoinNetwork
|
|
1053
|
+
});
|
|
1054
|
+
// Prepare inputs
|
|
1055
|
+
await Promise.all(inputs.map(async (utxo)=>{
|
|
1056
|
+
const txHex = await this.getTxHex(utxo.txid, network);
|
|
1057
|
+
const inputData = {
|
|
1058
|
+
hash: utxo.txid,
|
|
1059
|
+
index: utxo.vout,
|
|
1060
|
+
nonWitnessUtxo: Buffer.from(txHex, 'hex')
|
|
1061
|
+
};
|
|
1062
|
+
if (addressType === browser.BitcoinAddressType.NATIVE_SEGWIT) {
|
|
1063
|
+
const p2wpkh = bitcoin__namespace.payments.p2wpkh({
|
|
1064
|
+
address: senderAddress,
|
|
1065
|
+
network: bitcoinNetwork
|
|
1066
|
+
});
|
|
1067
|
+
if (!p2wpkh.output) {
|
|
1068
|
+
throw new Error('Failed to generate scriptCode for P2WPKH');
|
|
1069
|
+
}
|
|
1070
|
+
inputData.witnessUtxo = {
|
|
1071
|
+
script: p2wpkh.output,
|
|
1072
|
+
value: BigInt(utxo.value)
|
|
1073
|
+
};
|
|
1074
|
+
} else if (addressType === browser.BitcoinAddressType.TAPROOT) {
|
|
1075
|
+
throw new Error('Taproot transaction creation not yet supported');
|
|
1076
|
+
}
|
|
1077
|
+
psbt.addInput(inputData);
|
|
1078
|
+
}));
|
|
1079
|
+
// Add Receiver Output
|
|
1080
|
+
psbt.addOutput({
|
|
1081
|
+
address: receiverAddress,
|
|
1082
|
+
value: BigInt(amount)
|
|
1083
|
+
});
|
|
1084
|
+
// Add Change Output
|
|
1085
|
+
if (change > 0n) {
|
|
1086
|
+
psbt.addOutput({
|
|
1087
|
+
address: senderAddress,
|
|
1088
|
+
value: change
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
this.logger.debug('[BTC Client] Transaction created', {
|
|
1092
|
+
fee,
|
|
1093
|
+
change,
|
|
1094
|
+
inputCount: inputs.length
|
|
1095
|
+
});
|
|
1096
|
+
return psbt.toHex();
|
|
1097
|
+
}
|
|
1098
|
+
/**
|
|
1099
|
+
* Fetches the raw transaction hex for a given transaction ID
|
|
1100
|
+
* @param txid - The transaction ID
|
|
1101
|
+
* @param network - The network
|
|
1102
|
+
* @returns The transaction hex
|
|
1103
|
+
*/ async getTxHex(txid, network) {
|
|
1104
|
+
const baseUrl = getDefaultRpcUrl(network);
|
|
1105
|
+
const response = await fetch(`${baseUrl}/tx/${txid}/hex`);
|
|
1106
|
+
if (!response.ok) {
|
|
1107
|
+
throw new Error(`Failed to fetch TX hex: ${response.statusText}`);
|
|
1108
|
+
}
|
|
1109
|
+
return await response.text();
|
|
1110
|
+
}
|
|
1111
|
+
/**
|
|
1112
|
+
* Gets the Bitcoin wallets
|
|
1113
|
+
* @returns The Bitcoin wallets
|
|
1114
|
+
*/ async getBitcoinWallets() {
|
|
1115
|
+
const wallets = await this.getWallets();
|
|
1116
|
+
const btcWallets = wallets.filter((wallet)=>wallet.chainName === 'bitcoin');
|
|
1117
|
+
return btcWallets;
|
|
1118
|
+
}
|
|
1119
|
+
/**
|
|
1120
|
+
* Creates a new instance of DynamicBtcWalletClient
|
|
1121
|
+
* @param props - The client properties
|
|
1122
|
+
*/ constructor({ environmentId, authToken, baseApiUrl, baseMPCRelayApiUrl, storageKey, debug, featureFlags, authMode = browser.AuthMode.HEADER, sdkVersion, forwardMPCClient }){
|
|
1123
|
+
super({
|
|
1124
|
+
environmentId,
|
|
1125
|
+
authToken,
|
|
1126
|
+
baseApiUrl,
|
|
1127
|
+
baseMPCRelayApiUrl,
|
|
1128
|
+
storageKey,
|
|
1129
|
+
debug,
|
|
1130
|
+
featureFlags,
|
|
1131
|
+
authMode,
|
|
1132
|
+
sdkVersion,
|
|
1133
|
+
forwardMPCClient
|
|
1134
|
+
}), this.chainName = 'BTC';
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
exports.DynamicBtcWalletClient = DynamicBtcWalletClient;
|