@1sat/sweep-ui 0.0.75 → 0.0.77

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/index.js CHANGED
@@ -281,6 +281,7 @@ import {
281
281
  sweepBsv21,
282
282
  sweepOrdinals
283
283
  } from "@1sat/actions";
284
+ var SWEEP_BATCH_SIZE = 25;
284
285
  function getOwner(output) {
285
286
  return output.events?.find((e) => e.startsWith("own:"))?.slice(4);
286
287
  }
@@ -293,13 +294,21 @@ function buildKeys(outputs, keyMap) {
293
294
  return key;
294
295
  });
295
296
  }
297
+ function chunk(items, size) {
298
+ const batches = [];
299
+ for (let i = 0;i < items.length; i += size) {
300
+ batches.push(items.slice(i, i + size));
301
+ }
302
+ return batches;
303
+ }
296
304
  async function executeSweep(params) {
297
305
  const { wallet, keys, funding, ordinals, amount, onProgress } = params;
298
306
  const ctx = createContext(wallet, { services: getServices(), chain: "main" });
299
307
  const result = {
300
308
  ordinalTxids: [],
301
309
  bsv21Txids: [],
302
- errors: []
310
+ errors: [],
311
+ sweptOutpoints: []
303
312
  };
304
313
  if (funding.length > 0) {
305
314
  onProgress(`Sweeping ${funding.length} BSV UTXOs...`);
@@ -319,22 +328,32 @@ async function executeSweep(params) {
319
328
  }
320
329
  }
321
330
  if (ordinals.length > 0) {
322
- onProgress(`Sweeping ${ordinals.length} ordinals...`);
323
- try {
324
- const inputs = await prepareSweepInputs(ctx, ordinals);
325
- const ordResult = await sweepOrdinals.execute(ctx, {
326
- inputs,
327
- keys: buildKeys(ordinals, keys)
328
- });
329
- if (ordResult.error)
330
- result.errors.push(`Ordinals: ${ordResult.error}`);
331
- else if (ordResult.txid)
332
- result.ordinalTxids.push(ordResult.txid);
333
- } catch (e) {
334
- result.errors.push(`Ordinals: ${e instanceof Error ? e.message : String(e)}`);
331
+ const batches = chunk(ordinals, SWEEP_BATCH_SIZE);
332
+ for (let b = 0;b < batches.length; b++) {
333
+ const batch = batches[b];
334
+ const from = b * SWEEP_BATCH_SIZE + 1;
335
+ const to = b * SWEEP_BATCH_SIZE + batch.length;
336
+ onProgress(batches.length === 1 ? `Sweeping ${batch.length} ordinal${batch.length !== 1 ? "s" : ""}...` : `Sweeping ordinals ${from}–${to} of ${ordinals.length} (batch ${b + 1}/${batches.length})...`);
337
+ try {
338
+ const inputs = await prepareSweepInputs(ctx, batch);
339
+ const ordResult = await sweepOrdinals.execute(ctx, {
340
+ inputs,
341
+ keys: buildKeys(batch, keys)
342
+ });
343
+ if (ordResult.error) {
344
+ result.errors.push(`Ordinals batch ${b + 1}/${batches.length}: ${ordResult.error}`);
345
+ break;
346
+ }
347
+ if (ordResult.txid)
348
+ result.ordinalTxids.push(ordResult.txid);
349
+ result.sweptOutpoints.push(...batch.map((o) => o.outpoint));
350
+ } catch (e) {
351
+ result.errors.push(`Ordinals batch ${b + 1}/${batches.length}: ${e instanceof Error ? e.message : String(e)}`);
352
+ break;
353
+ }
335
354
  }
336
355
  }
337
- onProgress("Sweep complete");
356
+ onProgress(result.errors.length > 0 ? "Sweep stopped with errors" : "Sweep complete");
338
357
  return result;
339
358
  }
340
359
  async function sweepBsv21Token(params) {
@@ -507,7 +526,6 @@ function Input({ className, type, ...props }) {
507
526
 
508
527
  // src/components/asset-preview.tsx
509
528
  import { jsx as jsx4, jsxs } from "react/jsx-runtime";
510
- var ORDINALS_PER_PAGE = 20;
511
529
  function isImageType(ct) {
512
530
  return ct.startsWith("image/") && ct !== "image/svg+xml";
513
531
  }
@@ -715,9 +733,10 @@ function OrdinalsSection({
715
733
  ordinals,
716
734
  selectedOrdinals,
717
735
  onToggle,
718
- onSelectAll,
736
+ onSelectPage,
719
737
  onDeselectAll,
720
738
  onSweep,
739
+ onSweepAll,
721
740
  onSend,
722
741
  onBurn,
723
742
  walletConnected
@@ -726,9 +745,13 @@ function OrdinalsSection({
726
745
  const [address, setAddress] = useState("");
727
746
  if (ordinals.length === 0)
728
747
  return null;
729
- const totalPages = Math.ceil(ordinals.length / ORDINALS_PER_PAGE);
730
- const start = page * ORDINALS_PER_PAGE;
731
- const pageItems = ordinals.slice(start, start + ORDINALS_PER_PAGE);
748
+ const totalPages = Math.ceil(ordinals.length / SWEEP_BATCH_SIZE);
749
+ const safePage = Math.min(page, totalPages - 1);
750
+ const start = safePage * SWEEP_BATCH_SIZE;
751
+ const pageItems = ordinals.slice(start, start + SWEEP_BATCH_SIZE);
752
+ const pageOutpoints = pageItems.map((o) => o.outpoint);
753
+ const selectedCount = selectedOrdinals.size;
754
+ const batchCount = Math.ceil(ordinals.length / SWEEP_BATCH_SIZE);
732
755
  return /* @__PURE__ */ jsxs("div", {
733
756
  className: "border border-blue-500/20 bg-blue-500/5 p-4 rounded-lg",
734
757
  children: [
@@ -755,13 +778,21 @@ function OrdinalsSection({
755
778
  ordinals.length,
756
779
  " inscription",
757
780
  ordinals.length !== 1 ? "s" : "",
758
- selectedOrdinals.size > 0 && /* @__PURE__ */ jsxs("span", {
781
+ selectedCount > 0 && /* @__PURE__ */ jsxs("span", {
759
782
  className: "text-blue-400 ml-1",
760
783
  children: [
761
784
  "(",
762
- selectedOrdinals.size,
785
+ selectedCount,
763
786
  " selected)"
764
787
  ]
788
+ }),
789
+ /* @__PURE__ */ jsxs("span", {
790
+ className: "ml-1 text-muted-foreground/80",
791
+ children: [
792
+ "· ",
793
+ SWEEP_BATCH_SIZE,
794
+ "/page"
795
+ ]
765
796
  })
766
797
  ]
767
798
  })
@@ -774,15 +805,15 @@ function OrdinalsSection({
774
805
  variant: "outline",
775
806
  size: "sm",
776
807
  className: "h-7 text-[11px]",
777
- onClick: onSelectAll,
778
- children: "Select All"
808
+ onClick: () => onSelectPage(pageOutpoints),
809
+ children: "Select page"
779
810
  }),
780
811
  /* @__PURE__ */ jsx4(Button, {
781
812
  variant: "outline",
782
813
  size: "sm",
783
814
  className: "h-7 text-[11px]",
784
815
  onClick: onDeselectAll,
785
- disabled: selectedOrdinals.size === 0,
816
+ disabled: selectedCount === 0,
786
817
  children: "Deselect"
787
818
  })
788
819
  ]
@@ -804,15 +835,15 @@ function OrdinalsSection({
804
835
  variant: "ghost",
805
836
  size: "sm",
806
837
  className: "text-xs",
807
- onClick: () => setPage(page - 1),
808
- disabled: page === 0,
838
+ onClick: () => setPage(safePage - 1),
839
+ disabled: safePage === 0,
809
840
  children: "Prev"
810
841
  }),
811
842
  /* @__PURE__ */ jsxs("span", {
812
843
  className: "text-xs text-muted-foreground",
813
844
  children: [
814
845
  "Page ",
815
- page + 1,
846
+ safePage + 1,
816
847
  " of ",
817
848
  totalPages
818
849
  ]
@@ -821,16 +852,16 @@ function OrdinalsSection({
821
852
  variant: "ghost",
822
853
  size: "sm",
823
854
  className: "text-xs",
824
- onClick: () => setPage(page + 1),
825
- disabled: page >= totalPages - 1,
855
+ onClick: () => setPage(safePage + 1),
856
+ disabled: safePage >= totalPages - 1,
826
857
  children: "Next"
827
858
  })
828
859
  ]
829
860
  }),
830
- selectedOrdinals.size > 0 && /* @__PURE__ */ jsxs("div", {
861
+ /* @__PURE__ */ jsxs("div", {
831
862
  className: "mt-3 space-y-2",
832
863
  children: [
833
- onSend && /* @__PURE__ */ jsx4(Input, {
864
+ selectedCount > 0 && onSend && /* @__PURE__ */ jsx4(Input, {
834
865
  type: "text",
835
866
  placeholder: "Destination address...",
836
867
  value: address,
@@ -838,34 +869,48 @@ function OrdinalsSection({
838
869
  className: "font-mono text-xs"
839
870
  }),
840
871
  /* @__PURE__ */ jsxs("div", {
841
- className: "flex gap-2",
872
+ className: "flex flex-wrap gap-2",
842
873
  children: [
843
- onSend && /* @__PURE__ */ jsxs(Button, {
874
+ selectedCount > 0 && onSend && /* @__PURE__ */ jsxs(Button, {
844
875
  variant: "outline",
845
876
  size: "sm",
846
- className: "flex-1",
877
+ className: "flex-1 min-w-[6rem]",
847
878
  disabled: !address.trim(),
848
879
  onClick: () => onSend(address.trim()),
849
880
  children: [
850
881
  "Send ",
851
- selectedOrdinals.size,
852
- " Ordinal",
853
- selectedOrdinals.size !== 1 ? "s" : ""
882
+ selectedCount
854
883
  ]
855
884
  }),
856
- /* @__PURE__ */ jsx4(Button, {
885
+ selectedCount > 0 && /* @__PURE__ */ jsxs(Button, {
857
886
  size: "sm",
858
- className: "flex-1",
887
+ className: "flex-1 min-w-[6rem]",
859
888
  onClick: onSweep,
860
889
  disabled: !walletConnected,
861
890
  title: walletConnected ? undefined : "Connect BRC-100 wallet to sweep",
862
- children: "Sweep to Wallet"
891
+ children: [
892
+ "Sweep ",
893
+ selectedCount,
894
+ selectedCount > SWEEP_BATCH_SIZE ? ` (${Math.ceil(selectedCount / SWEEP_BATCH_SIZE)} batches)` : ""
895
+ ]
863
896
  }),
864
- onBurn && /* @__PURE__ */ jsx4(Button, {
897
+ /* @__PURE__ */ jsxs(Button, {
898
+ variant: selectedCount > 0 ? "outline" : "default",
899
+ size: "sm",
900
+ className: "flex-1 min-w-[6rem]",
901
+ onClick: onSweepAll,
902
+ disabled: !walletConnected,
903
+ title: walletConnected ? undefined : "Connect BRC-100 wallet to sweep",
904
+ children: [
905
+ "Sweep all",
906
+ batchCount > 1 ? ` (${batchCount} batches)` : ""
907
+ ]
908
+ }),
909
+ selectedCount > 0 && onBurn && /* @__PURE__ */ jsx4(Button, {
865
910
  size: "sm",
866
911
  className: "bg-red-600 hover:bg-red-700 text-white",
867
912
  onClick: () => {
868
- if (window.confirm(`Permanently burn ${selectedOrdinals.size} ordinal${selectedOrdinals.size !== 1 ? "s" : ""}? This cannot be undone.`))
913
+ if (window.confirm(`Permanently burn ${selectedCount} ordinal${selectedCount !== 1 ? "s" : ""}? This cannot be undone.`))
869
914
  onBurn();
870
915
  },
871
916
  children: "Burn"
@@ -1202,13 +1247,15 @@ function OpnsSection({
1202
1247
  opnsNames,
1203
1248
  selectedOpns,
1204
1249
  onToggle,
1205
- onSelectAll,
1250
+ onSelectPage,
1206
1251
  onDeselectAll,
1207
1252
  onSweep,
1253
+ onSweepAll,
1208
1254
  onSend,
1209
1255
  onBurn,
1210
1256
  walletConnected
1211
1257
  }) {
1258
+ const [page, setPage] = useState3(0);
1212
1259
  const [address, setAddress] = useState3("");
1213
1260
  const [resolvedNames, setResolvedNames] = useState3(new Map);
1214
1261
  const [overlayValid, setOverlayValid] = useState3(new Map);
@@ -1263,6 +1310,13 @@ function OpnsSection({
1263
1310
  }, [opnsNames]);
1264
1311
  if (opnsNames.length === 0)
1265
1312
  return null;
1313
+ const totalPages = Math.ceil(opnsNames.length / SWEEP_BATCH_SIZE);
1314
+ const safePage = Math.min(page, totalPages - 1);
1315
+ const start = safePage * SWEEP_BATCH_SIZE;
1316
+ const pageItems = opnsNames.slice(start, start + SWEEP_BATCH_SIZE);
1317
+ const pageOutpoints = pageItems.map((o) => o.outpoint);
1318
+ const selectedCount = selectedOpns.size;
1319
+ const batchCount = Math.ceil(opnsNames.length / SWEEP_BATCH_SIZE);
1266
1320
  return /* @__PURE__ */ jsxs3("div", {
1267
1321
  className: "border border-orange-500/20 bg-orange-500/5 p-4 rounded-lg",
1268
1322
  children: [
@@ -1289,13 +1343,21 @@ function OpnsSection({
1289
1343
  opnsNames.length,
1290
1344
  " domain",
1291
1345
  opnsNames.length !== 1 ? "s" : "",
1292
- selectedOpns.size > 0 && /* @__PURE__ */ jsxs3("span", {
1346
+ selectedCount > 0 && /* @__PURE__ */ jsxs3("span", {
1293
1347
  className: "text-orange-400 ml-1",
1294
1348
  children: [
1295
1349
  "(",
1296
- selectedOpns.size,
1350
+ selectedCount,
1297
1351
  " selected)"
1298
1352
  ]
1353
+ }),
1354
+ /* @__PURE__ */ jsxs3("span", {
1355
+ className: "ml-1 text-muted-foreground/80",
1356
+ children: [
1357
+ "· ",
1358
+ SWEEP_BATCH_SIZE,
1359
+ "/page"
1360
+ ]
1299
1361
  })
1300
1362
  ]
1301
1363
  })
@@ -1308,15 +1370,15 @@ function OpnsSection({
1308
1370
  variant: "outline",
1309
1371
  size: "sm",
1310
1372
  className: "h-7 text-[11px]",
1311
- onClick: onSelectAll,
1312
- children: "Select All"
1373
+ onClick: () => onSelectPage(pageOutpoints),
1374
+ children: "Select page"
1313
1375
  }),
1314
1376
  /* @__PURE__ */ jsx6(Button, {
1315
1377
  variant: "outline",
1316
1378
  size: "sm",
1317
1379
  className: "h-7 text-[11px]",
1318
1380
  onClick: onDeselectAll,
1319
- disabled: selectedOpns.size === 0,
1381
+ disabled: selectedCount === 0,
1320
1382
  children: "Deselect"
1321
1383
  })
1322
1384
  ]
@@ -1325,7 +1387,7 @@ function OpnsSection({
1325
1387
  }),
1326
1388
  /* @__PURE__ */ jsx6("div", {
1327
1389
  className: "space-y-1",
1328
- children: opnsNames.map((item) => {
1390
+ children: pageItems.map((item) => {
1329
1391
  const isSelected = selectedOpns.has(item.outpoint);
1330
1392
  const displayName = resolvedNames.get(item.outpoint) ?? item.outpoint.substring(0, 8) + "...";
1331
1393
  return /* @__PURE__ */ jsxs3("div", {
@@ -1351,10 +1413,40 @@ function OpnsSection({
1351
1413
  }, item.outpoint);
1352
1414
  })
1353
1415
  }),
1354
- selectedOpns.size > 0 && /* @__PURE__ */ jsxs3("div", {
1416
+ totalPages > 1 && /* @__PURE__ */ jsxs3("div", {
1417
+ className: "flex items-center justify-center gap-4 mt-2",
1418
+ children: [
1419
+ /* @__PURE__ */ jsx6(Button, {
1420
+ variant: "ghost",
1421
+ size: "sm",
1422
+ className: "text-xs",
1423
+ onClick: () => setPage(safePage - 1),
1424
+ disabled: safePage === 0,
1425
+ children: "Prev"
1426
+ }),
1427
+ /* @__PURE__ */ jsxs3("span", {
1428
+ className: "text-xs text-muted-foreground",
1429
+ children: [
1430
+ "Page ",
1431
+ safePage + 1,
1432
+ " of ",
1433
+ totalPages
1434
+ ]
1435
+ }),
1436
+ /* @__PURE__ */ jsx6(Button, {
1437
+ variant: "ghost",
1438
+ size: "sm",
1439
+ className: "text-xs",
1440
+ onClick: () => setPage(safePage + 1),
1441
+ disabled: safePage >= totalPages - 1,
1442
+ children: "Next"
1443
+ })
1444
+ ]
1445
+ }),
1446
+ /* @__PURE__ */ jsxs3("div", {
1355
1447
  className: "mt-3 space-y-2",
1356
1448
  children: [
1357
- onSend && /* @__PURE__ */ jsx6(Input, {
1449
+ selectedCount > 0 && onSend && /* @__PURE__ */ jsx6(Input, {
1358
1450
  type: "text",
1359
1451
  placeholder: "Destination address...",
1360
1452
  value: address,
@@ -1362,34 +1454,48 @@ function OpnsSection({
1362
1454
  className: "font-mono text-xs"
1363
1455
  }),
1364
1456
  /* @__PURE__ */ jsxs3("div", {
1365
- className: "flex gap-2",
1457
+ className: "flex flex-wrap gap-2",
1366
1458
  children: [
1367
- onSend && /* @__PURE__ */ jsxs3(Button, {
1459
+ selectedCount > 0 && onSend && /* @__PURE__ */ jsxs3(Button, {
1368
1460
  variant: "outline",
1369
1461
  size: "sm",
1370
- className: "flex-1",
1462
+ className: "flex-1 min-w-[6rem]",
1371
1463
  disabled: !address.trim(),
1372
1464
  onClick: () => onSend(address.trim()),
1373
1465
  children: [
1374
1466
  "Send ",
1375
- selectedOpns.size,
1376
- " Domain",
1377
- selectedOpns.size !== 1 ? "s" : ""
1467
+ selectedCount
1378
1468
  ]
1379
1469
  }),
1380
- /* @__PURE__ */ jsx6(Button, {
1470
+ selectedCount > 0 && /* @__PURE__ */ jsxs3(Button, {
1381
1471
  size: "sm",
1382
- className: "flex-1",
1472
+ className: "flex-1 min-w-[6rem]",
1383
1473
  onClick: onSweep,
1384
1474
  disabled: !walletConnected,
1385
1475
  title: walletConnected ? undefined : "Connect BRC-100 wallet to sweep",
1386
- children: "Sweep to Wallet"
1476
+ children: [
1477
+ "Sweep ",
1478
+ selectedCount,
1479
+ selectedCount > SWEEP_BATCH_SIZE ? ` (${Math.ceil(selectedCount / SWEEP_BATCH_SIZE)} batches)` : ""
1480
+ ]
1481
+ }),
1482
+ /* @__PURE__ */ jsxs3(Button, {
1483
+ variant: selectedCount > 0 ? "outline" : "default",
1484
+ size: "sm",
1485
+ className: "flex-1 min-w-[6rem]",
1486
+ onClick: onSweepAll,
1487
+ disabled: !walletConnected,
1488
+ title: walletConnected ? undefined : "Connect BRC-100 wallet to sweep",
1489
+ children: [
1490
+ "Sweep all",
1491
+ batchCount > 1 ? ` (${batchCount} batches)` : ""
1492
+ ]
1387
1493
  }),
1388
- onBurn && /* @__PURE__ */ jsx6(Button, {
1494
+ selectedCount > 0 && onBurn && /* @__PURE__ */ jsx6(Button, {
1389
1495
  size: "sm",
1390
1496
  className: "bg-red-600 hover:bg-red-700 text-white",
1391
1497
  onClick: () => {
1392
- if (window.confirm(`Permanently burn ${selectedOpns.size} domain${selectedOpns.size !== 1 ? "s" : ""}? This cannot be undone.`))
1498
+ if (window.confirm(`Permanently burn ${selectedCount} domain${selectedCount !== 1 ? "s" : ""}? This cannot be undone.`))
1393
1499
  onBurn();
1394
1500
  },
1395
1501
  children: "Burn"
@@ -2163,10 +2269,9 @@ function SweepApp({
2163
2269
  return next;
2164
2270
  });
2165
2271
  }, []);
2166
- const handleSelectAllOrdinals = useCallback2(() => {
2167
- if (assets)
2168
- setSelectedOrdinals(new Set(assets.ordinals.map((o) => o.outpoint)));
2169
- }, [assets]);
2272
+ const handleSelectOrdinalPage = useCallback2((outpoints) => {
2273
+ setSelectedOrdinals(new Set(outpoints));
2274
+ }, []);
2170
2275
  const handleDeselectAllOrdinals = useCallback2(() => setSelectedOrdinals(new Set), []);
2171
2276
  const handleToggleOpns = useCallback2((outpoint) => {
2172
2277
  setSelectedOpns((prev) => {
@@ -2178,10 +2283,9 @@ function SweepApp({
2178
2283
  return next;
2179
2284
  });
2180
2285
  }, []);
2181
- const handleSelectAllOpns = useCallback2(() => {
2182
- if (assets)
2183
- setSelectedOpns(new Set(assets.opnsNames.map((o) => o.outpoint)));
2184
- }, [assets]);
2286
+ const handleSelectOpnsPage = useCallback2((outpoints) => {
2287
+ setSelectedOpns(new Set(outpoints));
2288
+ }, []);
2185
2289
  const handleDeselectAllOpns = useCallback2(() => setSelectedOpns(new Set), []);
2186
2290
  const refreshAssets = useCallback2(async () => {
2187
2291
  if (!legacyKeys)
@@ -2309,26 +2413,52 @@ function SweepApp({
2309
2413
  return result.txid;
2310
2414
  });
2311
2415
  }, [legacyKeys, assets, sweepAmount, getSelectedFunding, runOperation]);
2312
- const handleSweepOrdinals = useCallback2(async () => {
2416
+ const sweepOrdinalList = useCallback2(async (list, label) => {
2313
2417
  const wallet = resolveWallet();
2314
- if (!wallet || !legacyKeys || !assets)
2418
+ if (!wallet || !legacyKeys || list.length === 0)
2315
2419
  return;
2316
- const selected = assets.ordinals.filter((o) => selectedOrdinals.has(o.outpoint));
2317
- if (selected.length === 0)
2318
- return;
2319
- await runOperation(`Sweep ${selected.length} ordinal${selected.length !== 1 ? "s" : ""}`, async () => {
2420
+ await runOperation(label, async () => {
2320
2421
  const result = await executeSweep({
2321
2422
  wallet,
2322
2423
  keys: keyMap,
2323
2424
  funding: [],
2324
- ordinals: selected,
2425
+ ordinals: list,
2325
2426
  onProgress: setSweepProgress
2326
2427
  });
2327
- if (result.errors.length > 0)
2428
+ if (result.sweptOutpoints.length > 0) {
2429
+ setSelectedOrdinals((prev) => {
2430
+ const next = new Set(prev);
2431
+ for (const op of result.sweptOutpoints)
2432
+ next.delete(op);
2433
+ return next;
2434
+ });
2435
+ }
2436
+ if (result.errors.length > 0) {
2437
+ for (const txid of result.ordinalTxids)
2438
+ addTx(label, txid);
2439
+ if (result.sweptOutpoints.length > 0)
2440
+ await refreshAssets();
2328
2441
  throw new Error(result.errors[0]);
2329
- return result.ordinalTxids[0] ?? "";
2442
+ }
2443
+ for (let i = 0;i < result.ordinalTxids.length - 1; i++) {
2444
+ addTx(label, result.ordinalTxids[i]);
2445
+ }
2446
+ return result.ordinalTxids[result.ordinalTxids.length - 1] ?? "";
2330
2447
  });
2331
- }, [resolveWallet, legacyKeys, assets, selectedOrdinals, runOperation]);
2448
+ }, [resolveWallet, legacyKeys, keyMap, runOperation, addTx, refreshAssets]);
2449
+ const handleSweepOrdinals = useCallback2(async () => {
2450
+ if (!assets)
2451
+ return;
2452
+ const selected = assets.ordinals.filter((o) => selectedOrdinals.has(o.outpoint));
2453
+ if (selected.length === 0)
2454
+ return;
2455
+ await sweepOrdinalList(selected, `Sweep ${selected.length} ordinal${selected.length !== 1 ? "s" : ""}`);
2456
+ }, [assets, selectedOrdinals, sweepOrdinalList]);
2457
+ const handleSweepAllOrdinals = useCallback2(async () => {
2458
+ if (!assets || assets.ordinals.length === 0)
2459
+ return;
2460
+ await sweepOrdinalList(assets.ordinals, `Sweep all ${assets.ordinals.length} ordinal${assets.ordinals.length !== 1 ? "s" : ""}`);
2461
+ }, [assets, sweepOrdinalList]);
2332
2462
  const handleSendOrdinals = useCallback2(async (destination) => {
2333
2463
  if (!legacyKeys || !assets)
2334
2464
  return;
@@ -2360,26 +2490,52 @@ function SweepApp({
2360
2490
  return result.txid;
2361
2491
  });
2362
2492
  }, [legacyKeys, assets, selectedOrdinals, runOperation]);
2363
- const handleSweepOpns = useCallback2(async () => {
2493
+ const sweepOpnsList = useCallback2(async (list, label) => {
2364
2494
  const wallet = resolveWallet();
2365
- if (!wallet || !legacyKeys || !assets)
2366
- return;
2367
- const selected = assets.opnsNames.filter((o) => selectedOpns.has(o.outpoint));
2368
- if (selected.length === 0)
2495
+ if (!wallet || !legacyKeys || list.length === 0)
2369
2496
  return;
2370
- await runOperation(`Sweep ${selected.length} domain${selected.length !== 1 ? "s" : ""}`, async () => {
2497
+ await runOperation(label, async () => {
2371
2498
  const result = await executeSweep({
2372
2499
  wallet,
2373
2500
  keys: keyMap,
2374
2501
  funding: [],
2375
- ordinals: selected,
2502
+ ordinals: list,
2376
2503
  onProgress: setSweepProgress
2377
2504
  });
2378
- if (result.errors.length > 0)
2505
+ if (result.sweptOutpoints.length > 0) {
2506
+ setSelectedOpns((prev) => {
2507
+ const next = new Set(prev);
2508
+ for (const op of result.sweptOutpoints)
2509
+ next.delete(op);
2510
+ return next;
2511
+ });
2512
+ }
2513
+ if (result.errors.length > 0) {
2514
+ for (const txid of result.ordinalTxids)
2515
+ addTx(label, txid);
2516
+ if (result.sweptOutpoints.length > 0)
2517
+ await refreshAssets();
2379
2518
  throw new Error(result.errors[0]);
2380
- return result.ordinalTxids[0] ?? "";
2519
+ }
2520
+ for (let i = 0;i < result.ordinalTxids.length - 1; i++) {
2521
+ addTx(label, result.ordinalTxids[i]);
2522
+ }
2523
+ return result.ordinalTxids[result.ordinalTxids.length - 1] ?? "";
2381
2524
  });
2382
- }, [resolveWallet, legacyKeys, assets, selectedOpns, runOperation]);
2525
+ }, [resolveWallet, legacyKeys, keyMap, runOperation, addTx, refreshAssets]);
2526
+ const handleSweepOpns = useCallback2(async () => {
2527
+ if (!assets)
2528
+ return;
2529
+ const selected = assets.opnsNames.filter((o) => selectedOpns.has(o.outpoint));
2530
+ if (selected.length === 0)
2531
+ return;
2532
+ await sweepOpnsList(selected, `Sweep ${selected.length} domain${selected.length !== 1 ? "s" : ""}`);
2533
+ }, [assets, selectedOpns, sweepOpnsList]);
2534
+ const handleSweepAllOpns = useCallback2(async () => {
2535
+ if (!assets || assets.opnsNames.length === 0)
2536
+ return;
2537
+ await sweepOpnsList(assets.opnsNames, `Sweep all ${assets.opnsNames.length} domain${assets.opnsNames.length !== 1 ? "s" : ""}`);
2538
+ }, [assets, sweepOpnsList]);
2383
2539
  const handleSendOpns = useCallback2(async (destination) => {
2384
2540
  if (!legacyKeys || !assets)
2385
2541
  return;
@@ -2503,9 +2659,10 @@ function SweepApp({
2503
2659
  ordinals: assets.ordinals,
2504
2660
  selectedOrdinals,
2505
2661
  onToggle: handleToggleOrdinal,
2506
- onSelectAll: handleSelectAllOrdinals,
2662
+ onSelectPage: handleSelectOrdinalPage,
2507
2663
  onDeselectAll: handleDeselectAllOrdinals,
2508
2664
  onSweep: handleSweepOrdinals,
2665
+ onSweepAll: handleSweepAllOrdinals,
2509
2666
  onSend: sweepOnly ? undefined : handleSendOrdinals,
2510
2667
  onBurn: sweepOnly ? undefined : handleBurnOrdinals,
2511
2668
  walletConnected
@@ -2517,9 +2674,10 @@ function SweepApp({
2517
2674
  opnsNames: assets.opnsNames,
2518
2675
  selectedOpns,
2519
2676
  onToggle: handleToggleOpns,
2520
- onSelectAll: handleSelectAllOpns,
2677
+ onSelectPage: handleSelectOpnsPage,
2521
2678
  onDeselectAll: handleDeselectAllOpns,
2522
2679
  onSweep: handleSweepOpns,
2680
+ onSweepAll: handleSweepAllOpns,
2523
2681
  onSend: sweepOnly ? undefined : handleSendOpns,
2524
2682
  onBurn: sweepOnly ? undefined : handleBurnOpns,
2525
2683
  walletConnected
@@ -2698,6 +2856,7 @@ export {
2698
2856
  Tabs,
2699
2857
  SweepProgress,
2700
2858
  SweepApp,
2859
+ SWEEP_BATCH_SIZE,
2701
2860
  RunSection,
2702
2861
  OrdinalsSection,
2703
2862
  OpnsSection,
@@ -1,14 +1,19 @@
1
1
  import type { IndexedOutput } from '@1sat/types';
2
2
  import type { PrivateKey, WalletInterface } from '@bsv/sdk';
3
3
  import type { TokenBalance } from './scanner';
4
+ /** Page size, select-page size, and createAction batch size for ordinal/OpNS sweeps. */
5
+ export declare const SWEEP_BATCH_SIZE = 25;
4
6
  export interface SweepResult {
5
7
  bsvTxid?: string;
6
8
  ordinalTxids: string[];
7
9
  bsv21Txids: string[];
8
10
  errors: string[];
11
+ /** Outpoints successfully swept (ordinals/OpNS). */
12
+ sweptOutpoints: string[];
9
13
  }
10
14
  /**
11
15
  * Sweep BSV funding and ordinals into the connected wallet.
16
+ * Ordinals are processed in batches of {@link SWEEP_BATCH_SIZE}; stops on first batch error.
12
17
  */
13
18
  export declare function executeSweep(params: {
14
19
  wallet: WalletInterface;
@@ -1 +1 @@
1
- {"version":3,"file":"sweeper.d.ts","sourceRoot":"","sources":["../../src/lib/sweeper.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAG7C,MAAM,WAAW,WAAW;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,MAAM,EAAE,MAAM,EAAE,CAAA;CAChB;AAmBD;;GAEG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE;IAC1C,MAAM,EAAE,eAAe,CAAA;IACvB,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IAC7B,OAAO,EAAE,aAAa,EAAE,CAAA;IACxB,QAAQ,EAAE,aAAa,EAAE,CAAA;IACzB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACnC,GAAG,OAAO,CAAC,WAAW,CAAC,CA6CvB;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE;IAC7C,MAAM,EAAE,eAAe,CAAA;IACvB,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IAC7B,KAAK,EAAE,YAAY,CAAA;IACnB,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACnC,GAAG,OAAO,CAAC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA4B7C"}
1
+ {"version":3,"file":"sweeper.d.ts","sourceRoot":"","sources":["../../src/lib/sweeper.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAG7C,wFAAwF;AACxF,eAAO,MAAM,gBAAgB,KAAK,CAAA;AAElC,MAAM,WAAW,WAAW;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,oDAAoD;IACpD,cAAc,EAAE,MAAM,EAAE,CAAA;CACxB;AA2BD;;;GAGG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE;IAC1C,MAAM,EAAE,eAAe,CAAA;IACvB,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IAC7B,OAAO,EAAE,aAAa,EAAE,CAAA;IACxB,QAAQ,EAAE,aAAa,EAAE,CAAA;IACzB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACnC,GAAG,OAAO,CAAC,WAAW,CAAC,CAiEvB;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE;IAC7C,MAAM,EAAE,eAAe,CAAA;IACvB,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IAC7B,KAAK,EAAE,YAAY,CAAA;IACnB,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACnC,GAAG,OAAO,CAAC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA4B7C"}