@1sat/wallet-toolbox 0.0.21 → 0.0.23
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/dist/api/constants.d.ts +1 -0
- package/dist/api/constants.js +2 -1
- package/dist/api/inscriptions/index.js +3 -7
- package/dist/api/ordinals/index.js +16 -15
- package/dist/api/sweep/index.d.ts +9 -2
- package/dist/api/sweep/index.js +166 -2
- package/dist/api/sweep/types.d.ts +23 -0
- package/dist/api/tokens/index.js +2 -2
- package/dist/backup/FileBackupProvider.d.ts +96 -0
- package/dist/backup/FileBackupProvider.js +184 -0
- package/dist/backup/FileRestoreReader.d.ts +58 -0
- package/dist/backup/FileRestoreReader.js +88 -0
- package/dist/backup/index.d.ts +5 -0
- package/dist/backup/index.js +4 -0
- package/dist/backup/types.d.ts +31 -0
- package/dist/backup/types.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/package.json +4 -2
package/dist/api/constants.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export declare const ORDLOCK_PREFIX = "2097dfd76851bf465e8f715593b217714858bbe95
|
|
|
15
15
|
export declare const ORDLOCK_SUFFIX = "615179547a75537a537a537a0079537a75527a527a7575615579008763567901c161517957795779210ac407f0e4bd44bfc207355a778b046225a7068fc59ee7eda43ad905aadbffc800206c266b30e6a1319c66dc401e5bd6b432ba49688eecd118297041da8074ce081059795679615679aa0079610079517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e01007e81517a75615779567956795679567961537956795479577995939521414136d08c5ed2bf3ba048afe6dcaebafeffffffffffffffffffffffffffffff00517951796151795179970079009f63007952799367007968517a75517a75517a7561527a75517a517951795296a0630079527994527a75517a6853798277527982775379012080517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f517f7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e7c7e01205279947f7754537993527993013051797e527e54797e58797e527e53797e52797e57797e0079517a75517a75517a75517a75517a75517a75517a75517a75517a75517a75517a75517a75517a756100795779ac517a75517a75517a75517a75517a75517a75517a75517a75517a7561517a75517a756169587951797e58797eaa577961007982775179517958947f7551790128947f77517a75517a75618777777777777777777767557951876351795779a9876957795779ac777777777777777767006868";
|
|
16
16
|
export declare const LOCK_PREFIX = "20d37f4de0d1c735b4d51a5572df0f3d9104d1d9e99db8694fdd1b1a92e1f0dce1757601687f76a9";
|
|
17
17
|
export declare const LOCK_SUFFIX = "88ac7e7601207f75a9011488";
|
|
18
|
+
export declare const ONESAT_PROTOCOL: [0 | 1 | 2, string];
|
|
18
19
|
export declare const MESSAGE_SIGNING_PROTOCOL: [0 | 1 | 2, string];
|
|
19
20
|
export declare const MAX_INSCRIPTION_BYTES = 100000;
|
|
20
21
|
export declare const MIN_UNLOCK_SATS = 1500;
|
package/dist/api/constants.js
CHANGED
|
@@ -21,7 +21,8 @@ export const ORDLOCK_SUFFIX = "615179547a75537a537a537a0079537a75527a527a7575615
|
|
|
21
21
|
// Lock template constants
|
|
22
22
|
export const LOCK_PREFIX = "20d37f4de0d1c735b4d51a5572df0f3d9104d1d9e99db8694fdd1b1a92e1f0dce1757601687f76a9";
|
|
23
23
|
export const LOCK_SUFFIX = "88ac7e7601207f75a9011488";
|
|
24
|
-
//
|
|
24
|
+
// Protocol IDs
|
|
25
|
+
export const ONESAT_PROTOCOL = [1, "1sat"];
|
|
25
26
|
export const MESSAGE_SIGNING_PROTOCOL = [1, "message signing"];
|
|
26
27
|
// Constants
|
|
27
28
|
export const MAX_INSCRIPTION_BYTES = 100_000;
|
|
@@ -5,11 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { P2PKH, PublicKey, Script, Utils } from "@bsv/sdk";
|
|
7
7
|
import { Inscription } from "@bopen-io/templates";
|
|
8
|
-
import { MAX_INSCRIPTION_BYTES, ORDINALS_BASKET } from "../constants";
|
|
9
|
-
// ============================================================================
|
|
10
|
-
// Constants
|
|
11
|
-
// ============================================================================
|
|
12
|
-
const ORDINAL_PROTOCOL = [1, "ordinal"];
|
|
8
|
+
import { MAX_INSCRIPTION_BYTES, ORDINALS_BASKET, ONESAT_PROTOCOL } from "../constants";
|
|
13
9
|
// ============================================================================
|
|
14
10
|
// Internal helpers
|
|
15
11
|
// ============================================================================
|
|
@@ -58,7 +54,7 @@ export const inscribe = {
|
|
|
58
54
|
}
|
|
59
55
|
const keyID = Date.now().toString();
|
|
60
56
|
const { publicKey } = await ctx.wallet.getPublicKey({
|
|
61
|
-
protocolID:
|
|
57
|
+
protocolID: ONESAT_PROTOCOL,
|
|
62
58
|
keyID,
|
|
63
59
|
counterparty: "self",
|
|
64
60
|
forSelf: true,
|
|
@@ -75,7 +71,7 @@ export const inscribe = {
|
|
|
75
71
|
basket: ORDINALS_BASKET,
|
|
76
72
|
tags: [`type:${input.contentType}`],
|
|
77
73
|
customInstructions: JSON.stringify({
|
|
78
|
-
protocolID:
|
|
74
|
+
protocolID: ONESAT_PROTOCOL,
|
|
79
75
|
keyID,
|
|
80
76
|
}),
|
|
81
77
|
},
|
|
@@ -6,18 +6,13 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { BigNumber, Hash, LockingScript, OP, P2PKH, PublicKey, Script, Transaction, TransactionSignature, UnlockingScript, Utils, } from "@bsv/sdk";
|
|
8
8
|
import { OrdLock } from "@bopen-io/templates";
|
|
9
|
-
import { ORDINALS_BASKET, ORDLOCK_PREFIX, ORDLOCK_SUFFIX } from "../constants";
|
|
10
|
-
// ============================================================================
|
|
11
|
-
// Constants
|
|
12
|
-
// ============================================================================
|
|
13
|
-
const ORDINAL_PROTOCOL = [1, "ordinal"];
|
|
14
|
-
const ORDINAL_LISTING_PROTOCOL = [1, "ordinal listing"];
|
|
9
|
+
import { ORDINALS_BASKET, ORDLOCK_PREFIX, ORDLOCK_SUFFIX, ONESAT_PROTOCOL } from "../constants";
|
|
15
10
|
// ============================================================================
|
|
16
11
|
// Internal helpers
|
|
17
12
|
// ============================================================================
|
|
18
13
|
async function deriveCancelAddressInternal(ctx, outpoint) {
|
|
19
14
|
const result = await ctx.wallet.getPublicKey({
|
|
20
|
-
protocolID:
|
|
15
|
+
protocolID: ONESAT_PROTOCOL,
|
|
21
16
|
keyID: outpoint,
|
|
22
17
|
forSelf: true,
|
|
23
18
|
});
|
|
@@ -97,7 +92,7 @@ export async function buildTransferOrdinal(ctx, request) {
|
|
|
97
92
|
let recipientAddress;
|
|
98
93
|
if (counterparty) {
|
|
99
94
|
const { publicKey } = await ctx.wallet.getPublicKey({
|
|
100
|
-
protocolID:
|
|
95
|
+
protocolID: ONESAT_PROTOCOL,
|
|
101
96
|
keyID: outpoint,
|
|
102
97
|
counterparty,
|
|
103
98
|
forSelf: false,
|
|
@@ -169,7 +164,7 @@ export async function buildListOrdinal(ctx, request) {
|
|
|
169
164
|
basket: ORDINALS_BASKET,
|
|
170
165
|
tags,
|
|
171
166
|
customInstructions: JSON.stringify({
|
|
172
|
-
protocolID:
|
|
167
|
+
protocolID: ONESAT_PROTOCOL,
|
|
173
168
|
keyID: outpoint,
|
|
174
169
|
}),
|
|
175
170
|
},
|
|
@@ -248,7 +243,10 @@ export const transferOrdinal = {
|
|
|
248
243
|
if ("error" in params) {
|
|
249
244
|
return params;
|
|
250
245
|
}
|
|
251
|
-
const result = await ctx.wallet.createAction(
|
|
246
|
+
const result = await ctx.wallet.createAction({
|
|
247
|
+
...params,
|
|
248
|
+
options: { randomizeOutputs: false },
|
|
249
|
+
});
|
|
252
250
|
if (!result.txid) {
|
|
253
251
|
return { error: "no-txid-returned" };
|
|
254
252
|
}
|
|
@@ -283,7 +281,10 @@ export const listOrdinal = {
|
|
|
283
281
|
if ("error" in params) {
|
|
284
282
|
return params;
|
|
285
283
|
}
|
|
286
|
-
const result = await ctx.wallet.createAction(
|
|
284
|
+
const result = await ctx.wallet.createAction({
|
|
285
|
+
...params,
|
|
286
|
+
options: { randomizeOutputs: false },
|
|
287
|
+
});
|
|
287
288
|
if (!result.txid) {
|
|
288
289
|
return { error: "no-txid-returned" };
|
|
289
290
|
}
|
|
@@ -355,7 +356,7 @@ export const cancelListing = {
|
|
|
355
356
|
customInstructions: JSON.stringify({ protocolID, keyID }),
|
|
356
357
|
},
|
|
357
358
|
],
|
|
358
|
-
options: { signAndProcess: false },
|
|
359
|
+
options: { signAndProcess: false, randomizeOutputs: false },
|
|
359
360
|
});
|
|
360
361
|
if ("error" in createResult && createResult.error) {
|
|
361
362
|
return { error: String(createResult.error) };
|
|
@@ -473,7 +474,7 @@ export const purchaseOrdinal = {
|
|
|
473
474
|
return { error: "not-an-ordlock-listing" };
|
|
474
475
|
}
|
|
475
476
|
const { publicKey } = await ctx.wallet.getPublicKey({
|
|
476
|
-
protocolID:
|
|
477
|
+
protocolID: ONESAT_PROTOCOL,
|
|
477
478
|
keyID: outpoint,
|
|
478
479
|
counterparty: "self",
|
|
479
480
|
forSelf: true,
|
|
@@ -488,7 +489,7 @@ export const purchaseOrdinal = {
|
|
|
488
489
|
basket: ORDINALS_BASKET,
|
|
489
490
|
tags: [`type:${contentType}`, `origin:${origin}`],
|
|
490
491
|
customInstructions: JSON.stringify({
|
|
491
|
-
protocolID:
|
|
492
|
+
protocolID: ONESAT_PROTOCOL,
|
|
492
493
|
keyID: outpoint,
|
|
493
494
|
}),
|
|
494
495
|
});
|
|
@@ -523,7 +524,7 @@ export const purchaseOrdinal = {
|
|
|
523
524
|
},
|
|
524
525
|
],
|
|
525
526
|
outputs,
|
|
526
|
-
options: { signAndProcess: false },
|
|
527
|
+
options: { signAndProcess: false, randomizeOutputs: false },
|
|
527
528
|
});
|
|
528
529
|
if ("error" in createResult && createResult.error) {
|
|
529
530
|
return { error: String(createResult.error) };
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { OneSatContext, Skill } from "../skills/types";
|
|
7
7
|
import type { IndexedOutput } from "../../services/types";
|
|
8
|
-
import type { SweepBsvRequest, SweepBsvResponse, SweepInput } from "./types";
|
|
8
|
+
import type { SweepBsvRequest, SweepBsvResponse, SweepInput, SweepOrdinalsRequest, SweepOrdinalsResponse } from "./types";
|
|
9
9
|
export * from "./types";
|
|
10
10
|
/**
|
|
11
11
|
* Prepare sweep inputs from IndexedOutput objects by fetching locking scripts.
|
|
@@ -20,4 +20,11 @@ export declare function prepareSweepInputs(ctx: OneSatContext, utxos: IndexedOut
|
|
|
20
20
|
* value is swept (minus fees).
|
|
21
21
|
*/
|
|
22
22
|
export declare const sweepBsv: Skill<SweepBsvRequest, SweepBsvResponse>;
|
|
23
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Sweep ordinals from external inputs into the destination wallet.
|
|
25
|
+
*
|
|
26
|
+
* Each input is expected to be a 1-sat ordinal output. Each ordinal is
|
|
27
|
+
* transferred to a derived address using the wallet's key derivation.
|
|
28
|
+
*/
|
|
29
|
+
export declare const sweepOrdinals: Skill<SweepOrdinalsRequest, SweepOrdinalsResponse>;
|
|
30
|
+
export declare const sweepSkills: (Skill<SweepBsvRequest, SweepBsvResponse> | Skill<SweepOrdinalsRequest, SweepOrdinalsResponse>)[];
|
package/dist/api/sweep/index.js
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Functions for sweeping assets from external wallets into a BRC-100 wallet.
|
|
5
5
|
*/
|
|
6
|
-
import { P2PKH, PrivateKey, Transaction, } from "@bsv/sdk";
|
|
6
|
+
import { P2PKH, PrivateKey, PublicKey, Transaction, } from "@bsv/sdk";
|
|
7
|
+
import { ONESAT_PROTOCOL } from "../constants";
|
|
7
8
|
export * from "./types";
|
|
8
9
|
/**
|
|
9
10
|
* Prepare sweep inputs from IndexedOutput objects by fetching locking scripts.
|
|
@@ -217,5 +218,168 @@ export const sweepBsv = {
|
|
|
217
218
|
}
|
|
218
219
|
},
|
|
219
220
|
};
|
|
221
|
+
/**
|
|
222
|
+
* Sweep ordinals from external inputs into the destination wallet.
|
|
223
|
+
*
|
|
224
|
+
* Each input is expected to be a 1-sat ordinal output. Each ordinal is
|
|
225
|
+
* transferred to a derived address using the wallet's key derivation.
|
|
226
|
+
*/
|
|
227
|
+
export const sweepOrdinals = {
|
|
228
|
+
meta: {
|
|
229
|
+
name: "sweepOrdinals",
|
|
230
|
+
description: "Sweep ordinals from external wallet (via WIF) into the connected wallet",
|
|
231
|
+
category: "sweep",
|
|
232
|
+
requiresServices: true,
|
|
233
|
+
inputSchema: {
|
|
234
|
+
type: "object",
|
|
235
|
+
properties: {
|
|
236
|
+
inputs: {
|
|
237
|
+
type: "array",
|
|
238
|
+
description: "Ordinal UTXOs to sweep",
|
|
239
|
+
items: {
|
|
240
|
+
type: "object",
|
|
241
|
+
properties: {
|
|
242
|
+
outpoint: { type: "string", description: "Outpoint (txid_vout)" },
|
|
243
|
+
satoshis: { type: "integer", description: "Satoshis (should be 1)" },
|
|
244
|
+
lockingScript: { type: "string", description: "Locking script hex" },
|
|
245
|
+
contentType: { type: "string", description: "Content type from metadata" },
|
|
246
|
+
origin: { type: "string", description: "Origin outpoint" },
|
|
247
|
+
},
|
|
248
|
+
required: ["outpoint", "satoshis", "lockingScript"],
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
wif: {
|
|
252
|
+
type: "string",
|
|
253
|
+
description: "WIF private key controlling the inputs",
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
required: ["inputs", "wif"],
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
async execute(ctx, request) {
|
|
260
|
+
if (!ctx.services) {
|
|
261
|
+
return { error: "services-required" };
|
|
262
|
+
}
|
|
263
|
+
try {
|
|
264
|
+
const { inputs, wif } = request;
|
|
265
|
+
if (!inputs || inputs.length === 0) {
|
|
266
|
+
return { error: "no-inputs" };
|
|
267
|
+
}
|
|
268
|
+
// Parse WIF
|
|
269
|
+
const privateKey = PrivateKey.fromWif(wif);
|
|
270
|
+
// Fetch BEEF for all input transactions and merge them
|
|
271
|
+
const txids = [...new Set(inputs.map((i) => i.outpoint.split("_")[0]))];
|
|
272
|
+
console.log(`[sweepOrdinals] Fetching BEEF for ${txids.length} transactions`);
|
|
273
|
+
const firstBeef = await ctx.services.getBeefForTxid(txids[0]);
|
|
274
|
+
for (let i = 1; i < txids.length; i++) {
|
|
275
|
+
const additionalBeef = await ctx.services.getBeefForTxid(txids[i]);
|
|
276
|
+
firstBeef.mergeBeef(additionalBeef);
|
|
277
|
+
}
|
|
278
|
+
console.log(`[sweepOrdinals] Merged BEEF valid=${firstBeef.isValid()}, txs=${firstBeef.txs.length}`);
|
|
279
|
+
// Build input descriptors
|
|
280
|
+
const inputDescriptors = inputs.map((input) => {
|
|
281
|
+
const [txid, voutStr] = input.outpoint.split("_");
|
|
282
|
+
return {
|
|
283
|
+
outpoint: `${txid}.${voutStr}`,
|
|
284
|
+
inputDescription: `Ordinal ${input.origin ?? input.outpoint}`,
|
|
285
|
+
unlockingScriptLength: 108,
|
|
286
|
+
sequenceNumber: 0xffffffff,
|
|
287
|
+
};
|
|
288
|
+
});
|
|
289
|
+
// Build outputs - one per ordinal, each 1 sat to derived address
|
|
290
|
+
const outputs = [];
|
|
291
|
+
for (const input of inputs) {
|
|
292
|
+
// Derive a unique public key for this ordinal using the input outpoint as keyID
|
|
293
|
+
const pubKeyResult = await ctx.wallet.getPublicKey({
|
|
294
|
+
protocolID: ONESAT_PROTOCOL,
|
|
295
|
+
keyID: input.outpoint,
|
|
296
|
+
forSelf: true,
|
|
297
|
+
});
|
|
298
|
+
if (!pubKeyResult.publicKey) {
|
|
299
|
+
return { error: `Failed to derive key for ${input.outpoint}` };
|
|
300
|
+
}
|
|
301
|
+
// Create P2PKH locking script from derived public key
|
|
302
|
+
const derivedAddress = PublicKey.fromString(pubKeyResult.publicKey).toAddress();
|
|
303
|
+
const lockingScript = new P2PKH().lock(derivedAddress);
|
|
304
|
+
// Build tags following ordinals API pattern
|
|
305
|
+
const tags = [];
|
|
306
|
+
if (input.contentType)
|
|
307
|
+
tags.push(`type:${input.contentType}`);
|
|
308
|
+
if (input.origin)
|
|
309
|
+
tags.push(`origin:${input.origin}`);
|
|
310
|
+
outputs.push({
|
|
311
|
+
lockingScript: lockingScript.toHex(),
|
|
312
|
+
satoshis: 1,
|
|
313
|
+
outputDescription: `Ordinal ${input.origin ?? input.outpoint}`,
|
|
314
|
+
basket: "1sat",
|
|
315
|
+
tags,
|
|
316
|
+
customInstructions: JSON.stringify({
|
|
317
|
+
protocolID: ONESAT_PROTOCOL,
|
|
318
|
+
keyID: input.outpoint,
|
|
319
|
+
}),
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
const beefData = firstBeef.toBinary();
|
|
323
|
+
// Create action to get signable transaction
|
|
324
|
+
// CRITICAL: randomizeOutputs must be false to preserve ordinal satoshi positions
|
|
325
|
+
const createResult = await ctx.wallet.createAction({
|
|
326
|
+
description: `Sweep ${inputs.length} ordinal${inputs.length !== 1 ? "s" : ""}`,
|
|
327
|
+
inputBEEF: beefData,
|
|
328
|
+
inputs: inputDescriptors,
|
|
329
|
+
outputs,
|
|
330
|
+
options: { signAndProcess: false, randomizeOutputs: false },
|
|
331
|
+
});
|
|
332
|
+
if ("error" in createResult && createResult.error) {
|
|
333
|
+
return { error: String(createResult.error) };
|
|
334
|
+
}
|
|
335
|
+
if (!createResult.signableTransaction) {
|
|
336
|
+
return { error: "no-signable-transaction" };
|
|
337
|
+
}
|
|
338
|
+
// Sign each input with our external key
|
|
339
|
+
const tx = Transaction.fromBEEF(createResult.signableTransaction.tx);
|
|
340
|
+
// Build a set of outpoints we control
|
|
341
|
+
const ourOutpoints = new Set(inputs.map((input) => {
|
|
342
|
+
const [txid, vout] = input.outpoint.split("_");
|
|
343
|
+
return `${txid}.${vout}`;
|
|
344
|
+
}));
|
|
345
|
+
// Set up P2PKH unlocker on each input we control
|
|
346
|
+
for (let i = 0; i < tx.inputs.length; i++) {
|
|
347
|
+
const txInput = tx.inputs[i];
|
|
348
|
+
const inputOutpoint = `${txInput.sourceTXID}.${txInput.sourceOutputIndex}`;
|
|
349
|
+
if (ourOutpoints.has(inputOutpoint)) {
|
|
350
|
+
const p2pkh = new P2PKH();
|
|
351
|
+
txInput.unlockingScriptTemplate = p2pkh.unlock(privateKey, "all", true);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
await tx.sign();
|
|
355
|
+
// Extract unlocking scripts for signAction
|
|
356
|
+
const spends = {};
|
|
357
|
+
for (let i = 0; i < tx.inputs.length; i++) {
|
|
358
|
+
const txInput = tx.inputs[i];
|
|
359
|
+
const inputOutpoint = `${txInput.sourceTXID}.${txInput.sourceOutputIndex}`;
|
|
360
|
+
if (ourOutpoints.has(inputOutpoint)) {
|
|
361
|
+
spends[i] = { unlockingScript: txInput.unlockingScript?.toHex() ?? "" };
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
// Complete the action with our signatures
|
|
365
|
+
const signResult = await ctx.wallet.signAction({
|
|
366
|
+
reference: createResult.signableTransaction.reference,
|
|
367
|
+
spends,
|
|
368
|
+
});
|
|
369
|
+
if ("error" in signResult) {
|
|
370
|
+
return { error: String(signResult.error) };
|
|
371
|
+
}
|
|
372
|
+
return {
|
|
373
|
+
txid: signResult.txid,
|
|
374
|
+
beef: signResult.tx ? Array.from(signResult.tx) : undefined,
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
catch (error) {
|
|
378
|
+
return {
|
|
379
|
+
error: error instanceof Error ? error.message : "unknown-error",
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
};
|
|
220
384
|
// Export skills array for registry
|
|
221
|
-
export const sweepSkills = [sweepBsv];
|
|
385
|
+
export const sweepSkills = [sweepBsv, sweepOrdinals];
|
|
@@ -28,3 +28,26 @@ export interface SweepBsvResponse {
|
|
|
28
28
|
/** Error message if failed */
|
|
29
29
|
error?: string;
|
|
30
30
|
}
|
|
31
|
+
/** Input for ordinal sweep operations */
|
|
32
|
+
export interface SweepOrdinalInput extends SweepInput {
|
|
33
|
+
/** Content type from ordfs metadata */
|
|
34
|
+
contentType?: string;
|
|
35
|
+
/** Origin outpoint for tracking */
|
|
36
|
+
origin?: string;
|
|
37
|
+
}
|
|
38
|
+
/** Request to sweep ordinals */
|
|
39
|
+
export interface SweepOrdinalsRequest {
|
|
40
|
+
/** Ordinal UTXOs to sweep */
|
|
41
|
+
inputs: SweepOrdinalInput[];
|
|
42
|
+
/** WIF private key controlling the inputs */
|
|
43
|
+
wif: string;
|
|
44
|
+
}
|
|
45
|
+
/** Response from ordinal sweep operation */
|
|
46
|
+
export interface SweepOrdinalsResponse {
|
|
47
|
+
/** Transaction ID if successful */
|
|
48
|
+
txid?: string;
|
|
49
|
+
/** BEEF (transaction with validity proof) */
|
|
50
|
+
beef?: number[];
|
|
51
|
+
/** Error message if failed */
|
|
52
|
+
error?: string;
|
|
53
|
+
}
|
package/dist/api/tokens/index.js
CHANGED
|
@@ -289,7 +289,7 @@ export const sendBsv21 = {
|
|
|
289
289
|
inputDescription: "Token input",
|
|
290
290
|
})),
|
|
291
291
|
outputs,
|
|
292
|
-
options: { signAndProcess: false },
|
|
292
|
+
options: { signAndProcess: false, randomizeOutputs: false },
|
|
293
293
|
});
|
|
294
294
|
if ("error" in createResult && createResult.error) {
|
|
295
295
|
return { error: String(createResult.error) };
|
|
@@ -421,7 +421,7 @@ export const purchaseBsv21 = {
|
|
|
421
421
|
},
|
|
422
422
|
],
|
|
423
423
|
outputs,
|
|
424
|
-
options: { signAndProcess: false },
|
|
424
|
+
options: { signAndProcess: false, randomizeOutputs: false },
|
|
425
425
|
});
|
|
426
426
|
if ("error" in createResult && createResult.error) {
|
|
427
427
|
return { error: String(createResult.error) };
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { Zip, ZipDeflate } from "fflate";
|
|
2
|
+
import type { sdk } from "@bsv/wallet-toolbox-mobile/out/src/index.client.js";
|
|
3
|
+
import type { TableSettings } from "@bsv/wallet-toolbox-mobile/out/src/storage/schema/tables/TableSettings.js";
|
|
4
|
+
import type { TableSyncState } from "@bsv/wallet-toolbox-mobile/out/src/storage/schema/tables/TableSyncState.js";
|
|
5
|
+
import type { TableUser } from "@bsv/wallet-toolbox-mobile/out/src/storage/schema/tables/TableUser.js";
|
|
6
|
+
type AuthId = sdk.AuthId;
|
|
7
|
+
type ProcessSyncChunkResult = sdk.ProcessSyncChunkResult;
|
|
8
|
+
type RequestSyncChunkArgs = sdk.RequestSyncChunkArgs;
|
|
9
|
+
type SyncChunk = sdk.SyncChunk;
|
|
10
|
+
type WalletStorageProvider = sdk.WalletStorageProvider;
|
|
11
|
+
type WalletServices = sdk.WalletServices;
|
|
12
|
+
/**
|
|
13
|
+
* FileBackupProvider implements WalletStorageProvider to receive sync chunks
|
|
14
|
+
* during wallet export. It encodes each chunk with MessagePack and streams
|
|
15
|
+
* it directly to a fflate Zip instance.
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
* 1. Create fflate.Zip with streaming callback
|
|
19
|
+
* 2. Create FileBackupProvider with the zip instance
|
|
20
|
+
* 3. Call storage.syncToWriter(auth, provider)
|
|
21
|
+
* 4. Provider receives processSyncChunk() calls and writes to zip
|
|
22
|
+
* 5. Call provider.getChunkCount() to get chunk count for manifest
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* import { Zip } from 'fflate';
|
|
27
|
+
*
|
|
28
|
+
* const chunks: Uint8Array[] = [];
|
|
29
|
+
* const zip = new Zip((err, data, final) => {
|
|
30
|
+
* if (err) throw err;
|
|
31
|
+
* chunks.push(data);
|
|
32
|
+
* if (final) {
|
|
33
|
+
* const blob = new Blob(chunks, { type: 'application/zip' });
|
|
34
|
+
* // Download or save blob
|
|
35
|
+
* }
|
|
36
|
+
* });
|
|
37
|
+
*
|
|
38
|
+
* const provider = new FileBackupProvider(zip, storage.getSettings(), identityKey);
|
|
39
|
+
* await storage.syncToWriter(auth, provider);
|
|
40
|
+
* console.log(`Exported ${provider.getChunkCount()} chunks`);
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare class FileBackupProvider implements WalletStorageProvider {
|
|
44
|
+
private zip;
|
|
45
|
+
private chunkCount;
|
|
46
|
+
private settings;
|
|
47
|
+
private identityKey;
|
|
48
|
+
constructor(zip: Zip, settings: TableSettings, identityKey: string);
|
|
49
|
+
/**
|
|
50
|
+
* Get the number of chunks written. Call after sync completes.
|
|
51
|
+
*/
|
|
52
|
+
getChunkCount(): number;
|
|
53
|
+
/**
|
|
54
|
+
* Add a file to the zip archive with compression.
|
|
55
|
+
* Creates a ZipDeflate stream and pushes the data in one chunk.
|
|
56
|
+
*/
|
|
57
|
+
private addFileToZip;
|
|
58
|
+
isStorageProvider(): boolean;
|
|
59
|
+
setServices(_v: WalletServices): void;
|
|
60
|
+
findOrInsertSyncStateAuth(_auth: AuthId, _storageIdentityKey: string, _storageName: string): Promise<{
|
|
61
|
+
syncState: TableSyncState;
|
|
62
|
+
isNew: boolean;
|
|
63
|
+
}>;
|
|
64
|
+
setActive(_auth: AuthId, _newActiveStorageIdentityKey: string): Promise<number>;
|
|
65
|
+
getSyncChunk(_args: RequestSyncChunkArgs): Promise<SyncChunk>;
|
|
66
|
+
/**
|
|
67
|
+
* Receives sync chunks from WalletStorageManager.syncToWriter().
|
|
68
|
+
* Encodes each chunk with MessagePack and adds to the zip.
|
|
69
|
+
*/
|
|
70
|
+
processSyncChunk(_args: RequestSyncChunkArgs, chunk: SyncChunk): Promise<ProcessSyncChunkResult>;
|
|
71
|
+
makeAvailable(): Promise<TableSettings>;
|
|
72
|
+
migrate(_storageName: string, _storageIdentityKey: string): Promise<string>;
|
|
73
|
+
destroy(): Promise<void>;
|
|
74
|
+
findOrInsertUser(identityKey: string): Promise<{
|
|
75
|
+
user: TableUser;
|
|
76
|
+
isNew: boolean;
|
|
77
|
+
}>;
|
|
78
|
+
abortAction(_auth: AuthId, _args: unknown): Promise<never>;
|
|
79
|
+
createAction(_auth: AuthId, _args: unknown): Promise<never>;
|
|
80
|
+
processAction(_auth: AuthId, _args: unknown): Promise<never>;
|
|
81
|
+
internalizeAction(_auth: AuthId, _args: unknown): Promise<never>;
|
|
82
|
+
insertCertificateAuth(_auth: AuthId, _certificate: unknown): Promise<number>;
|
|
83
|
+
relinquishCertificate(_auth: AuthId, _args: unknown): Promise<number>;
|
|
84
|
+
relinquishOutput(_auth: AuthId, _args: unknown): Promise<number>;
|
|
85
|
+
isAvailable(): boolean;
|
|
86
|
+
getServices(): WalletServices;
|
|
87
|
+
getSettings(): TableSettings;
|
|
88
|
+
findCertificatesAuth(_auth: AuthId, _args: unknown): Promise<never[]>;
|
|
89
|
+
findOutputBasketsAuth(_auth: AuthId, _args: unknown): Promise<never[]>;
|
|
90
|
+
findOutputsAuth(_auth: AuthId, _args: unknown): Promise<never[]>;
|
|
91
|
+
findProvenTxReqs(_args: unknown): Promise<never[]>;
|
|
92
|
+
listActions(_auth: AuthId, _vargs: unknown): Promise<never>;
|
|
93
|
+
listCertificates(_auth: AuthId, _vargs: unknown): Promise<never>;
|
|
94
|
+
listOutputs(_auth: AuthId, _vargs: unknown): Promise<never>;
|
|
95
|
+
}
|
|
96
|
+
export { Zip, ZipDeflate };
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { encode } from "@msgpack/msgpack";
|
|
2
|
+
import { Zip, ZipDeflate } from "fflate";
|
|
3
|
+
/**
|
|
4
|
+
* FileBackupProvider implements WalletStorageProvider to receive sync chunks
|
|
5
|
+
* during wallet export. It encodes each chunk with MessagePack and streams
|
|
6
|
+
* it directly to a fflate Zip instance.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* 1. Create fflate.Zip with streaming callback
|
|
10
|
+
* 2. Create FileBackupProvider with the zip instance
|
|
11
|
+
* 3. Call storage.syncToWriter(auth, provider)
|
|
12
|
+
* 4. Provider receives processSyncChunk() calls and writes to zip
|
|
13
|
+
* 5. Call provider.getChunkCount() to get chunk count for manifest
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* import { Zip } from 'fflate';
|
|
18
|
+
*
|
|
19
|
+
* const chunks: Uint8Array[] = [];
|
|
20
|
+
* const zip = new Zip((err, data, final) => {
|
|
21
|
+
* if (err) throw err;
|
|
22
|
+
* chunks.push(data);
|
|
23
|
+
* if (final) {
|
|
24
|
+
* const blob = new Blob(chunks, { type: 'application/zip' });
|
|
25
|
+
* // Download or save blob
|
|
26
|
+
* }
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* const provider = new FileBackupProvider(zip, storage.getSettings(), identityKey);
|
|
30
|
+
* await storage.syncToWriter(auth, provider);
|
|
31
|
+
* console.log(`Exported ${provider.getChunkCount()} chunks`);
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export class FileBackupProvider {
|
|
35
|
+
zip;
|
|
36
|
+
chunkCount = 0;
|
|
37
|
+
settings;
|
|
38
|
+
identityKey;
|
|
39
|
+
constructor(zip, settings, identityKey) {
|
|
40
|
+
this.zip = zip;
|
|
41
|
+
this.settings = settings;
|
|
42
|
+
this.identityKey = identityKey;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Get the number of chunks written. Call after sync completes.
|
|
46
|
+
*/
|
|
47
|
+
getChunkCount() {
|
|
48
|
+
return this.chunkCount;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Add a file to the zip archive with compression.
|
|
52
|
+
* Creates a ZipDeflate stream and pushes the data in one chunk.
|
|
53
|
+
*/
|
|
54
|
+
addFileToZip(filename, data) {
|
|
55
|
+
const deflate = new ZipDeflate(filename, { level: 6 });
|
|
56
|
+
this.zip.add(deflate);
|
|
57
|
+
deflate.push(data, true); // true = final chunk
|
|
58
|
+
}
|
|
59
|
+
// WalletStorageProvider interface methods
|
|
60
|
+
isStorageProvider() {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
setServices(_v) {
|
|
64
|
+
// Not needed for backup
|
|
65
|
+
}
|
|
66
|
+
// WalletStorageSync interface methods
|
|
67
|
+
async findOrInsertSyncStateAuth(_auth, _storageIdentityKey, _storageName) {
|
|
68
|
+
throw new Error("Not supported: findOrInsertSyncStateAuth");
|
|
69
|
+
}
|
|
70
|
+
async setActive(_auth, _newActiveStorageIdentityKey) {
|
|
71
|
+
throw new Error("Not supported: setActive");
|
|
72
|
+
}
|
|
73
|
+
async getSyncChunk(_args) {
|
|
74
|
+
throw new Error("Not supported: getSyncChunk - this is a write-only provider");
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Receives sync chunks from WalletStorageManager.syncToWriter().
|
|
78
|
+
* Encodes each chunk with MessagePack and adds to the zip.
|
|
79
|
+
*/
|
|
80
|
+
async processSyncChunk(_args, chunk) {
|
|
81
|
+
const chunkName = `chunk-${String(this.chunkCount).padStart(4, "0")}.bin`;
|
|
82
|
+
const encoded = encode(chunk);
|
|
83
|
+
// Add chunk to zip with compression using ZipDeflate
|
|
84
|
+
this.addFileToZip(chunkName, new Uint8Array(encoded));
|
|
85
|
+
this.chunkCount++;
|
|
86
|
+
// Check if this chunk has any data - if all arrays are empty or undefined, we're done
|
|
87
|
+
const hasData = (chunk.provenTxs && chunk.provenTxs.length > 0) ||
|
|
88
|
+
(chunk.provenTxReqs && chunk.provenTxReqs.length > 0) ||
|
|
89
|
+
(chunk.outputBaskets && chunk.outputBaskets.length > 0) ||
|
|
90
|
+
(chunk.txLabels && chunk.txLabels.length > 0) ||
|
|
91
|
+
(chunk.outputTags && chunk.outputTags.length > 0) ||
|
|
92
|
+
(chunk.transactions && chunk.transactions.length > 0) ||
|
|
93
|
+
(chunk.txLabelMaps && chunk.txLabelMaps.length > 0) ||
|
|
94
|
+
(chunk.commissions && chunk.commissions.length > 0) ||
|
|
95
|
+
(chunk.outputs && chunk.outputs.length > 0) ||
|
|
96
|
+
(chunk.outputTagMaps && chunk.outputTagMaps.length > 0) ||
|
|
97
|
+
(chunk.certificates && chunk.certificates.length > 0) ||
|
|
98
|
+
(chunk.certificateFields && chunk.certificateFields.length > 0);
|
|
99
|
+
return {
|
|
100
|
+
done: !hasData,
|
|
101
|
+
maxUpdated_at: undefined,
|
|
102
|
+
updates: 0,
|
|
103
|
+
inserts: 0,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
// WalletStorageWriter interface methods (throw not supported)
|
|
107
|
+
async makeAvailable() {
|
|
108
|
+
return this.settings;
|
|
109
|
+
}
|
|
110
|
+
async migrate(_storageName, _storageIdentityKey) {
|
|
111
|
+
throw new Error("Not supported: migrate");
|
|
112
|
+
}
|
|
113
|
+
async destroy() {
|
|
114
|
+
throw new Error("Not supported: destroy");
|
|
115
|
+
}
|
|
116
|
+
async findOrInsertUser(identityKey) {
|
|
117
|
+
// Return a mock user for backup purposes
|
|
118
|
+
const now = new Date();
|
|
119
|
+
return {
|
|
120
|
+
user: {
|
|
121
|
+
userId: 1,
|
|
122
|
+
identityKey: identityKey || this.identityKey,
|
|
123
|
+
activeStorage: this.settings.storageIdentityKey || "",
|
|
124
|
+
created_at: now,
|
|
125
|
+
updated_at: now,
|
|
126
|
+
},
|
|
127
|
+
isNew: false,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
async abortAction(_auth, _args) {
|
|
131
|
+
throw new Error("Not supported: abortAction");
|
|
132
|
+
}
|
|
133
|
+
async createAction(_auth, _args) {
|
|
134
|
+
throw new Error("Not supported: createAction");
|
|
135
|
+
}
|
|
136
|
+
async processAction(_auth, _args) {
|
|
137
|
+
throw new Error("Not supported: processAction");
|
|
138
|
+
}
|
|
139
|
+
async internalizeAction(_auth, _args) {
|
|
140
|
+
throw new Error("Not supported: internalizeAction");
|
|
141
|
+
}
|
|
142
|
+
async insertCertificateAuth(_auth, _certificate) {
|
|
143
|
+
throw new Error("Not supported: insertCertificateAuth");
|
|
144
|
+
}
|
|
145
|
+
async relinquishCertificate(_auth, _args) {
|
|
146
|
+
throw new Error("Not supported: relinquishCertificate");
|
|
147
|
+
}
|
|
148
|
+
async relinquishOutput(_auth, _args) {
|
|
149
|
+
throw new Error("Not supported: relinquishOutput");
|
|
150
|
+
}
|
|
151
|
+
// WalletStorageReader interface methods (throw not supported)
|
|
152
|
+
isAvailable() {
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
getServices() {
|
|
156
|
+
throw new Error("Not supported: getServices");
|
|
157
|
+
}
|
|
158
|
+
getSettings() {
|
|
159
|
+
return this.settings;
|
|
160
|
+
}
|
|
161
|
+
async findCertificatesAuth(_auth, _args) {
|
|
162
|
+
throw new Error("Not supported: findCertificatesAuth");
|
|
163
|
+
}
|
|
164
|
+
async findOutputBasketsAuth(_auth, _args) {
|
|
165
|
+
throw new Error("Not supported: findOutputBasketsAuth");
|
|
166
|
+
}
|
|
167
|
+
async findOutputsAuth(_auth, _args) {
|
|
168
|
+
throw new Error("Not supported: findOutputsAuth");
|
|
169
|
+
}
|
|
170
|
+
async findProvenTxReqs(_args) {
|
|
171
|
+
throw new Error("Not supported: findProvenTxReqs");
|
|
172
|
+
}
|
|
173
|
+
async listActions(_auth, _vargs) {
|
|
174
|
+
throw new Error("Not supported: listActions");
|
|
175
|
+
}
|
|
176
|
+
async listCertificates(_auth, _vargs) {
|
|
177
|
+
throw new Error("Not supported: listCertificates");
|
|
178
|
+
}
|
|
179
|
+
async listOutputs(_auth, _vargs) {
|
|
180
|
+
throw new Error("Not supported: listOutputs");
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
// Re-export Zip and ZipDeflate for convenience
|
|
184
|
+
export { Zip, ZipDeflate };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { Unzipped } from "fflate";
|
|
2
|
+
import type { sdk } from "@bsv/wallet-toolbox-mobile/out/src/index.client.js";
|
|
3
|
+
import type { TableSettings } from "@bsv/wallet-toolbox-mobile/out/src/storage/schema/tables/TableSettings.js";
|
|
4
|
+
import type { BackupManifest } from "./types";
|
|
5
|
+
type RequestSyncChunkArgs = sdk.RequestSyncChunkArgs;
|
|
6
|
+
type SyncChunk = sdk.SyncChunk;
|
|
7
|
+
type WalletStorageSyncReader = sdk.WalletStorageSyncReader;
|
|
8
|
+
/**
|
|
9
|
+
* FileRestoreReader implements WalletStorageSyncReader to serve sync chunks
|
|
10
|
+
* from a previously exported backup ZIP file during wallet restore.
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* 1. Unzip the backup file with fflate.unzip()
|
|
14
|
+
* 2. Read and parse manifest.json
|
|
15
|
+
* 3. Create FileRestoreReader with unzipped data and manifest
|
|
16
|
+
* 4. Call storage.syncFromReader(identityKey, reader)
|
|
17
|
+
* 5. Reader serves chunks sequentially via getSyncChunk()
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { unzip } from 'fflate';
|
|
22
|
+
*
|
|
23
|
+
* const zipData = new Uint8Array(await file.arrayBuffer());
|
|
24
|
+
* const unzipped = await new Promise<Unzipped>((resolve, reject) => {
|
|
25
|
+
* unzip(zipData, (err, data) => err ? reject(err) : resolve(data));
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* const manifest = JSON.parse(
|
|
29
|
+
* new TextDecoder().decode(unzipped['manifest.json'])
|
|
30
|
+
* );
|
|
31
|
+
*
|
|
32
|
+
* const reader = new FileRestoreReader(unzipped, manifest);
|
|
33
|
+
* await storage.syncFromReader(manifest.identityKey, reader);
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare class FileRestoreReader implements WalletStorageSyncReader {
|
|
37
|
+
private unzipped;
|
|
38
|
+
private manifest;
|
|
39
|
+
private currentChunkIndex;
|
|
40
|
+
private settings;
|
|
41
|
+
constructor(unzipped: Unzipped, manifest: BackupManifest);
|
|
42
|
+
/**
|
|
43
|
+
* Get the backup manifest.
|
|
44
|
+
*/
|
|
45
|
+
getManifest(): BackupManifest;
|
|
46
|
+
/**
|
|
47
|
+
* Reset the reader to start from the beginning.
|
|
48
|
+
* Call this if you need to re-read the backup.
|
|
49
|
+
*/
|
|
50
|
+
reset(): void;
|
|
51
|
+
makeAvailable(): Promise<TableSettings>;
|
|
52
|
+
/**
|
|
53
|
+
* Returns sync chunks sequentially from the backup.
|
|
54
|
+
* When all chunks are consumed, returns an empty chunk to signal completion.
|
|
55
|
+
*/
|
|
56
|
+
getSyncChunk(args: RequestSyncChunkArgs): Promise<SyncChunk>;
|
|
57
|
+
}
|
|
58
|
+
export {};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { decode } from "@msgpack/msgpack";
|
|
2
|
+
/**
|
|
3
|
+
* FileRestoreReader implements WalletStorageSyncReader to serve sync chunks
|
|
4
|
+
* from a previously exported backup ZIP file during wallet restore.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* 1. Unzip the backup file with fflate.unzip()
|
|
8
|
+
* 2. Read and parse manifest.json
|
|
9
|
+
* 3. Create FileRestoreReader with unzipped data and manifest
|
|
10
|
+
* 4. Call storage.syncFromReader(identityKey, reader)
|
|
11
|
+
* 5. Reader serves chunks sequentially via getSyncChunk()
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { unzip } from 'fflate';
|
|
16
|
+
*
|
|
17
|
+
* const zipData = new Uint8Array(await file.arrayBuffer());
|
|
18
|
+
* const unzipped = await new Promise<Unzipped>((resolve, reject) => {
|
|
19
|
+
* unzip(zipData, (err, data) => err ? reject(err) : resolve(data));
|
|
20
|
+
* });
|
|
21
|
+
*
|
|
22
|
+
* const manifest = JSON.parse(
|
|
23
|
+
* new TextDecoder().decode(unzipped['manifest.json'])
|
|
24
|
+
* );
|
|
25
|
+
*
|
|
26
|
+
* const reader = new FileRestoreReader(unzipped, manifest);
|
|
27
|
+
* await storage.syncFromReader(manifest.identityKey, reader);
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export class FileRestoreReader {
|
|
31
|
+
unzipped;
|
|
32
|
+
manifest;
|
|
33
|
+
currentChunkIndex = 0;
|
|
34
|
+
settings;
|
|
35
|
+
constructor(unzipped, manifest) {
|
|
36
|
+
this.unzipped = unzipped;
|
|
37
|
+
this.manifest = manifest;
|
|
38
|
+
// Create settings from manifest
|
|
39
|
+
this.settings = {
|
|
40
|
+
created_at: new Date(manifest.createdAt),
|
|
41
|
+
updated_at: new Date(manifest.createdAt),
|
|
42
|
+
storageIdentityKey: `backup-${manifest.identityKey.slice(0, 16)}`,
|
|
43
|
+
storageName: "FileBackup",
|
|
44
|
+
chain: manifest.chain,
|
|
45
|
+
dbtype: "IndexedDB",
|
|
46
|
+
maxOutputScript: 256,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Get the backup manifest.
|
|
51
|
+
*/
|
|
52
|
+
getManifest() {
|
|
53
|
+
return this.manifest;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Reset the reader to start from the beginning.
|
|
57
|
+
* Call this if you need to re-read the backup.
|
|
58
|
+
*/
|
|
59
|
+
reset() {
|
|
60
|
+
this.currentChunkIndex = 0;
|
|
61
|
+
}
|
|
62
|
+
// WalletStorageSyncReader interface methods
|
|
63
|
+
async makeAvailable() {
|
|
64
|
+
return this.settings;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Returns sync chunks sequentially from the backup.
|
|
68
|
+
* When all chunks are consumed, returns an empty chunk to signal completion.
|
|
69
|
+
*/
|
|
70
|
+
async getSyncChunk(args) {
|
|
71
|
+
if (this.currentChunkIndex >= this.manifest.chunkCount) {
|
|
72
|
+
// Return empty chunk to signal completion
|
|
73
|
+
return {
|
|
74
|
+
fromStorageIdentityKey: this.settings.storageIdentityKey,
|
|
75
|
+
toStorageIdentityKey: args.toStorageIdentityKey,
|
|
76
|
+
userIdentityKey: this.manifest.identityKey,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const chunkName = `chunk-${String(this.currentChunkIndex).padStart(4, "0")}.bin`;
|
|
80
|
+
const chunkData = this.unzipped[chunkName];
|
|
81
|
+
if (!chunkData) {
|
|
82
|
+
throw new Error(`Missing chunk file: ${chunkName}`);
|
|
83
|
+
}
|
|
84
|
+
const chunk = decode(chunkData);
|
|
85
|
+
this.currentChunkIndex++;
|
|
86
|
+
return chunk;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { FileBackupProvider, Zip, ZipDeflate } from "./FileBackupProvider";
|
|
2
|
+
export { FileRestoreReader } from "./FileRestoreReader";
|
|
3
|
+
export type { BackupManifest, BackupProgressEvent, BackupProgressCallback, } from "./types";
|
|
4
|
+
export { unzip } from "fflate";
|
|
5
|
+
export type { Unzipped } from "fflate";
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { sdk } from "@bsv/wallet-toolbox-mobile/out/src/index.client.js";
|
|
2
|
+
type Chain = sdk.Chain;
|
|
3
|
+
/**
|
|
4
|
+
* Manifest stored in the backup ZIP file describing the backup contents.
|
|
5
|
+
*/
|
|
6
|
+
export interface BackupManifest {
|
|
7
|
+
/** Backup format version */
|
|
8
|
+
version: 1;
|
|
9
|
+
/** ISO timestamp when backup was created */
|
|
10
|
+
createdAt: string;
|
|
11
|
+
/** Network chain (main or test) */
|
|
12
|
+
chain: Chain;
|
|
13
|
+
/** Wallet identity public key */
|
|
14
|
+
identityKey: string;
|
|
15
|
+
/** Number of sync chunks in the backup */
|
|
16
|
+
chunkCount: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Progress events emitted during backup/restore operations.
|
|
20
|
+
*/
|
|
21
|
+
export interface BackupProgressEvent {
|
|
22
|
+
stage: "preparing" | "exporting" | "downloading" | "uploading" | "importing" | "complete";
|
|
23
|
+
message: string;
|
|
24
|
+
/** Optional progress percentage (0-100) */
|
|
25
|
+
progress?: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Callback type for progress updates during backup/restore.
|
|
29
|
+
*/
|
|
30
|
+
export type BackupProgressCallback = (event: BackupProgressEvent) => void;
|
|
31
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@1sat/wallet-toolbox",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.23",
|
|
4
4
|
"description": "BSV wallet library extending @bsv/wallet-toolbox with 1Sat Ordinals protocol support",
|
|
5
5
|
"author": "1Sat Team",
|
|
6
6
|
"license": "MIT",
|
|
@@ -39,7 +39,9 @@
|
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@bopen-io/templates": "^1.1.6",
|
|
41
41
|
"@bsv/sdk": "^1.10.1",
|
|
42
|
-
"
|
|
42
|
+
"@msgpack/msgpack": "^3.1.3",
|
|
43
|
+
"buffer": "^6.0.3",
|
|
44
|
+
"fflate": "^0.8.2"
|
|
43
45
|
},
|
|
44
46
|
"peerDependencies": {
|
|
45
47
|
"@bsv/wallet-toolbox-mobile": "npm:@bopen-io/wallet-toolbox-mobile@^1.7.20-idb-fix.15"
|