@utexo/rgb-sdk 1.0.1
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/LICENCE +201 -0
- package/Readme.md +521 -0
- package/dist/index.cjs +1915 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.mts +998 -0
- package/dist/index.d.ts +998 -0
- package/dist/index.mjs +1830 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +65 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1830 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
2
|
+
import path__default from 'path';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import * as rgblib from 'rgb-lib';
|
|
5
|
+
import * as noble from '@noble/hashes/legacy.js';
|
|
6
|
+
import * as os from 'os';
|
|
7
|
+
|
|
8
|
+
// src/client/rgb-lib-client.ts
|
|
9
|
+
|
|
10
|
+
// src/errors/index.ts
|
|
11
|
+
var SDKError = class _SDKError extends Error {
|
|
12
|
+
constructor(message, code, statusCode, cause) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = "SDKError";
|
|
15
|
+
this.code = code;
|
|
16
|
+
this.statusCode = statusCode;
|
|
17
|
+
this.cause = cause;
|
|
18
|
+
Object.setPrototypeOf(this, _SDKError.prototype);
|
|
19
|
+
if (Error.captureStackTrace) {
|
|
20
|
+
Error.captureStackTrace(this, _SDKError);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Convert error to JSON for logging
|
|
25
|
+
*/
|
|
26
|
+
toJSON() {
|
|
27
|
+
return {
|
|
28
|
+
name: this.name,
|
|
29
|
+
message: this.message,
|
|
30
|
+
code: this.code,
|
|
31
|
+
statusCode: this.statusCode,
|
|
32
|
+
cause: this.cause?.message,
|
|
33
|
+
stack: this.stack
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
var NetworkError = class _NetworkError extends SDKError {
|
|
38
|
+
constructor(message, statusCode, cause) {
|
|
39
|
+
super(message, "NETWORK_ERROR", statusCode, cause);
|
|
40
|
+
this.name = "NetworkError";
|
|
41
|
+
Object.setPrototypeOf(this, _NetworkError.prototype);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var ValidationError = class _ValidationError extends SDKError {
|
|
45
|
+
constructor(message, field) {
|
|
46
|
+
super(message, "VALIDATION_ERROR");
|
|
47
|
+
this.name = "ValidationError";
|
|
48
|
+
this.field = field;
|
|
49
|
+
Object.setPrototypeOf(this, _ValidationError.prototype);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
var WalletError = class _WalletError extends SDKError {
|
|
53
|
+
constructor(message, code, cause) {
|
|
54
|
+
super(message, code || "WALLET_ERROR", void 0, cause);
|
|
55
|
+
this.name = "WalletError";
|
|
56
|
+
Object.setPrototypeOf(this, _WalletError.prototype);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
var CryptoError = class _CryptoError extends SDKError {
|
|
60
|
+
constructor(message, cause) {
|
|
61
|
+
super(message, "CRYPTO_ERROR", void 0, cause);
|
|
62
|
+
this.name = "CryptoError";
|
|
63
|
+
Object.setPrototypeOf(this, _CryptoError.prototype);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
var ConfigurationError = class _ConfigurationError extends SDKError {
|
|
67
|
+
constructor(message, field) {
|
|
68
|
+
super(message, "CONFIGURATION_ERROR");
|
|
69
|
+
this.name = "ConfigurationError";
|
|
70
|
+
Object.setPrototypeOf(this, _ConfigurationError.prototype);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
var BadRequestError = class _BadRequestError extends SDKError {
|
|
74
|
+
constructor(message, cause) {
|
|
75
|
+
super(message, "BAD_REQUEST", 400, cause);
|
|
76
|
+
this.name = "BadRequestError";
|
|
77
|
+
Object.setPrototypeOf(this, _BadRequestError.prototype);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
var NotFoundError = class _NotFoundError extends SDKError {
|
|
81
|
+
constructor(message, cause) {
|
|
82
|
+
super(message, "NOT_FOUND", 404, cause);
|
|
83
|
+
this.name = "NotFoundError";
|
|
84
|
+
Object.setPrototypeOf(this, _NotFoundError.prototype);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
var ConflictError = class _ConflictError extends SDKError {
|
|
88
|
+
constructor(message, cause) {
|
|
89
|
+
super(message, "CONFLICT", 409, cause);
|
|
90
|
+
this.name = "ConflictError";
|
|
91
|
+
Object.setPrototypeOf(this, _ConflictError.prototype);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
var RgbNodeError = class _RgbNodeError extends SDKError {
|
|
95
|
+
constructor(message, statusCode, cause) {
|
|
96
|
+
super(message, "RGB_NODE_ERROR", statusCode, cause);
|
|
97
|
+
this.name = "RgbNodeError";
|
|
98
|
+
Object.setPrototypeOf(this, _RgbNodeError.prototype);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// src/constants/derivation.ts
|
|
103
|
+
var DERIVATION_PURPOSE = 86;
|
|
104
|
+
var DERIVATION_ACCOUNT = 0;
|
|
105
|
+
var KEYCHAIN_RGB = 0;
|
|
106
|
+
var KEYCHAIN_BTC = 0;
|
|
107
|
+
|
|
108
|
+
// src/constants/network.ts
|
|
109
|
+
var COIN_RGB_MAINNET = 827166;
|
|
110
|
+
var COIN_RGB_TESTNET = 827167;
|
|
111
|
+
var COIN_BITCOIN_MAINNET = 0;
|
|
112
|
+
var COIN_BITCOIN_TESTNET = 1;
|
|
113
|
+
var NETWORK_MAP = {
|
|
114
|
+
"0": "mainnet",
|
|
115
|
+
"1": "testnet",
|
|
116
|
+
"2": "testnet",
|
|
117
|
+
// Alternative testnet number (also maps to testnet)
|
|
118
|
+
"3": "regtest",
|
|
119
|
+
"signet": "signet",
|
|
120
|
+
"mainnet": "mainnet",
|
|
121
|
+
"testnet": "testnet",
|
|
122
|
+
"testnet4": "testnet4",
|
|
123
|
+
"regtest": "regtest"
|
|
124
|
+
};
|
|
125
|
+
var BIP32_VERSIONS = {
|
|
126
|
+
mainnet: {
|
|
127
|
+
public: 76067358,
|
|
128
|
+
private: 76066276
|
|
129
|
+
},
|
|
130
|
+
testnet: {
|
|
131
|
+
public: 70617039,
|
|
132
|
+
private: 70615956
|
|
133
|
+
},
|
|
134
|
+
testnet4: {
|
|
135
|
+
public: 70617039,
|
|
136
|
+
private: 70615956
|
|
137
|
+
},
|
|
138
|
+
signet: {
|
|
139
|
+
public: 70617039,
|
|
140
|
+
private: 70615956
|
|
141
|
+
},
|
|
142
|
+
regtest: {
|
|
143
|
+
public: 70617039,
|
|
144
|
+
private: 70615956
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
// src/constants/defaults.ts
|
|
149
|
+
var DEFAULT_NETWORK = "regtest";
|
|
150
|
+
var DEFAULT_API_TIMEOUT = 12e4;
|
|
151
|
+
var DEFAULT_MAX_RETRIES = 3;
|
|
152
|
+
var DEFAULT_LOG_LEVEL = 3;
|
|
153
|
+
|
|
154
|
+
// src/utils/validation.ts
|
|
155
|
+
var VALID_NETWORKS = ["mainnet", "testnet", "testnet4", "signet", "regtest"];
|
|
156
|
+
function validateNetwork(network) {
|
|
157
|
+
const key = String(network);
|
|
158
|
+
const normalized = NETWORK_MAP[key];
|
|
159
|
+
if (!normalized || !VALID_NETWORKS.includes(normalized)) {
|
|
160
|
+
throw new ValidationError(
|
|
161
|
+
`Invalid network: ${network}. Must be one of: ${VALID_NETWORKS.join(", ")}`,
|
|
162
|
+
"network"
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function normalizeNetwork(network) {
|
|
167
|
+
validateNetwork(network);
|
|
168
|
+
const key = String(network);
|
|
169
|
+
return NETWORK_MAP[key];
|
|
170
|
+
}
|
|
171
|
+
function validateMnemonic(mnemonic, field = "mnemonic") {
|
|
172
|
+
if (!mnemonic || typeof mnemonic !== "string" || mnemonic.trim().length === 0) {
|
|
173
|
+
throw new ValidationError(`${field} must be a non-empty string`, field);
|
|
174
|
+
}
|
|
175
|
+
const words = mnemonic.trim().split(/\s+/);
|
|
176
|
+
if (words.length !== 12 && words.length !== 24) {
|
|
177
|
+
throw new ValidationError(
|
|
178
|
+
`${field} must be 12 or 24 words, got ${words.length} words`,
|
|
179
|
+
field
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function validateBase64(base64, field = "data") {
|
|
184
|
+
if (!base64 || typeof base64 !== "string" || base64.trim().length === 0) {
|
|
185
|
+
throw new ValidationError(`${field} must be a non-empty string`, field);
|
|
186
|
+
}
|
|
187
|
+
const base64Regex = /^[A-Za-z0-9+/=]+$/;
|
|
188
|
+
if (!base64Regex.test(base64.trim())) {
|
|
189
|
+
throw new ValidationError(`Invalid base64 format for ${field}`, field);
|
|
190
|
+
}
|
|
191
|
+
try {
|
|
192
|
+
Buffer.from(base64.trim(), "base64");
|
|
193
|
+
} catch (error) {
|
|
194
|
+
throw new ValidationError(`Invalid base64 encoding for ${field}`, field);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function validatePsbt(psbt, field = "psbt") {
|
|
198
|
+
validateBase64(psbt, field);
|
|
199
|
+
const psbtString = String(psbt).trim();
|
|
200
|
+
if (psbtString.length < 50) {
|
|
201
|
+
throw new ValidationError(`${field} appears to be too short to be a valid PSBT`, field);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function validateHex(hex, field = "data") {
|
|
205
|
+
if (!hex || typeof hex !== "string" || hex.trim().length === 0) {
|
|
206
|
+
throw new ValidationError(`${field} must be a non-empty string`, field);
|
|
207
|
+
}
|
|
208
|
+
const hexRegex = /^[0-9a-fA-F]+$/;
|
|
209
|
+
if (!hexRegex.test(hex.trim())) {
|
|
210
|
+
throw new ValidationError(`Invalid hex format for ${field}`, field);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
function validateRequired(value, field) {
|
|
214
|
+
if (value === null || value === void 0) {
|
|
215
|
+
throw new ValidationError(`${field} is required`, field);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function validateString(value, field) {
|
|
219
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
220
|
+
throw new ValidationError(`${field} must be a non-empty string`, field);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
var DEFAULT_TRANSPORT_ENDPOINT = "rpcs://proxy.iriswallet.com/0.2/json-rpc";
|
|
224
|
+
function mapNetworkToRgbLib(network) {
|
|
225
|
+
const networkMap = {
|
|
226
|
+
"mainnet": "Mainnet",
|
|
227
|
+
"testnet": "Testnet",
|
|
228
|
+
"testnet4": "Testnet4",
|
|
229
|
+
"signet": "Signet",
|
|
230
|
+
"regtest": "Regtest"
|
|
231
|
+
};
|
|
232
|
+
const networkStr = String(network).toLowerCase();
|
|
233
|
+
return networkMap[networkStr] || "Regtest";
|
|
234
|
+
}
|
|
235
|
+
var restoreWallet = (params) => {
|
|
236
|
+
const { backupFilePath, password, dataDir } = params;
|
|
237
|
+
if (!fs.existsSync(backupFilePath)) {
|
|
238
|
+
throw new ValidationError("Backup file not found", "backup");
|
|
239
|
+
}
|
|
240
|
+
if (!fs.existsSync(dataDir)) {
|
|
241
|
+
throw new ValidationError(`Restore directory does not exist: ${dataDir}`, "restoreDir");
|
|
242
|
+
}
|
|
243
|
+
rgblib.restoreBackup(backupFilePath, password, dataDir);
|
|
244
|
+
return {
|
|
245
|
+
message: "Wallet restored successfully"
|
|
246
|
+
};
|
|
247
|
+
};
|
|
248
|
+
var RGBLibClient = class {
|
|
249
|
+
constructor(params) {
|
|
250
|
+
this.online = null;
|
|
251
|
+
this.xpubVan = params.xpubVan;
|
|
252
|
+
this.xpubCol = params.xpubCol;
|
|
253
|
+
this.masterFingerprint = params.masterFingerprint;
|
|
254
|
+
this.originalNetwork = params.network;
|
|
255
|
+
this.network = normalizeNetwork(this.originalNetwork);
|
|
256
|
+
this.dataDir = params.dataDir;
|
|
257
|
+
this.transportEndpoint = params.transportEndpoint || DEFAULT_TRANSPORT_ENDPOINT;
|
|
258
|
+
if (params.indexerUrl) {
|
|
259
|
+
this.indexerUrl = params.indexerUrl;
|
|
260
|
+
} else {
|
|
261
|
+
const defaultIndexerUrls = {
|
|
262
|
+
"mainnet": "ssl://electrum.iriswallet.com:50003",
|
|
263
|
+
"testnet": "ssl://electrum.iriswallet.com:50013",
|
|
264
|
+
"testnet4": "ssl://electrum.iriswallet.com:50053",
|
|
265
|
+
"signet": "tcp://46.224.75.237:50001",
|
|
266
|
+
"regtest": "tcp://regtest.thunderstack.org:50001"
|
|
267
|
+
};
|
|
268
|
+
this.indexerUrl = defaultIndexerUrls[this.network] || defaultIndexerUrls["regtest"];
|
|
269
|
+
}
|
|
270
|
+
if (!fs.existsSync(this.dataDir)) {
|
|
271
|
+
fs.mkdirSync(this.dataDir, { recursive: true });
|
|
272
|
+
}
|
|
273
|
+
const walletData = {
|
|
274
|
+
dataDir: this.dataDir,
|
|
275
|
+
bitcoinNetwork: mapNetworkToRgbLib(this.originalNetwork),
|
|
276
|
+
databaseType: rgblib.DatabaseType.Sqlite,
|
|
277
|
+
accountXpubVanilla: this.xpubVan,
|
|
278
|
+
accountXpubColored: this.xpubCol,
|
|
279
|
+
masterFingerprint: this.masterFingerprint,
|
|
280
|
+
maxAllocationsPerUtxo: "1",
|
|
281
|
+
vanillaKeychain: "1",
|
|
282
|
+
supportedSchemas: [
|
|
283
|
+
rgblib.AssetSchema.Cfa,
|
|
284
|
+
rgblib.AssetSchema.Nia,
|
|
285
|
+
rgblib.AssetSchema.Uda
|
|
286
|
+
]
|
|
287
|
+
};
|
|
288
|
+
try {
|
|
289
|
+
this.wallet = new rgblib.Wallet(new rgblib.WalletData(walletData));
|
|
290
|
+
} catch (error) {
|
|
291
|
+
throw new WalletError("Failed to initialize rgb-lib wallet", void 0, error);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Ensure online connection is established
|
|
296
|
+
*/
|
|
297
|
+
ensureOnline() {
|
|
298
|
+
if (this.online) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
try {
|
|
302
|
+
console.log("indexerUrl", this.indexerUrl);
|
|
303
|
+
this.online = this.wallet.goOnline(false, this.indexerUrl);
|
|
304
|
+
} catch (error) {
|
|
305
|
+
throw new WalletError("Failed to establish online connection", void 0, error);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Get online object, creating it if needed
|
|
310
|
+
*/
|
|
311
|
+
getOnline() {
|
|
312
|
+
this.ensureOnline();
|
|
313
|
+
return this.online;
|
|
314
|
+
}
|
|
315
|
+
registerWallet() {
|
|
316
|
+
const online = this.getOnline();
|
|
317
|
+
const address = this.wallet.getAddress();
|
|
318
|
+
const btcBalance = this.wallet.getBtcBalance(online, false);
|
|
319
|
+
return {
|
|
320
|
+
address,
|
|
321
|
+
btcBalance
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
getBtcBalance() {
|
|
325
|
+
const online = this.getOnline();
|
|
326
|
+
return this.wallet.getBtcBalance(online, false);
|
|
327
|
+
}
|
|
328
|
+
getAddress() {
|
|
329
|
+
return this.wallet.getAddress();
|
|
330
|
+
}
|
|
331
|
+
listUnspents() {
|
|
332
|
+
const online = this.getOnline();
|
|
333
|
+
return this.wallet.listUnspents(online, false, false);
|
|
334
|
+
}
|
|
335
|
+
createUtxosBegin(params) {
|
|
336
|
+
const online = this.getOnline();
|
|
337
|
+
const upTo = params.upTo ?? false;
|
|
338
|
+
const num = params.num !== void 0 ? String(params.num) : null;
|
|
339
|
+
const size = params.size !== void 0 ? String(params.size) : null;
|
|
340
|
+
const feeRate = params.feeRate ? String(params.feeRate) : "1";
|
|
341
|
+
const skipSync = false;
|
|
342
|
+
return this.wallet.createUtxosBegin(online, upTo, num, size, feeRate, skipSync);
|
|
343
|
+
}
|
|
344
|
+
createUtxosEnd(params) {
|
|
345
|
+
const online = this.getOnline();
|
|
346
|
+
const signedPsbt = params.signedPsbt;
|
|
347
|
+
const skipSync = params.skipSync ?? false;
|
|
348
|
+
return this.wallet.createUtxosEnd(online, signedPsbt, skipSync);
|
|
349
|
+
}
|
|
350
|
+
sendBegin(params) {
|
|
351
|
+
const online = this.getOnline();
|
|
352
|
+
console.log("sendBegin params", params);
|
|
353
|
+
const feeRate = String(params.feeRate ?? 1);
|
|
354
|
+
const minConfirmations = String(params.minConfirmations ?? 1);
|
|
355
|
+
const donation = false;
|
|
356
|
+
let assetId = params.assetId;
|
|
357
|
+
let amount = params.amount;
|
|
358
|
+
let recipientId;
|
|
359
|
+
let transportEndpoints = [];
|
|
360
|
+
let witnessData = null;
|
|
361
|
+
if (params.witnessData && params.witnessData.amountSat) {
|
|
362
|
+
witnessData = {
|
|
363
|
+
amountSat: String(params.witnessData.amountSat),
|
|
364
|
+
blinding: params.witnessData.blinding ? Number(params.witnessData.blinding) : null
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
if (params.invoice) {
|
|
368
|
+
const invoiceStr = params.invoice;
|
|
369
|
+
const invoiceData = this.decodeRGBInvoice({ invoice: invoiceStr });
|
|
370
|
+
recipientId = invoiceData.recipientId;
|
|
371
|
+
transportEndpoints = invoiceData.transportEndpoints;
|
|
372
|
+
}
|
|
373
|
+
if (transportEndpoints.length === 0) {
|
|
374
|
+
transportEndpoints = [this.transportEndpoint];
|
|
375
|
+
}
|
|
376
|
+
if (!assetId) {
|
|
377
|
+
throw new ValidationError("asset_id is required for send operation", "asset_id");
|
|
378
|
+
}
|
|
379
|
+
if (!recipientId) {
|
|
380
|
+
throw new ValidationError("Could not extract recipient_id from invoice", "invoice");
|
|
381
|
+
}
|
|
382
|
+
if (!amount) {
|
|
383
|
+
throw new ValidationError("amount is required for send operation", "amount");
|
|
384
|
+
}
|
|
385
|
+
const assignment = { Fungible: amount };
|
|
386
|
+
const recipientMap = {
|
|
387
|
+
[assetId]: [{
|
|
388
|
+
recipientId,
|
|
389
|
+
witnessData,
|
|
390
|
+
assignment,
|
|
391
|
+
transportEndpoints
|
|
392
|
+
}]
|
|
393
|
+
};
|
|
394
|
+
const psbt = this.wallet.sendBegin(
|
|
395
|
+
online,
|
|
396
|
+
recipientMap,
|
|
397
|
+
donation,
|
|
398
|
+
feeRate,
|
|
399
|
+
minConfirmations
|
|
400
|
+
);
|
|
401
|
+
return psbt;
|
|
402
|
+
}
|
|
403
|
+
sendEnd(params) {
|
|
404
|
+
const online = this.getOnline();
|
|
405
|
+
const signedPsbt = params.signedPsbt;
|
|
406
|
+
const skipSync = params.skipSync ?? false;
|
|
407
|
+
return this.wallet.sendEnd(online, signedPsbt, skipSync);
|
|
408
|
+
}
|
|
409
|
+
sendBtcBegin(params) {
|
|
410
|
+
const online = this.getOnline();
|
|
411
|
+
const address = params.address;
|
|
412
|
+
const amount = String(params.amount);
|
|
413
|
+
const feeRate = String(params.feeRate);
|
|
414
|
+
const skipSync = params.skipSync ?? false;
|
|
415
|
+
return this.wallet.sendBtcBegin(online, address, amount, feeRate, skipSync);
|
|
416
|
+
}
|
|
417
|
+
sendBtcEnd(params) {
|
|
418
|
+
const online = this.getOnline();
|
|
419
|
+
const signedPsbt = params.signedPsbt;
|
|
420
|
+
const skipSync = params.skipSync ?? false;
|
|
421
|
+
return this.wallet.sendBtcEnd(online, signedPsbt, skipSync);
|
|
422
|
+
}
|
|
423
|
+
getFeeEstimation(params) {
|
|
424
|
+
const online = this.getOnline();
|
|
425
|
+
const blocks = String(params.blocks);
|
|
426
|
+
try {
|
|
427
|
+
const result = this.wallet.getFeeEstimation(online, blocks);
|
|
428
|
+
if (typeof result === "string") {
|
|
429
|
+
try {
|
|
430
|
+
return JSON.parse(result);
|
|
431
|
+
} catch {
|
|
432
|
+
return result;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return result;
|
|
436
|
+
} catch (error) {
|
|
437
|
+
console.warn("rgb-lib estimation fee are not available, using default fee rate 2");
|
|
438
|
+
return 2;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
blindReceive(params) {
|
|
442
|
+
const assetId = params.assetId || null;
|
|
443
|
+
const assignment = `{"Fungible":${params.amount}}`;
|
|
444
|
+
const durationSeconds = String(params.durationSeconds ?? 2e3);
|
|
445
|
+
const transportEndpoints = [this.transportEndpoint];
|
|
446
|
+
const minConfirmations = String(params.minConfirmations ?? 3);
|
|
447
|
+
return this.wallet.blindReceive(
|
|
448
|
+
assetId,
|
|
449
|
+
assignment,
|
|
450
|
+
durationSeconds,
|
|
451
|
+
transportEndpoints,
|
|
452
|
+
minConfirmations
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
witnessReceive(params) {
|
|
456
|
+
const assetId = params.assetId || null;
|
|
457
|
+
const assignment = `{"Fungible":${params.amount}}`;
|
|
458
|
+
const durationSeconds = String(params.durationSeconds ?? 2e3);
|
|
459
|
+
const transportEndpoints = [this.transportEndpoint];
|
|
460
|
+
const minConfirmations = String(params.minConfirmations ?? 3);
|
|
461
|
+
return this.wallet.witnessReceive(
|
|
462
|
+
assetId,
|
|
463
|
+
assignment,
|
|
464
|
+
durationSeconds,
|
|
465
|
+
transportEndpoints,
|
|
466
|
+
minConfirmations
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
getAssetBalance(asset_id) {
|
|
470
|
+
return this.wallet.getAssetBalance(asset_id);
|
|
471
|
+
}
|
|
472
|
+
issueAssetNia(params) {
|
|
473
|
+
const ticker = params.ticker;
|
|
474
|
+
const name = params.name;
|
|
475
|
+
const precision = String(params.precision);
|
|
476
|
+
const amounts = params.amounts.map((a) => String(a));
|
|
477
|
+
return this.wallet.issueAssetNIA(ticker, name, precision, amounts);
|
|
478
|
+
}
|
|
479
|
+
issueAssetIfa(params) {
|
|
480
|
+
throw new ValidationError("issueAssetIfa is not fully supported in rgb-lib. Use RGB Node server for IFA assets.", "asset");
|
|
481
|
+
}
|
|
482
|
+
inflateBegin(params) {
|
|
483
|
+
throw new ValidationError("inflateBegin is not fully supported in rgb-lib. Use RGB Node server for inflation operations.", "asset");
|
|
484
|
+
}
|
|
485
|
+
inflateEnd(params) {
|
|
486
|
+
throw new ValidationError("inflateEnd is not fully supported in rgb-lib. Use RGB Node server for inflation operations.", "asset");
|
|
487
|
+
}
|
|
488
|
+
listAssets() {
|
|
489
|
+
const filterAssetSchemas = [];
|
|
490
|
+
return this.wallet.listAssets(filterAssetSchemas);
|
|
491
|
+
}
|
|
492
|
+
decodeRGBInvoice(params) {
|
|
493
|
+
const invoiceString = params.invoice;
|
|
494
|
+
const invoice = new rgblib.Invoice(invoiceString);
|
|
495
|
+
try {
|
|
496
|
+
return invoice.invoiceData();
|
|
497
|
+
} finally {
|
|
498
|
+
invoice.drop();
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
refreshWallet() {
|
|
502
|
+
const online = this.getOnline();
|
|
503
|
+
const assetId = null;
|
|
504
|
+
const filter = [];
|
|
505
|
+
const skipSync = false;
|
|
506
|
+
this.wallet.refresh(online, assetId, filter, skipSync);
|
|
507
|
+
}
|
|
508
|
+
dropWallet() {
|
|
509
|
+
if (this.online) {
|
|
510
|
+
rgblib.dropOnline(this.online);
|
|
511
|
+
this.online = null;
|
|
512
|
+
}
|
|
513
|
+
if (this.wallet) {
|
|
514
|
+
this.wallet.drop();
|
|
515
|
+
this.wallet = null;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
listTransactions() {
|
|
519
|
+
const online = this.getOnline();
|
|
520
|
+
const skipSync = false;
|
|
521
|
+
return this.wallet.listTransactions(online, skipSync);
|
|
522
|
+
}
|
|
523
|
+
listTransfers(asset_id) {
|
|
524
|
+
return this.wallet.listTransfers(asset_id ? asset_id : null);
|
|
525
|
+
}
|
|
526
|
+
failTransfers(params) {
|
|
527
|
+
const online = this.getOnline();
|
|
528
|
+
const batchTransferIdx = params.batchTransferIdx !== void 0 ? params.batchTransferIdx : null;
|
|
529
|
+
const noAssetOnly = params.noAssetOnly ?? false;
|
|
530
|
+
const skipSync = params.skipSync ?? false;
|
|
531
|
+
return this.wallet.failTransfers(online, batchTransferIdx, noAssetOnly, skipSync);
|
|
532
|
+
}
|
|
533
|
+
deleteTransfers(params) {
|
|
534
|
+
const batchTransferIdx = params.batchTransferIdx !== void 0 ? params.batchTransferIdx : null;
|
|
535
|
+
const noAssetOnly = params.noAssetOnly ?? false;
|
|
536
|
+
return this.wallet.deleteTransfers(batchTransferIdx, noAssetOnly);
|
|
537
|
+
}
|
|
538
|
+
syncWallet() {
|
|
539
|
+
const online = this.getOnline();
|
|
540
|
+
this.wallet.sync(online);
|
|
541
|
+
}
|
|
542
|
+
createBackup(params) {
|
|
543
|
+
if (!params.backupPath) {
|
|
544
|
+
throw new ValidationError("backupPath is required", "backupPath");
|
|
545
|
+
}
|
|
546
|
+
if (!params.password) {
|
|
547
|
+
throw new ValidationError("password is required", "password");
|
|
548
|
+
}
|
|
549
|
+
if (!fs.existsSync(params.backupPath)) {
|
|
550
|
+
throw new ValidationError(`Backup directory does not exist: ${params.backupPath}`, "backupPath");
|
|
551
|
+
}
|
|
552
|
+
const fullBackupPath = path.join(params.backupPath, `${this.masterFingerprint}.backup`);
|
|
553
|
+
this.wallet.backup(fullBackupPath, params.password);
|
|
554
|
+
return {
|
|
555
|
+
message: "Backup created successfully",
|
|
556
|
+
backupPath: fullBackupPath
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Cleanup resources
|
|
561
|
+
*/
|
|
562
|
+
dispose() {
|
|
563
|
+
this.dropWallet();
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
// src/utils/environment.ts
|
|
568
|
+
function isNode() {
|
|
569
|
+
return typeof process !== "undefined" && process.versions != null && process.versions.node != null;
|
|
570
|
+
}
|
|
571
|
+
function isBare() {
|
|
572
|
+
return typeof globalThis !== "undefined" && globalThis.Bare;
|
|
573
|
+
}
|
|
574
|
+
function isBrowser() {
|
|
575
|
+
return typeof window !== "undefined" && typeof window.document !== "undefined";
|
|
576
|
+
}
|
|
577
|
+
function getEnvironment() {
|
|
578
|
+
if (isNode()) return "node";
|
|
579
|
+
if (isBrowser()) return "browser";
|
|
580
|
+
return "unknown";
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// src/utils/crypto-helpers.ts
|
|
584
|
+
function convertToArrayBuffer(data) {
|
|
585
|
+
if (!data) {
|
|
586
|
+
throw new Error("convertToArrayBuffer: data is undefined or null");
|
|
587
|
+
}
|
|
588
|
+
if (data instanceof Uint8Array) {
|
|
589
|
+
return data.buffer;
|
|
590
|
+
}
|
|
591
|
+
if (data && typeof data === "object" && "byteLength" in data && Object.prototype.toString.call(data) === "[object ArrayBuffer]") {
|
|
592
|
+
return data;
|
|
593
|
+
}
|
|
594
|
+
if (data && typeof data === "object") {
|
|
595
|
+
if ("buffer" in data && data.buffer) {
|
|
596
|
+
const buffer = data.buffer;
|
|
597
|
+
if (buffer instanceof ArrayBuffer) {
|
|
598
|
+
return buffer;
|
|
599
|
+
}
|
|
600
|
+
const uint8 = new Uint8Array(data);
|
|
601
|
+
return uint8.buffer;
|
|
602
|
+
}
|
|
603
|
+
try {
|
|
604
|
+
const uint8 = new Uint8Array(data);
|
|
605
|
+
return uint8.buffer;
|
|
606
|
+
} catch (error) {
|
|
607
|
+
throw new Error(`convertToArrayBuffer: Failed to convert data to ArrayBuffer: ${error instanceof Error ? error.message : String(error)}`);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
try {
|
|
611
|
+
const uint8 = new Uint8Array(data);
|
|
612
|
+
return uint8.buffer;
|
|
613
|
+
} catch (error) {
|
|
614
|
+
throw new Error(`convertToArrayBuffer: Failed to convert data to ArrayBuffer: ${error instanceof Error ? error.message : String(error)}`);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
async function sha256(data) {
|
|
618
|
+
if (isNode() || isBare()) {
|
|
619
|
+
const nodeCrypto2 = "node:crypto";
|
|
620
|
+
const { createHash } = await import(nodeCrypto2);
|
|
621
|
+
return createHash("sha256").update(data).digest();
|
|
622
|
+
} else {
|
|
623
|
+
if (!data) {
|
|
624
|
+
throw new Error("sha256: data is undefined or null");
|
|
625
|
+
}
|
|
626
|
+
const arrayBuffer = convertToArrayBuffer(data);
|
|
627
|
+
return new Uint8Array(await crypto.subtle.digest("SHA-256", arrayBuffer));
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
async function ripemd1602(data) {
|
|
631
|
+
if (isNode()) {
|
|
632
|
+
const nodeCrypto2 = "node:crypto";
|
|
633
|
+
const { createHash } = await import(nodeCrypto2);
|
|
634
|
+
return createHash("ripemd160").update(data).digest();
|
|
635
|
+
} else if (isBare()) {
|
|
636
|
+
return new Uint8Array(await noble.ripemd160(data));
|
|
637
|
+
} else {
|
|
638
|
+
const ripemd160Module = await import('ripemd160');
|
|
639
|
+
const RIPEMD160 = ripemd160Module.default || ripemd160Module;
|
|
640
|
+
const BufferPolyfill = globalThis.Buffer || (await import('buffer')).Buffer;
|
|
641
|
+
const hasher = new RIPEMD160();
|
|
642
|
+
hasher.update(BufferPolyfill.from(data));
|
|
643
|
+
return new Uint8Array(hasher.digest());
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
var nodeCrypto = null;
|
|
647
|
+
async function getNodeCrypto() {
|
|
648
|
+
if (!isNode()) {
|
|
649
|
+
throw new Error("Node.js crypto is only available in Node.js environment");
|
|
650
|
+
}
|
|
651
|
+
if (!nodeCrypto) {
|
|
652
|
+
const nodeCryptoPath = "node:crypto";
|
|
653
|
+
nodeCrypto = await import(nodeCryptoPath);
|
|
654
|
+
}
|
|
655
|
+
return nodeCrypto;
|
|
656
|
+
}
|
|
657
|
+
async function sha256Sync(data) {
|
|
658
|
+
if (!isNode()) {
|
|
659
|
+
return sha256(data);
|
|
660
|
+
}
|
|
661
|
+
if (!data) {
|
|
662
|
+
throw new Error("sha256Sync: data is undefined");
|
|
663
|
+
}
|
|
664
|
+
const crypto2 = await getNodeCrypto();
|
|
665
|
+
if (!crypto2) {
|
|
666
|
+
throw new Error("Node.js crypto is not available");
|
|
667
|
+
}
|
|
668
|
+
return crypto2.createHash("sha256").update(data).digest();
|
|
669
|
+
}
|
|
670
|
+
var ripemd160Sync = async (data) => {
|
|
671
|
+
if (!isNode()) {
|
|
672
|
+
return ripemd1602(data);
|
|
673
|
+
}
|
|
674
|
+
if (!data) {
|
|
675
|
+
throw new Error("ripemd160Sync: data is undefined");
|
|
676
|
+
}
|
|
677
|
+
const crypto2 = await getNodeCrypto();
|
|
678
|
+
if (!crypto2) {
|
|
679
|
+
throw new Error("Node.js crypto is not available");
|
|
680
|
+
}
|
|
681
|
+
return crypto2.createHash("ripemd160").update(data).digest();
|
|
682
|
+
};
|
|
683
|
+
|
|
684
|
+
// src/utils/fingerprint.ts
|
|
685
|
+
async function calculateMasterFingerprint(node) {
|
|
686
|
+
const pubkey = node.publicKey;
|
|
687
|
+
if (!pubkey) {
|
|
688
|
+
throw new CryptoError("Public key is undefined");
|
|
689
|
+
}
|
|
690
|
+
const pubkeyData = pubkey instanceof Uint8Array ? pubkey : new Uint8Array(pubkey);
|
|
691
|
+
const sha = await sha256Sync(pubkeyData);
|
|
692
|
+
const ripemd160Fn = ripemd160Sync;
|
|
693
|
+
const ripe = await ripemd160Fn(sha);
|
|
694
|
+
const fingerprintBytes = Array.from(ripe.subarray(0, 4));
|
|
695
|
+
return fingerprintBytes.map((b) => {
|
|
696
|
+
const hex = b.toString(16);
|
|
697
|
+
return hex.length === 1 ? "0" + hex : hex;
|
|
698
|
+
}).join("");
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// src/utils/bip32-helpers.ts
|
|
702
|
+
function getWifVersion(network) {
|
|
703
|
+
return network === "mainnet" ? 128 : 239;
|
|
704
|
+
}
|
|
705
|
+
function getNetworkVersionsFromConstants(network) {
|
|
706
|
+
const bip32Versions = BIP32_VERSIONS[network];
|
|
707
|
+
return {
|
|
708
|
+
bip32: bip32Versions,
|
|
709
|
+
wif: getWifVersion(network)
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
function normalizeSeedBuffer(seed) {
|
|
713
|
+
if (!seed) {
|
|
714
|
+
throw new CryptoError("Failed to generate seed - seed is undefined");
|
|
715
|
+
}
|
|
716
|
+
let seedBuffer;
|
|
717
|
+
if (seed instanceof Uint8Array) {
|
|
718
|
+
seedBuffer = seed;
|
|
719
|
+
} else if (seed instanceof ArrayBuffer) {
|
|
720
|
+
seedBuffer = new Uint8Array(seed);
|
|
721
|
+
} else if (seed && typeof seed === "object") {
|
|
722
|
+
if ("buffer" in seed && seed.buffer) {
|
|
723
|
+
const bufferValue = seed.buffer;
|
|
724
|
+
if (bufferValue instanceof ArrayBuffer) {
|
|
725
|
+
if (isNode() && seed instanceof Buffer) {
|
|
726
|
+
seedBuffer = seed;
|
|
727
|
+
} else {
|
|
728
|
+
const byteOffset = seed.byteOffset || 0;
|
|
729
|
+
const byteLength = seed.byteLength || seed.length || bufferValue.byteLength;
|
|
730
|
+
seedBuffer = new Uint8Array(bufferValue, byteOffset, byteLength);
|
|
731
|
+
}
|
|
732
|
+
} else {
|
|
733
|
+
try {
|
|
734
|
+
seedBuffer = new Uint8Array(seed);
|
|
735
|
+
} catch (error) {
|
|
736
|
+
throw new CryptoError(`Failed to convert seed to Uint8Array (buffer property invalid): ${error instanceof Error ? error.message : String(error)}`);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
} else {
|
|
740
|
+
try {
|
|
741
|
+
seedBuffer = new Uint8Array(seed);
|
|
742
|
+
} catch (error) {
|
|
743
|
+
throw new CryptoError(`Failed to convert seed to Uint8Array: ${error instanceof Error ? error.message : String(error)}`);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
} else {
|
|
747
|
+
throw new CryptoError(`Invalid seed type: ${typeof seed}`);
|
|
748
|
+
}
|
|
749
|
+
return seedBuffer;
|
|
750
|
+
}
|
|
751
|
+
function toNetworkName(bitcoinNetwork) {
|
|
752
|
+
const n = String(bitcoinNetwork).toLowerCase();
|
|
753
|
+
if (n.includes("main")) return "mainnet";
|
|
754
|
+
if (n.includes("reg")) return "regtest";
|
|
755
|
+
if (n.includes("sig")) return "signet";
|
|
756
|
+
if (n.includes("testnet4")) return "testnet4";
|
|
757
|
+
return "testnet";
|
|
758
|
+
}
|
|
759
|
+
function getNetworkVersions(bitcoinNetwork) {
|
|
760
|
+
const net = toNetworkName(bitcoinNetwork);
|
|
761
|
+
return getNetworkVersionsFromConstants(net);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
// src/crypto/dependencies.ts
|
|
765
|
+
var baseDeps = null;
|
|
766
|
+
var basePromise = null;
|
|
767
|
+
async function loadBaseDependencies() {
|
|
768
|
+
if (isNode()) {
|
|
769
|
+
const nodeModule = "node:module";
|
|
770
|
+
const { createRequire } = await import(nodeModule);
|
|
771
|
+
const requireFromModule = createRequire(import.meta.url);
|
|
772
|
+
const bip392 = requireFromModule("bip39");
|
|
773
|
+
const eccModule2 = requireFromModule("@bitcoinerlab/secp256k1");
|
|
774
|
+
const ecc2 = eccModule2 && typeof eccModule2 === "object" && "default" in eccModule2 ? eccModule2.default : eccModule2;
|
|
775
|
+
const bip322 = requireFromModule("bip32");
|
|
776
|
+
return {
|
|
777
|
+
bip39: bip392,
|
|
778
|
+
ecc: ecc2,
|
|
779
|
+
factory: bip322.BIP32Factory
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
const bip39Module = await import('bip39');
|
|
783
|
+
const bip39 = bip39Module.default || bip39Module;
|
|
784
|
+
const eccModule = await import('@bitcoinerlab/secp256k1');
|
|
785
|
+
const ecc = eccModule.default || eccModule;
|
|
786
|
+
const bip32 = await import('bip32');
|
|
787
|
+
return {
|
|
788
|
+
bip39,
|
|
789
|
+
ecc,
|
|
790
|
+
factory: bip32.BIP32Factory
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
async function ensureBaseDependencies() {
|
|
794
|
+
if (baseDeps) {
|
|
795
|
+
return baseDeps;
|
|
796
|
+
}
|
|
797
|
+
if (!basePromise) {
|
|
798
|
+
basePromise = loadBaseDependencies().then((deps) => {
|
|
799
|
+
baseDeps = deps;
|
|
800
|
+
basePromise = null;
|
|
801
|
+
return deps;
|
|
802
|
+
}).catch((error) => {
|
|
803
|
+
basePromise = null;
|
|
804
|
+
throw error;
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
return basePromise;
|
|
808
|
+
}
|
|
809
|
+
var signerDeps = null;
|
|
810
|
+
var signerPromise = null;
|
|
811
|
+
async function loadSignerDependencies() {
|
|
812
|
+
const base = await ensureBaseDependencies();
|
|
813
|
+
if (isNode()) {
|
|
814
|
+
const bdkNode = await import('@bitcoindevkit/bdk-wallet-node');
|
|
815
|
+
const init2 = bdkNode.default || bdkNode.init || bdkNode;
|
|
816
|
+
const bdk2 = bdkNode;
|
|
817
|
+
const nodeModule = "node:module";
|
|
818
|
+
const { createRequire } = await import(nodeModule);
|
|
819
|
+
const requireFromModule = createRequire(import.meta.url);
|
|
820
|
+
const bitcoinjs = requireFromModule("bitcoinjs-lib");
|
|
821
|
+
const Psbt2 = bitcoinjs.Psbt;
|
|
822
|
+
const payments2 = bitcoinjs.payments;
|
|
823
|
+
const networks2 = bitcoinjs.networks;
|
|
824
|
+
const bip3412 = requireFromModule("bitcoinjs-lib/src/payments/bip341.js");
|
|
825
|
+
const toXOnly2 = bip3412.toXOnly || ((pubkey) => Buffer.from(pubkey.slice(1)));
|
|
826
|
+
return {
|
|
827
|
+
...base,
|
|
828
|
+
Psbt: Psbt2,
|
|
829
|
+
payments: payments2,
|
|
830
|
+
networks: networks2,
|
|
831
|
+
toXOnly: toXOnly2,
|
|
832
|
+
bdk: bdk2,
|
|
833
|
+
init: init2
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
const bdkWeb = await import('@bitcoindevkit/bdk-wallet-web');
|
|
837
|
+
const init = bdkWeb.default || bdkWeb.init || bdkWeb;
|
|
838
|
+
const bdk = bdkWeb;
|
|
839
|
+
const bitcoinModule = await import('bitcoinjs-lib');
|
|
840
|
+
const Psbt = bitcoinModule.Psbt;
|
|
841
|
+
const payments = bitcoinModule.payments;
|
|
842
|
+
const networks = bitcoinModule.networks;
|
|
843
|
+
const bip341 = await import('bitcoinjs-lib/src/payments/bip341.js');
|
|
844
|
+
const toXOnly = bip341.toXOnly || ((pubkey) => Buffer.from(pubkey.slice(1)));
|
|
845
|
+
return {
|
|
846
|
+
...base,
|
|
847
|
+
Psbt,
|
|
848
|
+
payments,
|
|
849
|
+
networks,
|
|
850
|
+
toXOnly,
|
|
851
|
+
bdk,
|
|
852
|
+
init
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
async function ensureSignerDependencies() {
|
|
856
|
+
if (signerDeps) {
|
|
857
|
+
return signerDeps;
|
|
858
|
+
}
|
|
859
|
+
if (!signerPromise) {
|
|
860
|
+
signerPromise = loadSignerDependencies().then((deps) => {
|
|
861
|
+
signerDeps = deps;
|
|
862
|
+
signerPromise = null;
|
|
863
|
+
return deps;
|
|
864
|
+
}).catch((error) => {
|
|
865
|
+
signerPromise = null;
|
|
866
|
+
throw error;
|
|
867
|
+
});
|
|
868
|
+
}
|
|
869
|
+
return signerPromise;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
// src/crypto/keys.ts
|
|
873
|
+
function normalizeSeedInput(seed, field = "seed") {
|
|
874
|
+
if (typeof seed === "string") {
|
|
875
|
+
const trimmed = seed.trim();
|
|
876
|
+
if (!trimmed) {
|
|
877
|
+
throw new ValidationError(`${field} must be a non-empty hex string`, field);
|
|
878
|
+
}
|
|
879
|
+
const hex = trimmed.startsWith("0x") ? trimmed.slice(2) : trimmed;
|
|
880
|
+
if (hex.length % 2 !== 0) {
|
|
881
|
+
throw new ValidationError(`${field} hex string must have even length`, field);
|
|
882
|
+
}
|
|
883
|
+
if (hex.length !== 128) {
|
|
884
|
+
throw new ValidationError(`${field} must be 64 bytes (128 hex characters)`, field);
|
|
885
|
+
}
|
|
886
|
+
if (!/^[0-9a-fA-F]+$/.test(hex)) {
|
|
887
|
+
throw new ValidationError(`${field} must be a valid hex string`, field);
|
|
888
|
+
}
|
|
889
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
890
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
891
|
+
const byte = hex.slice(i * 2, i * 2 + 2);
|
|
892
|
+
bytes[i] = parseInt(byte, 16);
|
|
893
|
+
}
|
|
894
|
+
return bytes;
|
|
895
|
+
}
|
|
896
|
+
if (seed instanceof Uint8Array) {
|
|
897
|
+
if (seed.length === 0) {
|
|
898
|
+
throw new ValidationError(`${field} must not be empty`, field);
|
|
899
|
+
}
|
|
900
|
+
return new Uint8Array(seed);
|
|
901
|
+
}
|
|
902
|
+
throw new ValidationError(`${field} must be a 64-byte hex string or Uint8Array`, field);
|
|
903
|
+
}
|
|
904
|
+
function getCoinType(bitcoinNetwork, rgb) {
|
|
905
|
+
const net = toNetworkName(bitcoinNetwork);
|
|
906
|
+
if (rgb) return net === "mainnet" ? COIN_RGB_MAINNET : COIN_RGB_TESTNET;
|
|
907
|
+
return net === "mainnet" ? 0 : 1;
|
|
908
|
+
}
|
|
909
|
+
function accountDerivationPath(bitcoinNetwork, rgb) {
|
|
910
|
+
const coinType = getCoinType(bitcoinNetwork, rgb);
|
|
911
|
+
return `m/${DERIVATION_PURPOSE}'/${coinType}'/${DERIVATION_ACCOUNT}'`;
|
|
912
|
+
}
|
|
913
|
+
async function masterFingerprintFromNode(node) {
|
|
914
|
+
return calculateMasterFingerprint(node);
|
|
915
|
+
}
|
|
916
|
+
async function mnemonicToRoot(mnemonic, bitcoinNetwork) {
|
|
917
|
+
const { bip39, ecc, factory } = await ensureBaseDependencies();
|
|
918
|
+
if (!bip39 || typeof bip39.mnemonicToSeedSync !== "function") {
|
|
919
|
+
throw new CryptoError("bip39 module not loaded correctly");
|
|
920
|
+
}
|
|
921
|
+
const seedBuffer = normalizeSeedBuffer(bip39.mnemonicToSeedSync(mnemonic));
|
|
922
|
+
const versions = getNetworkVersions(bitcoinNetwork);
|
|
923
|
+
const bip32 = factory(ecc);
|
|
924
|
+
try {
|
|
925
|
+
return bip32.fromSeed(seedBuffer, versions);
|
|
926
|
+
} catch (error) {
|
|
927
|
+
throw new CryptoError(
|
|
928
|
+
`Failed to create BIP32 root node from seed: ${error instanceof Error ? error.message : String(error)}`,
|
|
929
|
+
error
|
|
930
|
+
);
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
async function getAccountXpub(mnemonic, bitcoinNetwork, rgb) {
|
|
934
|
+
const root = await mnemonicToRoot(mnemonic, bitcoinNetwork);
|
|
935
|
+
const path3 = accountDerivationPath(bitcoinNetwork, rgb);
|
|
936
|
+
const acct = root.derivePath(path3);
|
|
937
|
+
return acct.neutered().toBase58();
|
|
938
|
+
}
|
|
939
|
+
async function getMasterXpriv(mnemonic, bitcoinNetwork) {
|
|
940
|
+
const root = await mnemonicToRoot(mnemonic, bitcoinNetwork);
|
|
941
|
+
return root.toBase58();
|
|
942
|
+
}
|
|
943
|
+
function deriveAccountXpubsFromRoot(root, network) {
|
|
944
|
+
const vanillaPath = accountDerivationPath(network, false);
|
|
945
|
+
const coloredPath = accountDerivationPath(network, true);
|
|
946
|
+
return {
|
|
947
|
+
account_xpub_vanilla: root.derivePath(vanillaPath).neutered().toBase58(),
|
|
948
|
+
account_xpub_colored: root.derivePath(coloredPath).neutered().toBase58()
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
async function buildGeneratedKeysFromRoot(root, network, mnemonic) {
|
|
952
|
+
const xpub = root.neutered().toBase58();
|
|
953
|
+
const xpriv = root.toBase58();
|
|
954
|
+
const master_fingerprint = await masterFingerprintFromNode(root);
|
|
955
|
+
const { account_xpub_vanilla, account_xpub_colored } = deriveAccountXpubsFromRoot(root, network);
|
|
956
|
+
return {
|
|
957
|
+
mnemonic,
|
|
958
|
+
xpub,
|
|
959
|
+
accountXpubVanilla: account_xpub_vanilla,
|
|
960
|
+
accountXpubColored: account_xpub_colored,
|
|
961
|
+
masterFingerprint: master_fingerprint,
|
|
962
|
+
xpriv
|
|
963
|
+
};
|
|
964
|
+
}
|
|
965
|
+
async function getXpubFromXprivInternal(xpriv, bitcoinNetwork) {
|
|
966
|
+
const { ecc, factory } = await ensureBaseDependencies();
|
|
967
|
+
try {
|
|
968
|
+
const bip32 = factory(ecc);
|
|
969
|
+
let node;
|
|
970
|
+
if (bitcoinNetwork) {
|
|
971
|
+
const versions = getNetworkVersions(bitcoinNetwork);
|
|
972
|
+
node = bip32.fromBase58(xpriv, versions);
|
|
973
|
+
} else {
|
|
974
|
+
const inferredNetwork = xpriv.startsWith("xprv") ? "mainnet" : "testnet";
|
|
975
|
+
const versions = getNetworkVersions(inferredNetwork);
|
|
976
|
+
node = bip32.fromBase58(xpriv, versions);
|
|
977
|
+
}
|
|
978
|
+
return node.neutered().toBase58();
|
|
979
|
+
} catch (error) {
|
|
980
|
+
throw new CryptoError("Failed to derive xpub from xpriv", error);
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
async function buildKeysOutput(mnemonic, bitcoinNetwork) {
|
|
984
|
+
const normalizedNetwork = normalizeNetwork(bitcoinNetwork);
|
|
985
|
+
const root = await mnemonicToRoot(mnemonic, normalizedNetwork);
|
|
986
|
+
return buildGeneratedKeysFromRoot(root, normalizedNetwork, mnemonic);
|
|
987
|
+
}
|
|
988
|
+
async function buildKeysOutputFromSeed(seed, bitcoinNetwork) {
|
|
989
|
+
const { ecc, factory } = await ensureBaseDependencies();
|
|
990
|
+
const normalizedNetwork = normalizeNetwork(bitcoinNetwork);
|
|
991
|
+
const seedBuffer = normalizeSeedBuffer(seed);
|
|
992
|
+
const versions = getNetworkVersions(bitcoinNetwork);
|
|
993
|
+
const bip32 = factory(ecc);
|
|
994
|
+
let root;
|
|
995
|
+
try {
|
|
996
|
+
root = bip32.fromSeed(seedBuffer, versions);
|
|
997
|
+
} catch (error) {
|
|
998
|
+
throw new CryptoError("Failed to create BIP32 root node from seed", error);
|
|
999
|
+
}
|
|
1000
|
+
return buildGeneratedKeysFromRoot(root, normalizedNetwork, "");
|
|
1001
|
+
}
|
|
1002
|
+
async function buildKeysOutputFromXpriv(xpriv, bitcoinNetwork) {
|
|
1003
|
+
const { ecc, factory } = await ensureBaseDependencies();
|
|
1004
|
+
try {
|
|
1005
|
+
const bip32 = factory(ecc);
|
|
1006
|
+
const normalizedNetwork = normalizeNetwork(bitcoinNetwork);
|
|
1007
|
+
const versions = getNetworkVersions(bitcoinNetwork);
|
|
1008
|
+
const root = bip32.fromBase58(xpriv, versions);
|
|
1009
|
+
return buildGeneratedKeysFromRoot(root, normalizedNetwork, "");
|
|
1010
|
+
} catch (error) {
|
|
1011
|
+
throw new CryptoError("Failed to derive keys from xpriv", error);
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
async function generateKeys3(bitcoinNetwork = "regtest") {
|
|
1015
|
+
try {
|
|
1016
|
+
const { bip39 } = await ensureBaseDependencies();
|
|
1017
|
+
if (!bip39 || typeof bip39.generateMnemonic !== "function") {
|
|
1018
|
+
throw new Error("bip39 not loaded. Dependencies may not have initialized correctly.");
|
|
1019
|
+
}
|
|
1020
|
+
const mnemonic = bip39.generateMnemonic(128);
|
|
1021
|
+
return await buildKeysOutput(mnemonic, bitcoinNetwork);
|
|
1022
|
+
} catch (error) {
|
|
1023
|
+
if (error instanceof Error && error.message.includes("bip39 not loaded")) {
|
|
1024
|
+
throw new CryptoError("Failed to load dependencies", error);
|
|
1025
|
+
}
|
|
1026
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1027
|
+
error instanceof Error ? error.stack : void 0;
|
|
1028
|
+
throw new CryptoError(`Failed to generate mnemonic: ${errorMessage}`, error);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
async function deriveKeysFromMnemonic(bitcoinNetwork = "regtest", mnemonic) {
|
|
1032
|
+
validateMnemonic(mnemonic, "mnemonic");
|
|
1033
|
+
const normalizedNetwork = normalizeNetwork(bitcoinNetwork);
|
|
1034
|
+
try {
|
|
1035
|
+
const { bip39 } = await ensureBaseDependencies();
|
|
1036
|
+
const trimmedMnemonic = mnemonic.trim();
|
|
1037
|
+
if (!bip39 || !bip39.validateMnemonic(trimmedMnemonic)) {
|
|
1038
|
+
throw new ValidationError("Invalid mnemonic format - failed BIP39 validation", "mnemonic");
|
|
1039
|
+
}
|
|
1040
|
+
return await buildKeysOutput(trimmedMnemonic, normalizedNetwork);
|
|
1041
|
+
} catch (error) {
|
|
1042
|
+
if (error instanceof ValidationError) {
|
|
1043
|
+
throw error;
|
|
1044
|
+
}
|
|
1045
|
+
throw new CryptoError("Failed to derive keys from mnemonic", error);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
async function deriveKeysFromSeed(bitcoinNetwork = "regtest", seed) {
|
|
1049
|
+
const normalizedNetwork = normalizeNetwork(bitcoinNetwork);
|
|
1050
|
+
try {
|
|
1051
|
+
const normalizedSeed = normalizeSeedInput(seed, "seed");
|
|
1052
|
+
return await buildKeysOutputFromSeed(normalizedSeed, normalizedNetwork);
|
|
1053
|
+
} catch (error) {
|
|
1054
|
+
if (error instanceof ValidationError) {
|
|
1055
|
+
throw error;
|
|
1056
|
+
}
|
|
1057
|
+
throw new CryptoError("Failed to derive keys from seed", error);
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
async function restoreKeys(bitcoinNetwork = "regtest", mnemonic) {
|
|
1061
|
+
return deriveKeysFromMnemonic(bitcoinNetwork, mnemonic);
|
|
1062
|
+
}
|
|
1063
|
+
async function getXprivFromMnemonic(bitcoinNetwork = "regtest", mnemonic) {
|
|
1064
|
+
validateMnemonic(mnemonic, "mnemonic");
|
|
1065
|
+
const normalizedNetwork = normalizeNetwork(bitcoinNetwork);
|
|
1066
|
+
try {
|
|
1067
|
+
return await getMasterXpriv(mnemonic, normalizedNetwork);
|
|
1068
|
+
} catch (error) {
|
|
1069
|
+
if (error instanceof ValidationError) {
|
|
1070
|
+
throw error;
|
|
1071
|
+
}
|
|
1072
|
+
throw new CryptoError("Failed to derive xpriv from mnemonic", error);
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
async function getXpubFromXpriv(xpriv, bitcoinNetwork) {
|
|
1076
|
+
if (!xpriv || typeof xpriv !== "string") {
|
|
1077
|
+
throw new ValidationError("xpriv must be a non-empty string", "xpriv");
|
|
1078
|
+
}
|
|
1079
|
+
try {
|
|
1080
|
+
return await getXpubFromXprivInternal(xpriv, bitcoinNetwork);
|
|
1081
|
+
} catch (error) {
|
|
1082
|
+
if (error instanceof ValidationError) {
|
|
1083
|
+
throw error;
|
|
1084
|
+
}
|
|
1085
|
+
throw new CryptoError("Failed to derive xpub from xpriv", error);
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
async function deriveKeysFromXpriv(bitcoinNetwork = "regtest", xpriv) {
|
|
1089
|
+
if (!xpriv || typeof xpriv !== "string") {
|
|
1090
|
+
throw new ValidationError("xpriv must be a non-empty string", "xpriv");
|
|
1091
|
+
}
|
|
1092
|
+
const normalizedNetwork = normalizeNetwork(bitcoinNetwork);
|
|
1093
|
+
try {
|
|
1094
|
+
return await buildKeysOutputFromXpriv(xpriv, normalizedNetwork);
|
|
1095
|
+
} catch (error) {
|
|
1096
|
+
if (error instanceof ValidationError) {
|
|
1097
|
+
throw error;
|
|
1098
|
+
}
|
|
1099
|
+
throw new CryptoError("Failed to derive keys from xpriv", error);
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
async function accountXpubsFromMnemonic(bitcoinNetwork = "regtest", mnemonic) {
|
|
1103
|
+
validateMnemonic(mnemonic, "mnemonic");
|
|
1104
|
+
try {
|
|
1105
|
+
const { bip39 } = await ensureBaseDependencies();
|
|
1106
|
+
if (!bip39 || !bip39.validateMnemonic(mnemonic)) {
|
|
1107
|
+
throw new ValidationError("Invalid mnemonic format - failed BIP39 validation", "mnemonic");
|
|
1108
|
+
}
|
|
1109
|
+
return {
|
|
1110
|
+
account_xpub_vanilla: await getAccountXpub(mnemonic, bitcoinNetwork, false),
|
|
1111
|
+
account_xpub_colored: await getAccountXpub(mnemonic, bitcoinNetwork, true)
|
|
1112
|
+
};
|
|
1113
|
+
} catch (error) {
|
|
1114
|
+
if (error instanceof ValidationError) {
|
|
1115
|
+
throw error;
|
|
1116
|
+
}
|
|
1117
|
+
throw new CryptoError("Failed to derive account xpubs from mnemonic", error);
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
// src/crypto/signer.ts
|
|
1122
|
+
function normalizePath(path3) {
|
|
1123
|
+
if (typeof path3 === "string") {
|
|
1124
|
+
if (path3.startsWith("m/m/")) {
|
|
1125
|
+
return path3.replace(/^m\/m\//, "m/");
|
|
1126
|
+
}
|
|
1127
|
+
return path3;
|
|
1128
|
+
} else if (Array.isArray(path3)) {
|
|
1129
|
+
if (path3.length > 0 && path3[0] === 0 && path3.length > 1) {
|
|
1130
|
+
const second = path3[1];
|
|
1131
|
+
if (typeof second === "number" && second >= 2147483648) {
|
|
1132
|
+
return path3.slice(1);
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
return path3;
|
|
1136
|
+
}
|
|
1137
|
+
return path3;
|
|
1138
|
+
}
|
|
1139
|
+
function pathToString(path3) {
|
|
1140
|
+
if (typeof path3 === "string") {
|
|
1141
|
+
return path3;
|
|
1142
|
+
} else if (Array.isArray(path3)) {
|
|
1143
|
+
return path3.map((p) => {
|
|
1144
|
+
if (typeof p === "number") {
|
|
1145
|
+
return p >= 2147483648 ? `${p & 2147483647}'` : `${p}`;
|
|
1146
|
+
}
|
|
1147
|
+
return String(p);
|
|
1148
|
+
}).join("/");
|
|
1149
|
+
}
|
|
1150
|
+
return "";
|
|
1151
|
+
}
|
|
1152
|
+
function preprocessPsbtForBDK(psbtBase64, rootNode, fp, network, deps) {
|
|
1153
|
+
const { Psbt, networks, payments, toXOnly } = deps;
|
|
1154
|
+
if (!Psbt || !networks || !payments || !toXOnly) {
|
|
1155
|
+
throw new CryptoError("BitcoinJS modules not loaded");
|
|
1156
|
+
}
|
|
1157
|
+
const psbt = Psbt.fromBase64(psbtBase64.trim());
|
|
1158
|
+
const bjsNet = network === "mainnet" ? networks.bitcoin : networks.testnet;
|
|
1159
|
+
for (let i = 0; i < psbt.inputCount; i++) {
|
|
1160
|
+
const input = psbt.data.inputs[i];
|
|
1161
|
+
if (input.tapBip32Derivation && input.tapBip32Derivation.length > 0) {
|
|
1162
|
+
input.tapBip32Derivation.forEach((deriv) => {
|
|
1163
|
+
const normalizedPath = normalizePath(deriv.path);
|
|
1164
|
+
deriv.path = pathToString(normalizedPath);
|
|
1165
|
+
let pathStr = pathToString(normalizedPath);
|
|
1166
|
+
if (!pathStr.startsWith("m/")) {
|
|
1167
|
+
pathStr = "m/" + pathStr;
|
|
1168
|
+
}
|
|
1169
|
+
try {
|
|
1170
|
+
const derivedNode = rootNode.derivePath(pathStr);
|
|
1171
|
+
const pubkey = derivedNode.publicKey;
|
|
1172
|
+
if (!pubkey) {
|
|
1173
|
+
return;
|
|
1174
|
+
}
|
|
1175
|
+
const pubkeyBuffer = pubkey instanceof Buffer ? pubkey : Buffer.from(pubkey);
|
|
1176
|
+
const xOnly = toXOnly(pubkeyBuffer);
|
|
1177
|
+
const p2tr = payments.p2tr({ internalPubkey: xOnly, network: bjsNet });
|
|
1178
|
+
const expectedScript = p2tr.output;
|
|
1179
|
+
if (!expectedScript) {
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1182
|
+
if (input.witnessUtxo && expectedScript) {
|
|
1183
|
+
const currentScript = input.witnessUtxo.script;
|
|
1184
|
+
if (!currentScript.equals(expectedScript)) {
|
|
1185
|
+
input.witnessUtxo.script = expectedScript;
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
if (xOnly) {
|
|
1189
|
+
if (!input.tapInternalKey || !input.tapInternalKey.equals(xOnly)) {
|
|
1190
|
+
input.tapInternalKey = xOnly;
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
const fingerprintBuf = Buffer.from(fp, "hex");
|
|
1194
|
+
if (!deriv.masterFingerprint) {
|
|
1195
|
+
deriv.masterFingerprint = fingerprintBuf;
|
|
1196
|
+
} else {
|
|
1197
|
+
const currentFp = Buffer.from(deriv.masterFingerprint);
|
|
1198
|
+
if (!currentFp.equals(fingerprintBuf)) {
|
|
1199
|
+
deriv.masterFingerprint = fingerprintBuf;
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
if (!deriv.pubkey || !deriv.pubkey.equals(xOnly)) {
|
|
1203
|
+
deriv.pubkey = xOnly;
|
|
1204
|
+
}
|
|
1205
|
+
} catch (e) {
|
|
1206
|
+
}
|
|
1207
|
+
});
|
|
1208
|
+
}
|
|
1209
|
+
if (input.bip32Derivation && input.bip32Derivation.length > 0) {
|
|
1210
|
+
input.bip32Derivation.forEach((deriv) => {
|
|
1211
|
+
const normalizedPath = normalizePath(deriv.path);
|
|
1212
|
+
deriv.path = pathToString(normalizedPath);
|
|
1213
|
+
});
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
return psbt.toBase64();
|
|
1217
|
+
}
|
|
1218
|
+
function detectPsbtType(psbtBase64, deps) {
|
|
1219
|
+
const { Psbt } = deps;
|
|
1220
|
+
if (!Psbt) {
|
|
1221
|
+
throw new CryptoError("BitcoinJS Psbt module not loaded");
|
|
1222
|
+
}
|
|
1223
|
+
try {
|
|
1224
|
+
const psbt = Psbt.fromBase64(psbtBase64.trim());
|
|
1225
|
+
for (let i = 0; i < psbt.inputCount; i++) {
|
|
1226
|
+
const input = psbt.data.inputs[i];
|
|
1227
|
+
if (input.tapBip32Derivation && input.tapBip32Derivation.length > 0) {
|
|
1228
|
+
for (const deriv of input.tapBip32Derivation) {
|
|
1229
|
+
const pathStr = pathToString(deriv.path);
|
|
1230
|
+
if (pathStr.includes("827167'") || pathStr.includes("827166'")) {
|
|
1231
|
+
return "send";
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
return "create_utxo";
|
|
1237
|
+
} catch (e) {
|
|
1238
|
+
return "create_utxo";
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
function deriveDescriptors(rootNode, fp, network, psbtType) {
|
|
1242
|
+
const isMainnet = network === "mainnet";
|
|
1243
|
+
const coinTypeBtc = isMainnet ? COIN_BITCOIN_MAINNET : COIN_BITCOIN_TESTNET;
|
|
1244
|
+
const coinTypeRgb = isMainnet ? COIN_RGB_MAINNET : COIN_RGB_TESTNET;
|
|
1245
|
+
if (psbtType === "create_utxo") {
|
|
1246
|
+
const accountPath = `m/${DERIVATION_PURPOSE}'/${coinTypeBtc}'/${DERIVATION_ACCOUNT}'`;
|
|
1247
|
+
const accountNode = rootNode.derivePath(accountPath);
|
|
1248
|
+
const accountXprv = accountNode.toBase58();
|
|
1249
|
+
const origin = `[${fp}/${DERIVATION_PURPOSE}'/${coinTypeBtc}'/${DERIVATION_ACCOUNT}']`;
|
|
1250
|
+
return {
|
|
1251
|
+
external: `tr(${origin}${accountXprv}/0/*)`,
|
|
1252
|
+
internal: `tr(${origin}${accountXprv}/1/*)`
|
|
1253
|
+
};
|
|
1254
|
+
} else {
|
|
1255
|
+
const rgbAccountPath = `m/${DERIVATION_PURPOSE}'/${coinTypeRgb}'/${DERIVATION_ACCOUNT}'`;
|
|
1256
|
+
const rgbAccountNode = rootNode.derivePath(rgbAccountPath);
|
|
1257
|
+
const rgbKeychainNode = rgbAccountNode.derive(KEYCHAIN_RGB);
|
|
1258
|
+
const rgbKeychainXprv = rgbKeychainNode.toBase58();
|
|
1259
|
+
const rgbOrigin = `[${fp}/${DERIVATION_PURPOSE}'/${coinTypeRgb}'/${DERIVATION_ACCOUNT}'/${KEYCHAIN_RGB}]`;
|
|
1260
|
+
const btcAccountPath = `m/${DERIVATION_PURPOSE}'/${coinTypeBtc}'/${DERIVATION_ACCOUNT}'`;
|
|
1261
|
+
const btcAccountNode = rootNode.derivePath(btcAccountPath);
|
|
1262
|
+
const btcKeychainNode = btcAccountNode.derive(KEYCHAIN_BTC);
|
|
1263
|
+
const btcKeychainXprv = btcKeychainNode.toBase58();
|
|
1264
|
+
const btcOrigin = `[${fp}/${DERIVATION_PURPOSE}'/${coinTypeBtc}'/${DERIVATION_ACCOUNT}'/${KEYCHAIN_BTC}]`;
|
|
1265
|
+
return {
|
|
1266
|
+
external: `tr(${rgbOrigin}${rgbKeychainXprv}/*)`,
|
|
1267
|
+
internal: `tr(${btcOrigin}${btcKeychainXprv}/*)`
|
|
1268
|
+
};
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
function getNetworkVersions2(network) {
|
|
1272
|
+
return getNetworkVersions(network);
|
|
1273
|
+
}
|
|
1274
|
+
async function getMasterFingerprint(rootNode) {
|
|
1275
|
+
return calculateMasterFingerprint(rootNode);
|
|
1276
|
+
}
|
|
1277
|
+
async function signPsbtFromSeedInternal(seed, psbtBase64, network, options = {}, deps) {
|
|
1278
|
+
validatePsbt(psbtBase64, "psbtBase64");
|
|
1279
|
+
const { ecc, factory, bdk } = deps;
|
|
1280
|
+
const bip32 = factory(ecc);
|
|
1281
|
+
const seedBuffer = normalizeSeedBuffer(seed);
|
|
1282
|
+
const versions = getNetworkVersions2(network);
|
|
1283
|
+
let rootNode;
|
|
1284
|
+
try {
|
|
1285
|
+
rootNode = bip32.fromSeed(seedBuffer, versions);
|
|
1286
|
+
} catch (error) {
|
|
1287
|
+
throw new CryptoError("Failed to derive root node from seed", error);
|
|
1288
|
+
}
|
|
1289
|
+
const fp = await getMasterFingerprint(rootNode);
|
|
1290
|
+
const psbtType = detectPsbtType(psbtBase64, deps);
|
|
1291
|
+
const needsPreprocessing = psbtType === "send";
|
|
1292
|
+
const { external, internal } = deriveDescriptors(rootNode, fp, network, psbtType);
|
|
1293
|
+
let wallet2;
|
|
1294
|
+
try {
|
|
1295
|
+
wallet2 = bdk.Wallet.create(network, external, internal);
|
|
1296
|
+
} catch (error) {
|
|
1297
|
+
throw new CryptoError("Failed to create BDK wallet", error);
|
|
1298
|
+
}
|
|
1299
|
+
let processedPsbt = psbtBase64.trim();
|
|
1300
|
+
if (needsPreprocessing || options.preprocess) {
|
|
1301
|
+
try {
|
|
1302
|
+
processedPsbt = preprocessPsbtForBDK(psbtBase64, rootNode, fp, network, deps);
|
|
1303
|
+
} catch (error) {
|
|
1304
|
+
throw new CryptoError("Failed to preprocess PSBT", error);
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
let pstb;
|
|
1308
|
+
try {
|
|
1309
|
+
pstb = bdk.Psbt.from_string(processedPsbt);
|
|
1310
|
+
} catch (error) {
|
|
1311
|
+
throw new CryptoError("Failed to parse PSBT", error);
|
|
1312
|
+
}
|
|
1313
|
+
const signOptions = options.signOptions || new bdk.SignOptions();
|
|
1314
|
+
try {
|
|
1315
|
+
wallet2.sign(pstb, signOptions);
|
|
1316
|
+
} catch (error) {
|
|
1317
|
+
throw new CryptoError("Failed to sign PSBT", error);
|
|
1318
|
+
}
|
|
1319
|
+
return pstb.toString().trim();
|
|
1320
|
+
}
|
|
1321
|
+
async function signPsbt(mnemonic, psbtBase64, network = "testnet", options = {}) {
|
|
1322
|
+
try {
|
|
1323
|
+
validateMnemonic(mnemonic, "mnemonic");
|
|
1324
|
+
const { bip39 } = await ensureBaseDependencies();
|
|
1325
|
+
if (!bip39 || typeof bip39.mnemonicToSeedSync !== "function") {
|
|
1326
|
+
throw new CryptoError("bip39 module not loaded correctly");
|
|
1327
|
+
}
|
|
1328
|
+
let seed;
|
|
1329
|
+
try {
|
|
1330
|
+
seed = bip39.mnemonicToSeedSync(mnemonic);
|
|
1331
|
+
} catch (error) {
|
|
1332
|
+
throw new ValidationError("Invalid mnemonic format", "mnemonic");
|
|
1333
|
+
}
|
|
1334
|
+
const normalizedNetwork = normalizeNetwork(network);
|
|
1335
|
+
const deps = await ensureSignerDependencies();
|
|
1336
|
+
return await signPsbtFromSeedInternal(seed, psbtBase64, normalizedNetwork, options, deps);
|
|
1337
|
+
} catch (error) {
|
|
1338
|
+
if (error instanceof ValidationError || error instanceof CryptoError) {
|
|
1339
|
+
throw error;
|
|
1340
|
+
}
|
|
1341
|
+
throw new CryptoError("Unexpected error during PSBT signing", error);
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
async function signPsbtSync(mnemonic, psbtBase64, network = "testnet", options = {}) {
|
|
1345
|
+
return signPsbt(mnemonic, psbtBase64, network, options);
|
|
1346
|
+
}
|
|
1347
|
+
async function signPsbtFromSeed(seed, psbtBase64, network = "testnet", options = {}) {
|
|
1348
|
+
const normalizedSeed = normalizeSeedInput(seed);
|
|
1349
|
+
const normalizedNetwork = normalizeNetwork(network);
|
|
1350
|
+
const deps = await ensureSignerDependencies();
|
|
1351
|
+
return signPsbtFromSeedInternal(normalizedSeed, psbtBase64, normalizedNetwork, options, deps);
|
|
1352
|
+
}
|
|
1353
|
+
function ensureMessageInput(message) {
|
|
1354
|
+
if (typeof message === "string") {
|
|
1355
|
+
if (!message.length) {
|
|
1356
|
+
throw new ValidationError("message must not be empty", "message");
|
|
1357
|
+
}
|
|
1358
|
+
return Buffer.from(message, "utf8");
|
|
1359
|
+
}
|
|
1360
|
+
if (message instanceof Uint8Array) {
|
|
1361
|
+
if (!message.length) {
|
|
1362
|
+
throw new ValidationError("message must not be empty", "message");
|
|
1363
|
+
}
|
|
1364
|
+
return Buffer.from(message);
|
|
1365
|
+
}
|
|
1366
|
+
throw new ValidationError("message must be a string or Uint8Array", "message");
|
|
1367
|
+
}
|
|
1368
|
+
async function deriveRootFromSeedInput(seed, network) {
|
|
1369
|
+
const { ecc, factory } = await ensureBaseDependencies();
|
|
1370
|
+
const normalizedSeed = normalizeSeedInput(seed, "seed");
|
|
1371
|
+
const versions = getNetworkVersions2(network);
|
|
1372
|
+
const bip32 = factory(ecc);
|
|
1373
|
+
try {
|
|
1374
|
+
return bip32.fromSeed(normalizedSeed, versions);
|
|
1375
|
+
} catch (error) {
|
|
1376
|
+
throw new CryptoError("Failed to create BIP32 root node from seed", error);
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
var DEFAULT_RELATIVE_PATH = "0/0";
|
|
1380
|
+
async function signMessage(params) {
|
|
1381
|
+
const { message, seed } = params;
|
|
1382
|
+
if (!seed) {
|
|
1383
|
+
throw new ValidationError("seed is required", "seed");
|
|
1384
|
+
}
|
|
1385
|
+
const normalizedNetwork = normalizeNetwork(params.network ?? "regtest");
|
|
1386
|
+
const relativePath = DEFAULT_RELATIVE_PATH;
|
|
1387
|
+
const accountPath = accountDerivationPath(normalizedNetwork, false);
|
|
1388
|
+
const messageBytes = ensureMessageInput(message);
|
|
1389
|
+
const { ecc } = await ensureBaseDependencies();
|
|
1390
|
+
const root = await deriveRootFromSeedInput(seed, normalizedNetwork);
|
|
1391
|
+
const accountNode = root.derivePath(accountPath);
|
|
1392
|
+
const child = accountNode.derivePath(relativePath);
|
|
1393
|
+
const privateKey = child.privateKey;
|
|
1394
|
+
if (!privateKey) {
|
|
1395
|
+
throw new CryptoError("Derived node does not contain a private key");
|
|
1396
|
+
}
|
|
1397
|
+
if (!ecc || typeof ecc.signSchnorr !== "function") {
|
|
1398
|
+
throw new CryptoError("Schnorr signing not supported by ECC module");
|
|
1399
|
+
}
|
|
1400
|
+
const messageHash = await sha256(messageBytes);
|
|
1401
|
+
const signature = Buffer.from(ecc.signSchnorr(messageHash, privateKey)).toString("base64");
|
|
1402
|
+
return signature;
|
|
1403
|
+
}
|
|
1404
|
+
async function verifyMessage(params) {
|
|
1405
|
+
const { message, signature, accountXpub } = params;
|
|
1406
|
+
const messageBytes = ensureMessageInput(message);
|
|
1407
|
+
const relativePath = DEFAULT_RELATIVE_PATH;
|
|
1408
|
+
const signatureBytes = Buffer.from(signature, "base64");
|
|
1409
|
+
const normalizedNetwork = normalizeNetwork(params.network ?? "regtest");
|
|
1410
|
+
const versions = getNetworkVersions2(normalizedNetwork);
|
|
1411
|
+
const { ecc, factory } = await ensureBaseDependencies();
|
|
1412
|
+
if (!ecc || typeof ecc.verifySchnorr !== "function" || typeof ecc.xOnlyPointFromPoint !== "function") {
|
|
1413
|
+
throw new CryptoError("Schnorr verification not supported by ECC module");
|
|
1414
|
+
}
|
|
1415
|
+
let accountNode;
|
|
1416
|
+
try {
|
|
1417
|
+
accountNode = factory(ecc).fromBase58(accountXpub, versions);
|
|
1418
|
+
} catch (error) {
|
|
1419
|
+
throw new ValidationError("Invalid account xpub provided", "accountXpub");
|
|
1420
|
+
}
|
|
1421
|
+
const child = accountNode.derivePath(relativePath);
|
|
1422
|
+
const pubkeyBuffer = child.publicKey instanceof Buffer ? child.publicKey : Buffer.from(child.publicKey);
|
|
1423
|
+
const xOnlyPubkey = ecc.xOnlyPointFromPoint(pubkeyBuffer);
|
|
1424
|
+
const messageHash = await sha256(messageBytes);
|
|
1425
|
+
try {
|
|
1426
|
+
return ecc.verifySchnorr(messageHash, xOnlyPubkey, signatureBytes);
|
|
1427
|
+
} catch {
|
|
1428
|
+
return false;
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
async function estimatePsbt(psbtBase64) {
|
|
1432
|
+
if (!psbtBase64) {
|
|
1433
|
+
throw new ValidationError("psbt is required", "psbt");
|
|
1434
|
+
}
|
|
1435
|
+
const { Psbt } = await ensureSignerDependencies();
|
|
1436
|
+
if (!Psbt) {
|
|
1437
|
+
throw new CryptoError("BitcoinJS Psbt module not loaded");
|
|
1438
|
+
}
|
|
1439
|
+
let psbt;
|
|
1440
|
+
try {
|
|
1441
|
+
psbt = Psbt.fromBase64(psbtBase64.trim());
|
|
1442
|
+
return {
|
|
1443
|
+
fee: psbt.getFee(),
|
|
1444
|
+
feeRate: psbt.getFeeRate(),
|
|
1445
|
+
vbytes: psbt.extractTransaction().virtualSize()
|
|
1446
|
+
};
|
|
1447
|
+
} catch (error) {
|
|
1448
|
+
console.log("error", error);
|
|
1449
|
+
throw new ValidationError("Invalid PSBT provided", "psbt");
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
var restoreFromBackup = (params) => {
|
|
1453
|
+
const {
|
|
1454
|
+
backupFilePath,
|
|
1455
|
+
password,
|
|
1456
|
+
dataDir
|
|
1457
|
+
} = params;
|
|
1458
|
+
if (!backupFilePath) {
|
|
1459
|
+
throw new ValidationError("backup file is required", "backup");
|
|
1460
|
+
}
|
|
1461
|
+
if (!password) {
|
|
1462
|
+
throw new ValidationError("password is required", "password");
|
|
1463
|
+
}
|
|
1464
|
+
if (!dataDir) {
|
|
1465
|
+
throw new ValidationError("restore directory is required", "restoreDir");
|
|
1466
|
+
}
|
|
1467
|
+
return restoreWallet({
|
|
1468
|
+
backupFilePath,
|
|
1469
|
+
password,
|
|
1470
|
+
dataDir
|
|
1471
|
+
});
|
|
1472
|
+
};
|
|
1473
|
+
var createWallet = async (network = "regtest") => {
|
|
1474
|
+
return await generateKeys3(network);
|
|
1475
|
+
};
|
|
1476
|
+
var WalletManager = class {
|
|
1477
|
+
constructor(params) {
|
|
1478
|
+
this.disposed = false;
|
|
1479
|
+
if (!params.xpubVan) {
|
|
1480
|
+
throw new ValidationError("xpubVan is required", "xpubVan");
|
|
1481
|
+
}
|
|
1482
|
+
if (!params.xpubCol) {
|
|
1483
|
+
throw new ValidationError("xpubCol is required", "xpubCol");
|
|
1484
|
+
}
|
|
1485
|
+
if (!params.masterFingerprint) {
|
|
1486
|
+
throw new ValidationError("masterFingerprint is required", "masterFingerprint");
|
|
1487
|
+
}
|
|
1488
|
+
this.network = normalizeNetwork(params.network ?? "regtest");
|
|
1489
|
+
this.xpubVan = params.xpubVan;
|
|
1490
|
+
this.xpubCol = params.xpubCol;
|
|
1491
|
+
this.seed = params.seed ?? null;
|
|
1492
|
+
this.mnemonic = params.mnemonic ?? null;
|
|
1493
|
+
this.xpub = params.xpub ?? null;
|
|
1494
|
+
this.masterFingerprint = params.masterFingerprint;
|
|
1495
|
+
this.dataDir = params.dataDir ?? path__default.join(os.tmpdir(), "rgb-wallet", this.masterFingerprint);
|
|
1496
|
+
this.client = new RGBLibClient({
|
|
1497
|
+
xpubVan: params.xpubVan,
|
|
1498
|
+
xpubCol: params.xpubCol,
|
|
1499
|
+
masterFingerprint: params.masterFingerprint,
|
|
1500
|
+
network: this.network,
|
|
1501
|
+
transportEndpoint: params.transportEndpoint,
|
|
1502
|
+
indexerUrl: params.indexerUrl,
|
|
1503
|
+
dataDir: params.dataDir ?? this.dataDir
|
|
1504
|
+
});
|
|
1505
|
+
}
|
|
1506
|
+
/**
|
|
1507
|
+
* Get wallet's extended public keys
|
|
1508
|
+
*/
|
|
1509
|
+
getXpub() {
|
|
1510
|
+
return {
|
|
1511
|
+
xpubVan: this.xpubVan,
|
|
1512
|
+
xpubCol: this.xpubCol
|
|
1513
|
+
};
|
|
1514
|
+
}
|
|
1515
|
+
/**
|
|
1516
|
+
* Get wallet's network
|
|
1517
|
+
*/
|
|
1518
|
+
getNetwork() {
|
|
1519
|
+
return this.network;
|
|
1520
|
+
}
|
|
1521
|
+
/**
|
|
1522
|
+
* Dispose of sensitive wallet data
|
|
1523
|
+
* Clears mnemonic and seed from memory
|
|
1524
|
+
* Idempotent - safe to call multiple times
|
|
1525
|
+
*/
|
|
1526
|
+
dispose() {
|
|
1527
|
+
if (this.disposed) {
|
|
1528
|
+
return;
|
|
1529
|
+
}
|
|
1530
|
+
if (this.mnemonic !== null) {
|
|
1531
|
+
this.mnemonic = null;
|
|
1532
|
+
}
|
|
1533
|
+
if (this.seed !== null && this.seed.length > 0) {
|
|
1534
|
+
this.seed.fill(0);
|
|
1535
|
+
this.seed = null;
|
|
1536
|
+
}
|
|
1537
|
+
this.client.dropWallet();
|
|
1538
|
+
this.disposed = true;
|
|
1539
|
+
}
|
|
1540
|
+
/**
|
|
1541
|
+
* Check if wallet has been disposed
|
|
1542
|
+
*/
|
|
1543
|
+
isDisposed() {
|
|
1544
|
+
return this.disposed;
|
|
1545
|
+
}
|
|
1546
|
+
/**
|
|
1547
|
+
* Guard method to ensure wallet has not been disposed
|
|
1548
|
+
* @throws {WalletError} if wallet has been disposed
|
|
1549
|
+
*/
|
|
1550
|
+
ensureNotDisposed() {
|
|
1551
|
+
if (this.disposed) {
|
|
1552
|
+
throw new WalletError("Wallet has been disposed");
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
registerWallet() {
|
|
1556
|
+
return this.client.registerWallet();
|
|
1557
|
+
}
|
|
1558
|
+
getBtcBalance() {
|
|
1559
|
+
return this.client.getBtcBalance();
|
|
1560
|
+
}
|
|
1561
|
+
getAddress() {
|
|
1562
|
+
return this.client.getAddress();
|
|
1563
|
+
}
|
|
1564
|
+
listUnspents() {
|
|
1565
|
+
return this.client.listUnspents();
|
|
1566
|
+
}
|
|
1567
|
+
listAssets() {
|
|
1568
|
+
return this.client.listAssets();
|
|
1569
|
+
}
|
|
1570
|
+
getAssetBalance(asset_id) {
|
|
1571
|
+
return this.client.getAssetBalance(asset_id);
|
|
1572
|
+
}
|
|
1573
|
+
createUtxosBegin(params) {
|
|
1574
|
+
return this.client.createUtxosBegin(params);
|
|
1575
|
+
}
|
|
1576
|
+
createUtxosEnd(params) {
|
|
1577
|
+
return this.client.createUtxosEnd(params);
|
|
1578
|
+
}
|
|
1579
|
+
sendBegin(params) {
|
|
1580
|
+
return this.client.sendBegin(params);
|
|
1581
|
+
}
|
|
1582
|
+
sendEnd(params) {
|
|
1583
|
+
return this.client.sendEnd(params);
|
|
1584
|
+
}
|
|
1585
|
+
sendBtcBegin(params) {
|
|
1586
|
+
return this.client.sendBtcBegin(params);
|
|
1587
|
+
}
|
|
1588
|
+
sendBtcEnd(params) {
|
|
1589
|
+
return this.client.sendBtcEnd(params);
|
|
1590
|
+
}
|
|
1591
|
+
estimateFeeRate(blocks) {
|
|
1592
|
+
if (!Number.isFinite(blocks)) {
|
|
1593
|
+
throw new ValidationError("blocks must be a finite number", "blocks");
|
|
1594
|
+
}
|
|
1595
|
+
if (!Number.isInteger(blocks) || blocks <= 0) {
|
|
1596
|
+
throw new ValidationError("blocks must be a positive integer", "blocks");
|
|
1597
|
+
}
|
|
1598
|
+
return this.client.getFeeEstimation({ blocks });
|
|
1599
|
+
}
|
|
1600
|
+
async estimateFee(psbtBase64) {
|
|
1601
|
+
return await estimatePsbt(psbtBase64);
|
|
1602
|
+
}
|
|
1603
|
+
async sendBtc(params) {
|
|
1604
|
+
this.ensureNotDisposed();
|
|
1605
|
+
const psbt = this.sendBtcBegin(params);
|
|
1606
|
+
const signed = await this.signPsbt(psbt);
|
|
1607
|
+
return this.sendBtcEnd({ signedPsbt: signed });
|
|
1608
|
+
}
|
|
1609
|
+
blindReceive(params) {
|
|
1610
|
+
return this.client.blindReceive(params);
|
|
1611
|
+
}
|
|
1612
|
+
witnessReceive(params) {
|
|
1613
|
+
return this.client.witnessReceive(params);
|
|
1614
|
+
}
|
|
1615
|
+
issueAssetNia(params) {
|
|
1616
|
+
return this.client.issueAssetNia(params);
|
|
1617
|
+
}
|
|
1618
|
+
issueAssetIfa(params) {
|
|
1619
|
+
return this.client.issueAssetIfa(params);
|
|
1620
|
+
}
|
|
1621
|
+
inflateBegin(params) {
|
|
1622
|
+
return this.client.inflateBegin(params);
|
|
1623
|
+
}
|
|
1624
|
+
inflateEnd(params) {
|
|
1625
|
+
return this.client.inflateEnd(params);
|
|
1626
|
+
}
|
|
1627
|
+
/**
|
|
1628
|
+
* Complete inflate operation: begin → sign → end
|
|
1629
|
+
* @param params - Inflate parameters
|
|
1630
|
+
* @param mnemonic - Optional mnemonic for signing
|
|
1631
|
+
*/
|
|
1632
|
+
async inflate(params, mnemonic) {
|
|
1633
|
+
this.ensureNotDisposed();
|
|
1634
|
+
const psbt = await this.inflateBegin(params);
|
|
1635
|
+
const signedPsbt = await this.signPsbt(psbt, mnemonic);
|
|
1636
|
+
return await this.inflateEnd({
|
|
1637
|
+
signedPsbt
|
|
1638
|
+
});
|
|
1639
|
+
}
|
|
1640
|
+
refreshWallet() {
|
|
1641
|
+
this.client.refreshWallet();
|
|
1642
|
+
}
|
|
1643
|
+
listTransactions() {
|
|
1644
|
+
return this.client.listTransactions();
|
|
1645
|
+
}
|
|
1646
|
+
listTransfers(asset_id) {
|
|
1647
|
+
return this.client.listTransfers(asset_id);
|
|
1648
|
+
}
|
|
1649
|
+
failTransfers(params) {
|
|
1650
|
+
return this.client.failTransfers(params);
|
|
1651
|
+
}
|
|
1652
|
+
decodeRGBInvoice(params) {
|
|
1653
|
+
return this.client.decodeRGBInvoice(params);
|
|
1654
|
+
}
|
|
1655
|
+
createBackup(params) {
|
|
1656
|
+
return this.client.createBackup(params);
|
|
1657
|
+
}
|
|
1658
|
+
/**
|
|
1659
|
+
* Sign a PSBT using the wallet's mnemonic or a provided mnemonic
|
|
1660
|
+
* @param psbt - Base64 encoded PSBT
|
|
1661
|
+
* @param mnemonic - Optional mnemonic (uses wallet's mnemonic if not provided)
|
|
1662
|
+
*/
|
|
1663
|
+
async signPsbt(psbt, mnemonic) {
|
|
1664
|
+
this.ensureNotDisposed();
|
|
1665
|
+
const mnemonicToUse = mnemonic ?? this.mnemonic;
|
|
1666
|
+
if (mnemonicToUse) {
|
|
1667
|
+
return await signPsbt(mnemonicToUse, psbt, this.network);
|
|
1668
|
+
}
|
|
1669
|
+
if (this.seed) {
|
|
1670
|
+
return await signPsbtFromSeed(this.seed, psbt, this.network);
|
|
1671
|
+
}
|
|
1672
|
+
throw new WalletError("mnemonic is required. Provide it as parameter or initialize wallet with mnemonic.");
|
|
1673
|
+
}
|
|
1674
|
+
/**
|
|
1675
|
+
* Complete send operation: begin → sign → end
|
|
1676
|
+
* @param invoiceTransfer - Transfer invoice parameters
|
|
1677
|
+
* @param mnemonic - Optional mnemonic for signing
|
|
1678
|
+
*/
|
|
1679
|
+
async send(invoiceTransfer, mnemonic) {
|
|
1680
|
+
this.ensureNotDisposed();
|
|
1681
|
+
const psbt = await this.sendBegin(invoiceTransfer);
|
|
1682
|
+
const signedPsbt = await this.signPsbt(psbt, mnemonic);
|
|
1683
|
+
console.log("send signedPsbt", signedPsbt);
|
|
1684
|
+
return await this.sendEnd({ signedPsbt });
|
|
1685
|
+
}
|
|
1686
|
+
async createUtxos({ upTo, num, size, feeRate }) {
|
|
1687
|
+
this.ensureNotDisposed();
|
|
1688
|
+
const psbt = this.createUtxosBegin({ upTo, num, size, feeRate });
|
|
1689
|
+
const signedPsbt = await this.signPsbt(psbt);
|
|
1690
|
+
return this.createUtxosEnd({ signedPsbt });
|
|
1691
|
+
}
|
|
1692
|
+
syncWallet() {
|
|
1693
|
+
this.client.syncWallet();
|
|
1694
|
+
}
|
|
1695
|
+
async signMessage(message) {
|
|
1696
|
+
this.ensureNotDisposed();
|
|
1697
|
+
if (!message) {
|
|
1698
|
+
throw new ValidationError("message is required", "message");
|
|
1699
|
+
}
|
|
1700
|
+
if (!this.seed) {
|
|
1701
|
+
throw new WalletError("Wallet seed is required for message signing. Initialize the wallet with a seed.");
|
|
1702
|
+
}
|
|
1703
|
+
return signMessage({
|
|
1704
|
+
message,
|
|
1705
|
+
seed: this.seed,
|
|
1706
|
+
network: this.network
|
|
1707
|
+
});
|
|
1708
|
+
}
|
|
1709
|
+
async verifyMessage(message, signature, accountXpub) {
|
|
1710
|
+
if (!message) {
|
|
1711
|
+
throw new ValidationError("message is required", "message");
|
|
1712
|
+
}
|
|
1713
|
+
if (!signature) {
|
|
1714
|
+
throw new ValidationError("signature is required", "signature");
|
|
1715
|
+
}
|
|
1716
|
+
return verifyMessage({
|
|
1717
|
+
message,
|
|
1718
|
+
signature,
|
|
1719
|
+
accountXpub: this.xpubVan,
|
|
1720
|
+
network: this.network
|
|
1721
|
+
});
|
|
1722
|
+
}
|
|
1723
|
+
};
|
|
1724
|
+
function createWalletManager(params) {
|
|
1725
|
+
return new WalletManager(params);
|
|
1726
|
+
}
|
|
1727
|
+
var wallet = new Proxy({}, {
|
|
1728
|
+
get(target, prop) {
|
|
1729
|
+
{
|
|
1730
|
+
throw new WalletError(
|
|
1731
|
+
"The legacy singleton wallet instance is deprecated. Please use `new WalletManager(params)` or `createWalletManager(params)` instead. Example: const wallet = new WalletManager({ xpubVan, xpubCol, masterFingerprint, network, transportEndpoint, indexerUrl })"
|
|
1732
|
+
);
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
});
|
|
1736
|
+
|
|
1737
|
+
// src/types/rgb-model.ts
|
|
1738
|
+
var TransactionType = /* @__PURE__ */ ((TransactionType2) => {
|
|
1739
|
+
TransactionType2[TransactionType2["RGB_SEND"] = 0] = "RGB_SEND";
|
|
1740
|
+
TransactionType2[TransactionType2["DRAIN"] = 1] = "DRAIN";
|
|
1741
|
+
TransactionType2[TransactionType2["CREATE_UTXOS"] = 2] = "CREATE_UTXOS";
|
|
1742
|
+
TransactionType2[TransactionType2["USER"] = 3] = "USER";
|
|
1743
|
+
return TransactionType2;
|
|
1744
|
+
})(TransactionType || {});
|
|
1745
|
+
var TransferStatus = /* @__PURE__ */ ((TransferStatus2) => {
|
|
1746
|
+
TransferStatus2[TransferStatus2["WAITING_COUNTERPARTY"] = 0] = "WAITING_COUNTERPARTY";
|
|
1747
|
+
TransferStatus2[TransferStatus2["WAITING_CONFIRMATIONS"] = 1] = "WAITING_CONFIRMATIONS";
|
|
1748
|
+
TransferStatus2[TransferStatus2["SETTLED"] = 2] = "SETTLED";
|
|
1749
|
+
TransferStatus2[TransferStatus2["FAILED"] = 3] = "FAILED";
|
|
1750
|
+
return TransferStatus2;
|
|
1751
|
+
})(TransferStatus || {});
|
|
1752
|
+
var AssetIface = /* @__PURE__ */ ((AssetIface2) => {
|
|
1753
|
+
AssetIface2["RGB20"] = "RGB20";
|
|
1754
|
+
AssetIface2["RGB21"] = "RGB21";
|
|
1755
|
+
AssetIface2["RGB25"] = "RGB25";
|
|
1756
|
+
return AssetIface2;
|
|
1757
|
+
})(AssetIface || {});
|
|
1758
|
+
var AssetSchema2 = /* @__PURE__ */ ((AssetSchema3) => {
|
|
1759
|
+
AssetSchema3["Nia"] = "Nia";
|
|
1760
|
+
AssetSchema3["Uda"] = "Uda";
|
|
1761
|
+
AssetSchema3["Cfa"] = "Cfa";
|
|
1762
|
+
return AssetSchema3;
|
|
1763
|
+
})(AssetSchema2 || {});
|
|
1764
|
+
|
|
1765
|
+
// src/utils/logger.ts
|
|
1766
|
+
var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
1767
|
+
LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
|
|
1768
|
+
LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
|
|
1769
|
+
LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
|
|
1770
|
+
LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
|
|
1771
|
+
LogLevel2[LogLevel2["NONE"] = 4] = "NONE";
|
|
1772
|
+
return LogLevel2;
|
|
1773
|
+
})(LogLevel || {});
|
|
1774
|
+
var Logger = class {
|
|
1775
|
+
constructor() {
|
|
1776
|
+
this.level = 3 /* ERROR */;
|
|
1777
|
+
}
|
|
1778
|
+
/**
|
|
1779
|
+
* Set the log level
|
|
1780
|
+
*/
|
|
1781
|
+
setLevel(level) {
|
|
1782
|
+
this.level = level;
|
|
1783
|
+
}
|
|
1784
|
+
/**
|
|
1785
|
+
* Get the current log level
|
|
1786
|
+
*/
|
|
1787
|
+
getLevel() {
|
|
1788
|
+
return this.level;
|
|
1789
|
+
}
|
|
1790
|
+
/**
|
|
1791
|
+
* Log debug messages
|
|
1792
|
+
*/
|
|
1793
|
+
debug(...args) {
|
|
1794
|
+
if (this.level <= 0 /* DEBUG */) {
|
|
1795
|
+
console.debug("[SDK DEBUG]", ...args);
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
/**
|
|
1799
|
+
* Log info messages
|
|
1800
|
+
*/
|
|
1801
|
+
info(...args) {
|
|
1802
|
+
if (this.level <= 1 /* INFO */) {
|
|
1803
|
+
console.info("[SDK INFO]", ...args);
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
/**
|
|
1807
|
+
* Log warning messages
|
|
1808
|
+
*/
|
|
1809
|
+
warn(...args) {
|
|
1810
|
+
if (this.level <= 2 /* WARN */) {
|
|
1811
|
+
console.warn("[SDK WARN]", ...args);
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
/**
|
|
1815
|
+
* Log error messages
|
|
1816
|
+
*/
|
|
1817
|
+
error(...args) {
|
|
1818
|
+
if (this.level <= 3 /* ERROR */) {
|
|
1819
|
+
console.error("[SDK ERROR]", ...args);
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
};
|
|
1823
|
+
var logger = new Logger();
|
|
1824
|
+
function configureLogging(level) {
|
|
1825
|
+
logger.setLevel(level);
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
export { AssetIface, AssetSchema2 as AssetSchema, BIP32_VERSIONS, BadRequestError, COIN_BITCOIN_MAINNET, COIN_BITCOIN_TESTNET, COIN_RGB_MAINNET, COIN_RGB_TESTNET, ConfigurationError, ConflictError, CryptoError, DEFAULT_API_TIMEOUT, DEFAULT_LOG_LEVEL, DEFAULT_MAX_RETRIES, DEFAULT_NETWORK, DERIVATION_ACCOUNT, DERIVATION_PURPOSE, KEYCHAIN_BTC, KEYCHAIN_RGB, LogLevel, NETWORK_MAP, NetworkError, NotFoundError, RgbNodeError, SDKError, TransactionType, TransferStatus, ValidationError, WalletError, WalletManager, accountXpubsFromMnemonic, configureLogging, createWallet, createWalletManager, deriveKeysFromMnemonic, deriveKeysFromSeed, deriveKeysFromXpriv, generateKeys3 as generateKeys, getEnvironment, getXprivFromMnemonic, getXpubFromXpriv, isBrowser, isNode, logger, normalizeNetwork, restoreFromBackup, restoreKeys, signMessage, signPsbt, signPsbtFromSeed, signPsbtSync, validateBase64, validateHex, validateMnemonic, validateNetwork, validatePsbt, validateRequired, validateString, verifyMessage, wallet };
|
|
1829
|
+
//# sourceMappingURL=index.mjs.map
|
|
1830
|
+
//# sourceMappingURL=index.mjs.map
|