@1sat/sweep-ui 0.0.19 → 0.0.21

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.
Files changed (60) hide show
  1. package/dist/components/SweepApp.d.ts +3 -3
  2. package/dist/components/SweepApp.d.ts.map +1 -1
  3. package/dist/components/asset-preview.d.ts +5 -5
  4. package/dist/components/asset-preview.d.ts.map +1 -1
  5. package/dist/components/connect-wallet.d.ts +1 -1
  6. package/dist/components/connect-wallet.d.ts.map +1 -1
  7. package/dist/components/opns-section.d.ts +2 -2
  8. package/dist/components/opns-section.d.ts.map +1 -1
  9. package/dist/components/sweep-progress.d.ts +1 -1
  10. package/dist/components/sweep-progress.d.ts.map +1 -1
  11. package/dist/components/tx-history.d.ts.map +1 -1
  12. package/dist/components/ui/badge.d.ts +3 -3
  13. package/dist/components/ui/badge.d.ts.map +1 -1
  14. package/dist/components/ui/button.d.ts +3 -3
  15. package/dist/components/ui/button.d.ts.map +1 -1
  16. package/dist/components/ui/card.d.ts +9 -9
  17. package/dist/components/ui/card.d.ts.map +1 -1
  18. package/dist/components/ui/input.d.ts +2 -2
  19. package/dist/components/ui/input.d.ts.map +1 -1
  20. package/dist/components/ui/tabs.d.ts +5 -5
  21. package/dist/components/ui/tabs.d.ts.map +1 -1
  22. package/dist/components/wif-input.d.ts +1 -1
  23. package/dist/components/wif-input.d.ts.map +1 -1
  24. package/dist/index.d.ts +19 -19
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +1911 -1757
  27. package/dist/lib/legacy-send.d.ts +2 -2
  28. package/dist/lib/legacy-send.d.ts.map +1 -1
  29. package/dist/lib/scanner.d.ts +3 -3
  30. package/dist/lib/scanner.d.ts.map +1 -1
  31. package/dist/lib/services.d.ts +1 -1
  32. package/dist/lib/services.d.ts.map +1 -1
  33. package/dist/lib/sweeper.d.ts +3 -3
  34. package/dist/lib/sweeper.d.ts.map +1 -1
  35. package/dist/lib/utils.d.ts +1 -1
  36. package/dist/lib/utils.d.ts.map +1 -1
  37. package/dist/lib/wallet.d.ts +2 -2
  38. package/dist/lib/wallet.d.ts.map +1 -1
  39. package/dist/types.d.ts.map +1 -1
  40. package/package.json +53 -44
  41. package/src/components/SweepApp.tsx +480 -222
  42. package/src/components/asset-preview.tsx +380 -97
  43. package/src/components/connect-wallet.tsx +50 -25
  44. package/src/components/opns-section.tsx +167 -60
  45. package/src/components/sweep-progress.tsx +40 -17
  46. package/src/components/tx-history.tsx +30 -17
  47. package/src/components/ui/badge.tsx +17 -14
  48. package/src/components/ui/button.tsx +26 -22
  49. package/src/components/ui/card.tsx +76 -17
  50. package/src/components/ui/input.tsx +7 -7
  51. package/src/components/ui/tabs.tsx +51 -12
  52. package/src/components/wif-input.tsx +243 -135
  53. package/src/index.ts +54 -19
  54. package/src/lib/legacy-send.ts +110 -106
  55. package/src/lib/scanner.ts +45 -40
  56. package/src/lib/services.ts +11 -9
  57. package/src/lib/sweeper.ts +67 -54
  58. package/src/lib/utils.ts +11 -11
  59. package/src/lib/wallet.ts +16 -13
  60. package/src/types.ts +3 -3
@@ -1,254 +1,444 @@
1
- import { useCallback, useEffect, useMemo, useState } from "react";
2
- import { Toaster, toast } from "sonner";
3
- import { Badge } from "./ui/badge";
4
- import { Tabs, TabsList, TabsTrigger, TabsContent } from "./ui/tabs";
5
- import { ConnectWallet } from "./connect-wallet";
6
- import { WifInput } from "./wif-input";
7
- import { FundingSection, OrdinalsSection, Bsv21Section, Bsv20Section, LockedSection, RunSection } from "./asset-preview";
8
- import { OpnsSection } from "./opns-section";
9
- import { TxHistory, type TxRecord } from "./tx-history";
10
- import { deriveAddress, scanAddresses, type ScannedAssets } from "../lib/scanner";
11
- import { executeSweep, sweepBsv21Token } from "../lib/sweeper";
12
- import { legacySendBsv, legacySendOrdinals, legacyBurnOrdinals } from "../lib/legacy-send";
13
- import { getWallet } from "../lib/wallet";
14
- import type { LegacyKeys } from "../types";
15
- import { PrivateKey, type WalletInterface } from "@bsv/sdk";
16
-
17
- type TabId = "ordinals" | "opns" | "bsv21" | "bsv20" | "locks" | "run";
1
+ import { PrivateKey, type WalletInterface } from '@bsv/sdk'
2
+ import { useCallback, useEffect, useMemo, useState } from 'react'
3
+ import { Toaster, toast } from 'sonner'
4
+ import {
5
+ legacyBurnOrdinals,
6
+ legacySendBsv,
7
+ legacySendOrdinals,
8
+ } from '../lib/legacy-send'
9
+ import {
10
+ type ScannedAssets,
11
+ deriveAddress,
12
+ scanAddresses,
13
+ } from '../lib/scanner'
14
+ import { executeSweep, sweepBsv21Token } from '../lib/sweeper'
15
+ import { getWallet } from '../lib/wallet'
16
+ import type { LegacyKeys } from '../types'
17
+ import {
18
+ Bsv20Section,
19
+ Bsv21Section,
20
+ FundingSection,
21
+ LockedSection,
22
+ OrdinalsSection,
23
+ RunSection,
24
+ } from './asset-preview'
25
+ import { ConnectWallet } from './connect-wallet'
26
+ import { OpnsSection } from './opns-section'
27
+ import { TxHistory, type TxRecord } from './tx-history'
28
+ import { Badge } from './ui/badge'
29
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from './ui/tabs'
30
+ import { WifInput } from './wif-input'
31
+
32
+ type TabId = 'ordinals' | 'opns' | 'bsv21' | 'bsv20' | 'locks' | 'run'
18
33
 
19
34
  export interface SweepAppProps {
20
- legacyKeys?: LegacyKeys;
21
- wallet?: WalletInterface | null;
22
- sweepOnly?: boolean;
35
+ legacyKeys?: LegacyKeys
36
+ wallet?: WalletInterface | null
37
+ sweepOnly?: boolean
23
38
  }
24
39
 
25
- export function SweepApp({ legacyKeys: initialKeys, wallet: externalWallet, sweepOnly }: SweepAppProps) {
26
- const [walletConnected, setWalletConnected] = useState(!!externalWallet);
27
- const [scanning, setScanning] = useState(false);
28
- const [scanProgress, setScanProgress] = useState("");
29
- const [assets, setAssets] = useState<ScannedAssets | null>(null);
30
- const [legacyKeys, setLegacyKeys] = useState<LegacyKeys | null>(null);
31
- const [sweeping, setSweeping] = useState(false);
32
- const [sweepProgress, setSweepProgress] = useState("");
33
- const [txHistory, setTxHistory] = useState<TxRecord[]>([]);
34
- const [selectedOrdinals, setSelectedOrdinals] = useState<Set<string>>(new Set());
35
- const [selectedOpns, setSelectedOpns] = useState<Set<string>>(new Set());
36
- const [sweepAmount, setSweepAmount] = useState<number | null>(null);
37
- const [activeTab, setActiveTab] = useState<TabId>("ordinals");
40
+ export function SweepApp({
41
+ legacyKeys: initialKeys,
42
+ wallet: externalWallet,
43
+ sweepOnly,
44
+ }: SweepAppProps) {
45
+ const [walletConnected, setWalletConnected] = useState(!!externalWallet)
46
+ const [scanning, setScanning] = useState(false)
47
+ const [scanProgress, setScanProgress] = useState('')
48
+ const [assets, setAssets] = useState<ScannedAssets | null>(null)
49
+ const [legacyKeys, setLegacyKeys] = useState<LegacyKeys | null>(null)
50
+ const [sweeping, setSweeping] = useState(false)
51
+ const [sweepProgress, setSweepProgress] = useState('')
52
+ const [txHistory, setTxHistory] = useState<TxRecord[]>([])
53
+ const [selectedOrdinals, setSelectedOrdinals] = useState<Set<string>>(
54
+ new Set(),
55
+ )
56
+ const [selectedOpns, setSelectedOpns] = useState<Set<string>>(new Set())
57
+ const [sweepAmount, setSweepAmount] = useState<number | null>(null)
58
+ const [activeTab, setActiveTab] = useState<TabId>('ordinals')
38
59
 
39
60
  useEffect(() => {
40
- setWalletConnected(!!externalWallet);
41
- }, [externalWallet]);
61
+ setWalletConnected(!!externalWallet)
62
+ }, [externalWallet])
42
63
 
43
64
  const resolveWallet = useCallback((): WalletInterface | null => {
44
- return externalWallet ?? getWallet();
45
- }, [externalWallet]);
65
+ return externalWallet ?? getWallet()
66
+ }, [externalWallet])
46
67
 
47
68
  const keyMap = useMemo(() => {
48
- if (!legacyKeys) return new Map<string, PrivateKey>();
49
- const map = new Map<string, PrivateKey>();
50
- map.set(deriveAddress(legacyKeys.payPk), PrivateKey.fromWif(legacyKeys.payPk));
51
- map.set(deriveAddress(legacyKeys.ordPk), PrivateKey.fromWif(legacyKeys.ordPk));
69
+ if (!legacyKeys) return new Map<string, PrivateKey>()
70
+ const map = new Map<string, PrivateKey>()
71
+ map.set(
72
+ deriveAddress(legacyKeys.payPk),
73
+ PrivateKey.fromWif(legacyKeys.payPk),
74
+ )
75
+ map.set(
76
+ deriveAddress(legacyKeys.ordPk),
77
+ PrivateKey.fromWif(legacyKeys.ordPk),
78
+ )
52
79
  if (legacyKeys.identityPk) {
53
- map.set(deriveAddress(legacyKeys.identityPk), PrivateKey.fromWif(legacyKeys.identityPk));
80
+ map.set(
81
+ deriveAddress(legacyKeys.identityPk),
82
+ PrivateKey.fromWif(legacyKeys.identityPk),
83
+ )
54
84
  }
55
- return map;
56
- }, [legacyKeys]);
85
+ return map
86
+ }, [legacyKeys])
57
87
 
58
88
  const addTx = useCallback((label: string, txid: string, error?: string) => {
59
- setTxHistory((prev) => [...prev, { label, txid, timestamp: new Date(), error }]);
60
- }, []);
89
+ setTxHistory((prev) => [
90
+ ...prev,
91
+ { label, txid, timestamp: new Date(), error },
92
+ ])
93
+ }, [])
61
94
 
62
95
  const tabs = useMemo(() => {
63
- if (!assets) return [];
64
- const t: { id: TabId; label: string; count: number }[] = [];
65
- if (assets.ordinals.length > 0) t.push({ id: "ordinals", label: "Ordinals", count: assets.ordinals.length });
66
- if (assets.opnsNames.length > 0) t.push({ id: "opns", label: "OpNS", count: assets.opnsNames.length });
67
- if (assets.bsv21Tokens.length > 0) t.push({ id: "bsv21", label: "BSV-21", count: assets.bsv21Tokens.length });
68
- if (assets.bsv20Tokens.length > 0) t.push({ id: "bsv20", label: "BSV-20", count: assets.bsv20Tokens.length });
69
- if (assets.locked.length > 0) t.push({ id: "locks", label: "Locks", count: assets.locked.length });
70
- if (assets.run.length > 0) t.push({ id: "run", label: "RUN", count: assets.run.length });
71
- return t;
72
- }, [assets]);
96
+ if (!assets) return []
97
+ const t: { id: TabId; label: string; count: number }[] = []
98
+ if (assets.ordinals.length > 0)
99
+ t.push({
100
+ id: 'ordinals',
101
+ label: 'Ordinals',
102
+ count: assets.ordinals.length,
103
+ })
104
+ if (assets.opnsNames.length > 0)
105
+ t.push({ id: 'opns', label: 'OpNS', count: assets.opnsNames.length })
106
+ if (assets.bsv21Tokens.length > 0)
107
+ t.push({ id: 'bsv21', label: 'BSV-21', count: assets.bsv21Tokens.length })
108
+ if (assets.bsv20Tokens.length > 0)
109
+ t.push({ id: 'bsv20', label: 'BSV-20', count: assets.bsv20Tokens.length })
110
+ if (assets.locked.length > 0)
111
+ t.push({ id: 'locks', label: 'Locks', count: assets.locked.length })
112
+ if (assets.run.length > 0)
113
+ t.push({ id: 'run', label: 'RUN', count: assets.run.length })
114
+ return t
115
+ }, [assets])
73
116
 
74
117
  const handleToggleOrdinal = useCallback((outpoint: string) => {
75
- setSelectedOrdinals((prev) => { const next = new Set(prev); if (next.has(outpoint)) next.delete(outpoint); else next.add(outpoint); return next; });
76
- }, []);
77
- const handleSelectAllOrdinals = useCallback(() => { if (assets) setSelectedOrdinals(new Set(assets.ordinals.map((o) => o.outpoint))); }, [assets]);
78
- const handleDeselectAllOrdinals = useCallback(() => setSelectedOrdinals(new Set()), []);
118
+ setSelectedOrdinals((prev) => {
119
+ const next = new Set(prev)
120
+ if (next.has(outpoint)) next.delete(outpoint)
121
+ else next.add(outpoint)
122
+ return next
123
+ })
124
+ }, [])
125
+ const handleSelectAllOrdinals = useCallback(() => {
126
+ if (assets)
127
+ setSelectedOrdinals(new Set(assets.ordinals.map((o) => o.outpoint)))
128
+ }, [assets])
129
+ const handleDeselectAllOrdinals = useCallback(
130
+ () => setSelectedOrdinals(new Set()),
131
+ [],
132
+ )
79
133
 
80
134
  const handleToggleOpns = useCallback((outpoint: string) => {
81
- setSelectedOpns((prev) => { const next = new Set(prev); if (next.has(outpoint)) next.delete(outpoint); else next.add(outpoint); return next; });
82
- }, []);
83
- const handleSelectAllOpns = useCallback(() => { if (assets) setSelectedOpns(new Set(assets.opnsNames.map((o) => o.outpoint))); }, [assets]);
84
- const handleDeselectAllOpns = useCallback(() => setSelectedOpns(new Set()), []);
135
+ setSelectedOpns((prev) => {
136
+ const next = new Set(prev)
137
+ if (next.has(outpoint)) next.delete(outpoint)
138
+ else next.add(outpoint)
139
+ return next
140
+ })
141
+ }, [])
142
+ const handleSelectAllOpns = useCallback(() => {
143
+ if (assets)
144
+ setSelectedOpns(new Set(assets.opnsNames.map((o) => o.outpoint)))
145
+ }, [assets])
146
+ const handleDeselectAllOpns = useCallback(
147
+ () => setSelectedOpns(new Set()),
148
+ [],
149
+ )
85
150
 
86
151
  const refreshAssets = useCallback(async () => {
87
- if (!legacyKeys) return;
88
- const addresses = [...new Set([deriveAddress(legacyKeys.payPk), deriveAddress(legacyKeys.ordPk), ...(legacyKeys.identityPk ? [deriveAddress(legacyKeys.identityPk)] : [])])];
89
- const result = await scanAddresses(addresses);
90
- setAssets(result);
91
- setSelectedOrdinals(new Set());
92
- setSelectedOpns(new Set());
93
- setSweepAmount(null);
94
- }, [legacyKeys]);
152
+ if (!legacyKeys) return
153
+ const addresses = [
154
+ ...new Set([
155
+ deriveAddress(legacyKeys.payPk),
156
+ deriveAddress(legacyKeys.ordPk),
157
+ ...(legacyKeys.identityPk
158
+ ? [deriveAddress(legacyKeys.identityPk)]
159
+ : []),
160
+ ]),
161
+ ]
162
+ const result = await scanAddresses(addresses)
163
+ setAssets(result)
164
+ setSelectedOrdinals(new Set())
165
+ setSelectedOpns(new Set())
166
+ setSweepAmount(null)
167
+ }, [legacyKeys])
95
168
 
96
169
  const handleScan = useCallback(async (keys: LegacyKeys) => {
97
- setScanning(true);
98
- setAssets(null);
99
- setSelectedOrdinals(new Set());
100
- setSelectedOpns(new Set());
101
- setSweepAmount(null);
102
- setLegacyKeys(keys);
170
+ setScanning(true)
171
+ setAssets(null)
172
+ setSelectedOrdinals(new Set())
173
+ setSelectedOpns(new Set())
174
+ setSweepAmount(null)
175
+ setLegacyKeys(keys)
103
176
 
104
177
  try {
105
- const addresses = [...new Set([deriveAddress(keys.payPk), deriveAddress(keys.ordPk), ...(keys.identityPk ? [deriveAddress(keys.identityPk)] : [])])];
106
- const result = await scanAddresses(addresses, (p) => setScanProgress(p.detail ?? p.phase));
107
- setAssets(result);
108
-
109
- const total = result.funding.length + result.ordinals.length + result.opnsNames.length + result.bsv21Tokens.reduce((n, t) => n + t.outputs.length, 0) + result.bsv20Tokens.length + result.locked.length + result.run.length;
110
- if (total === 0) toast.info("No assets found at legacy addresses");
111
-
112
- if (result.ordinals.length > 0) setActiveTab("ordinals");
113
- else if (result.opnsNames.length > 0) setActiveTab("opns");
114
- else if (result.bsv21Tokens.length > 0) setActiveTab("bsv21");
115
- else if (result.bsv20Tokens.length > 0) setActiveTab("bsv20");
116
- else if (result.locked.length > 0) setActiveTab("locks");
178
+ const addresses = [
179
+ ...new Set([
180
+ deriveAddress(keys.payPk),
181
+ deriveAddress(keys.ordPk),
182
+ ...(keys.identityPk ? [deriveAddress(keys.identityPk)] : []),
183
+ ]),
184
+ ]
185
+ const result = await scanAddresses(addresses, (p) =>
186
+ setScanProgress(p.detail ?? p.phase),
187
+ )
188
+ setAssets(result)
189
+
190
+ const total =
191
+ result.funding.length +
192
+ result.ordinals.length +
193
+ result.opnsNames.length +
194
+ result.bsv21Tokens.reduce((n, t) => n + t.outputs.length, 0) +
195
+ result.bsv20Tokens.length +
196
+ result.locked.length +
197
+ result.run.length
198
+ if (total === 0) toast.info('No assets found at legacy addresses')
199
+
200
+ if (result.ordinals.length > 0) setActiveTab('ordinals')
201
+ else if (result.opnsNames.length > 0) setActiveTab('opns')
202
+ else if (result.bsv21Tokens.length > 0) setActiveTab('bsv21')
203
+ else if (result.bsv20Tokens.length > 0) setActiveTab('bsv20')
204
+ else if (result.locked.length > 0) setActiveTab('locks')
117
205
  } catch (e) {
118
- console.error("Scan failed:", e);
119
- toast.error(e instanceof Error ? e.message : "Scan failed");
206
+ console.error('Scan failed:', e)
207
+ toast.error(e instanceof Error ? e.message : 'Scan failed')
120
208
  } finally {
121
- setScanning(false);
209
+ setScanning(false)
122
210
  }
123
- }, []);
211
+ }, [])
124
212
 
125
213
  useEffect(() => {
126
- if (initialKeys) handleScan(initialKeys);
127
- }, [initialKeys, handleScan]);
128
-
129
- const runOperation = useCallback(async (label: string, op: () => Promise<string>) => {
130
- setSweeping(true);
131
- setSweepProgress(label + "...");
132
- try {
133
- const txid = await op();
134
- addTx(label, txid);
135
- toast.success(label);
136
- await refreshAssets();
137
- } catch (e) {
138
- const msg = e instanceof Error ? e.message : "Operation failed";
139
- addTx(label, "", msg);
140
- toast.error(msg);
141
- } finally {
142
- setSweeping(false);
143
- }
144
- }, [addTx, refreshAssets]);
214
+ if (initialKeys) handleScan(initialKeys)
215
+ }, [initialKeys, handleScan])
216
+
217
+ const runOperation = useCallback(
218
+ async (label: string, op: () => Promise<string>) => {
219
+ setSweeping(true)
220
+ setSweepProgress(label + '...')
221
+ try {
222
+ const txid = await op()
223
+ addTx(label, txid)
224
+ toast.success(label)
225
+ await refreshAssets()
226
+ } catch (e) {
227
+ const msg = e instanceof Error ? e.message : 'Operation failed'
228
+ addTx(label, '', msg)
229
+ toast.error(msg)
230
+ } finally {
231
+ setSweeping(false)
232
+ }
233
+ },
234
+ [addTx, refreshAssets],
235
+ )
145
236
 
146
237
  const getSelectedFunding = useCallback(() => {
147
- if (!assets) return [];
148
- if (sweepAmount === null) return assets.funding;
149
- const selected: typeof assets.funding = [];
150
- let accumulated = 0;
238
+ if (!assets) return []
239
+ if (sweepAmount === null) return assets.funding
240
+ const selected: typeof assets.funding = []
241
+ let accumulated = 0
151
242
  for (const utxo of assets.funding) {
152
- selected.push(utxo);
153
- accumulated += utxo.satoshis ?? 0;
154
- if (accumulated >= sweepAmount) break;
243
+ selected.push(utxo)
244
+ accumulated += utxo.satoshis ?? 0
245
+ if (accumulated >= sweepAmount) break
155
246
  }
156
- return selected;
157
- }, [assets, sweepAmount]);
247
+ return selected
248
+ }, [assets, sweepAmount])
158
249
 
159
250
  const handleSweepBsv = useCallback(async () => {
160
- const wallet = resolveWallet();
161
- if (!wallet || !legacyKeys || !assets) return;
162
- await runOperation("Sweep BSV", async () => {
163
- const result = await executeSweep({ wallet, keys: keyMap, funding: getSelectedFunding(), ordinals: [], amount: sweepAmount ?? undefined, onProgress: setSweepProgress });
164
- if (result.errors.length > 0) throw new Error(result.errors[0]);
165
- return result.bsvTxid ?? "";
166
- });
167
- }, [resolveWallet, legacyKeys, assets, sweepAmount, getSelectedFunding, runOperation]);
168
-
169
- const handleSendBsv = useCallback(async (destination: string) => {
170
- if (!legacyKeys || !assets) return;
171
- await runOperation("Send BSV", async () => {
172
- const result = await legacySendBsv({ funding: getSelectedFunding(), keys: legacyKeys, destination, amount: sweepAmount ?? undefined });
173
- return result.txid;
174
- });
175
- }, [legacyKeys, assets, sweepAmount, getSelectedFunding, runOperation]);
251
+ const wallet = resolveWallet()
252
+ if (!wallet || !legacyKeys || !assets) return
253
+ await runOperation('Sweep BSV', async () => {
254
+ const result = await executeSweep({
255
+ wallet,
256
+ keys: keyMap,
257
+ funding: getSelectedFunding(),
258
+ ordinals: [],
259
+ amount: sweepAmount ?? undefined,
260
+ onProgress: setSweepProgress,
261
+ })
262
+ if (result.errors.length > 0) throw new Error(result.errors[0])
263
+ return result.bsvTxid ?? ''
264
+ })
265
+ }, [
266
+ resolveWallet,
267
+ legacyKeys,
268
+ assets,
269
+ sweepAmount,
270
+ getSelectedFunding,
271
+ runOperation,
272
+ ])
273
+
274
+ const handleSendBsv = useCallback(
275
+ async (destination: string) => {
276
+ if (!legacyKeys || !assets) return
277
+ await runOperation('Send BSV', async () => {
278
+ const result = await legacySendBsv({
279
+ funding: getSelectedFunding(),
280
+ keys: legacyKeys,
281
+ destination,
282
+ amount: sweepAmount ?? undefined,
283
+ })
284
+ return result.txid
285
+ })
286
+ },
287
+ [legacyKeys, assets, sweepAmount, getSelectedFunding, runOperation],
288
+ )
176
289
 
177
290
  const handleSweepOrdinals = useCallback(async () => {
178
- const wallet = resolveWallet();
179
- if (!wallet || !legacyKeys || !assets) return;
180
- const selected = assets.ordinals.filter((o) => selectedOrdinals.has(o.outpoint));
181
- if (selected.length === 0) return;
182
- await runOperation(`Sweep ${selected.length} ordinal${selected.length !== 1 ? "s" : ""}`, async () => {
183
- const result = await executeSweep({ wallet, keys: keyMap, funding: [], ordinals: selected, onProgress: setSweepProgress });
184
- if (result.errors.length > 0) throw new Error(result.errors[0]);
185
- return result.ordinalTxids[0] ?? "";
186
- });
187
- }, [resolveWallet, legacyKeys, assets, selectedOrdinals, runOperation]);
188
-
189
- const handleSendOrdinals = useCallback(async (destination: string) => {
190
- if (!legacyKeys || !assets) return;
191
- const selected = assets.ordinals.filter((o) => selectedOrdinals.has(o.outpoint));
192
- if (selected.length === 0) return;
193
- await runOperation(`Send ${selected.length} ordinal${selected.length !== 1 ? "s" : ""}`, async () => {
194
- const result = await legacySendOrdinals({ ordinals: selected, funding: assets.funding, keys: legacyKeys, destination });
195
- return result.txid;
196
- });
197
- }, [legacyKeys, assets, selectedOrdinals, runOperation]);
291
+ const wallet = resolveWallet()
292
+ if (!wallet || !legacyKeys || !assets) return
293
+ const selected = assets.ordinals.filter((o) =>
294
+ selectedOrdinals.has(o.outpoint),
295
+ )
296
+ if (selected.length === 0) return
297
+ await runOperation(
298
+ `Sweep ${selected.length} ordinal${selected.length !== 1 ? 's' : ''}`,
299
+ async () => {
300
+ const result = await executeSweep({
301
+ wallet,
302
+ keys: keyMap,
303
+ funding: [],
304
+ ordinals: selected,
305
+ onProgress: setSweepProgress,
306
+ })
307
+ if (result.errors.length > 0) throw new Error(result.errors[0])
308
+ return result.ordinalTxids[0] ?? ''
309
+ },
310
+ )
311
+ }, [resolveWallet, legacyKeys, assets, selectedOrdinals, runOperation])
312
+
313
+ const handleSendOrdinals = useCallback(
314
+ async (destination: string) => {
315
+ if (!legacyKeys || !assets) return
316
+ const selected = assets.ordinals.filter((o) =>
317
+ selectedOrdinals.has(o.outpoint),
318
+ )
319
+ if (selected.length === 0) return
320
+ await runOperation(
321
+ `Send ${selected.length} ordinal${selected.length !== 1 ? 's' : ''}`,
322
+ async () => {
323
+ const result = await legacySendOrdinals({
324
+ ordinals: selected,
325
+ funding: assets.funding,
326
+ keys: legacyKeys,
327
+ destination,
328
+ })
329
+ return result.txid
330
+ },
331
+ )
332
+ },
333
+ [legacyKeys, assets, selectedOrdinals, runOperation],
334
+ )
198
335
 
199
336
  const handleBurnOrdinals = useCallback(async () => {
200
- if (!legacyKeys || !assets) return;
201
- const selected = assets.ordinals.filter((o) => selectedOrdinals.has(o.outpoint));
202
- if (selected.length === 0) return;
203
- await runOperation(`Burn ${selected.length} ordinal${selected.length !== 1 ? "s" : ""}`, async () => {
204
- const result = await legacyBurnOrdinals({ ordinals: selected, funding: assets.funding, keys: legacyKeys });
205
- return result.txid;
206
- });
207
- }, [legacyKeys, assets, selectedOrdinals, runOperation]);
337
+ if (!legacyKeys || !assets) return
338
+ const selected = assets.ordinals.filter((o) =>
339
+ selectedOrdinals.has(o.outpoint),
340
+ )
341
+ if (selected.length === 0) return
342
+ await runOperation(
343
+ `Burn ${selected.length} ordinal${selected.length !== 1 ? 's' : ''}`,
344
+ async () => {
345
+ const result = await legacyBurnOrdinals({
346
+ ordinals: selected,
347
+ funding: assets.funding,
348
+ keys: legacyKeys,
349
+ })
350
+ return result.txid
351
+ },
352
+ )
353
+ }, [legacyKeys, assets, selectedOrdinals, runOperation])
208
354
 
209
355
  const handleSweepOpns = useCallback(async () => {
210
- const wallet = resolveWallet();
211
- if (!wallet || !legacyKeys || !assets) return;
212
- const selected = assets.opnsNames.filter((o) => selectedOpns.has(o.outpoint));
213
- if (selected.length === 0) return;
214
- await runOperation(`Sweep ${selected.length} domain${selected.length !== 1 ? "s" : ""}`, async () => {
215
- const result = await executeSweep({ wallet, keys: keyMap, funding: [], ordinals: selected, onProgress: setSweepProgress });
216
- if (result.errors.length > 0) throw new Error(result.errors[0]);
217
- return result.ordinalTxids[0] ?? "";
218
- });
219
- }, [resolveWallet, legacyKeys, assets, selectedOpns, runOperation]);
220
-
221
- const handleSendOpns = useCallback(async (destination: string) => {
222
- if (!legacyKeys || !assets) return;
223
- const selected = assets.opnsNames.filter((o) => selectedOpns.has(o.outpoint));
224
- if (selected.length === 0) return;
225
- await runOperation(`Send ${selected.length} domain${selected.length !== 1 ? "s" : ""}`, async () => {
226
- const result = await legacySendOrdinals({ ordinals: selected, funding: assets.funding, keys: legacyKeys, destination });
227
- return result.txid;
228
- });
229
- }, [legacyKeys, assets, selectedOpns, runOperation]);
356
+ const wallet = resolveWallet()
357
+ if (!wallet || !legacyKeys || !assets) return
358
+ const selected = assets.opnsNames.filter((o) =>
359
+ selectedOpns.has(o.outpoint),
360
+ )
361
+ if (selected.length === 0) return
362
+ await runOperation(
363
+ `Sweep ${selected.length} domain${selected.length !== 1 ? 's' : ''}`,
364
+ async () => {
365
+ const result = await executeSweep({
366
+ wallet,
367
+ keys: keyMap,
368
+ funding: [],
369
+ ordinals: selected,
370
+ onProgress: setSweepProgress,
371
+ })
372
+ if (result.errors.length > 0) throw new Error(result.errors[0])
373
+ return result.ordinalTxids[0] ?? ''
374
+ },
375
+ )
376
+ }, [resolveWallet, legacyKeys, assets, selectedOpns, runOperation])
377
+
378
+ const handleSendOpns = useCallback(
379
+ async (destination: string) => {
380
+ if (!legacyKeys || !assets) return
381
+ const selected = assets.opnsNames.filter((o) =>
382
+ selectedOpns.has(o.outpoint),
383
+ )
384
+ if (selected.length === 0) return
385
+ await runOperation(
386
+ `Send ${selected.length} domain${selected.length !== 1 ? 's' : ''}`,
387
+ async () => {
388
+ const result = await legacySendOrdinals({
389
+ ordinals: selected,
390
+ funding: assets.funding,
391
+ keys: legacyKeys,
392
+ destination,
393
+ })
394
+ return result.txid
395
+ },
396
+ )
397
+ },
398
+ [legacyKeys, assets, selectedOpns, runOperation],
399
+ )
230
400
 
231
401
  const handleBurnOpns = useCallback(async () => {
232
- if (!legacyKeys || !assets) return;
233
- const selected = assets.opnsNames.filter((o) => selectedOpns.has(o.outpoint));
234
- if (selected.length === 0) return;
235
- await runOperation(`Burn ${selected.length} domain${selected.length !== 1 ? "s" : ""}`, async () => {
236
- const result = await legacyBurnOrdinals({ ordinals: selected, funding: assets.funding, keys: legacyKeys });
237
- return result.txid;
238
- });
239
- }, [legacyKeys, assets, selectedOpns, runOperation]);
240
-
241
- const handleSweepBsv21Token = useCallback(async (tokenId: string) => {
242
- const wallet = resolveWallet();
243
- if (!wallet || !assets) return;
244
- const token = assets.bsv21Tokens.find((t) => t.tokenId === tokenId);
245
- if (!token) return;
246
- await runOperation(`Sweep ${token.symbol ?? tokenId.slice(0, 8)}`, async () => {
247
- const result = await sweepBsv21Token({ wallet, keys: keyMap, token, onProgress: setSweepProgress });
248
- if (result.error) throw new Error(result.error);
249
- return result.txid ?? "";
250
- });
251
- }, [resolveWallet, assets, keyMap, runOperation]);
402
+ if (!legacyKeys || !assets) return
403
+ const selected = assets.opnsNames.filter((o) =>
404
+ selectedOpns.has(o.outpoint),
405
+ )
406
+ if (selected.length === 0) return
407
+ await runOperation(
408
+ `Burn ${selected.length} domain${selected.length !== 1 ? 's' : ''}`,
409
+ async () => {
410
+ const result = await legacyBurnOrdinals({
411
+ ordinals: selected,
412
+ funding: assets.funding,
413
+ keys: legacyKeys,
414
+ })
415
+ return result.txid
416
+ },
417
+ )
418
+ }, [legacyKeys, assets, selectedOpns, runOperation])
419
+
420
+ const handleSweepBsv21Token = useCallback(
421
+ async (tokenId: string) => {
422
+ const wallet = resolveWallet()
423
+ if (!wallet || !assets) return
424
+ const token = assets.bsv21Tokens.find((t) => t.tokenId === tokenId)
425
+ if (!token) return
426
+ await runOperation(
427
+ `Sweep ${token.symbol ?? tokenId.slice(0, 8)}`,
428
+ async () => {
429
+ const result = await sweepBsv21Token({
430
+ wallet,
431
+ keys: keyMap,
432
+ token,
433
+ onProgress: setSweepProgress,
434
+ })
435
+ if (result.error) throw new Error(result.error)
436
+ return result.txid ?? ''
437
+ },
438
+ )
439
+ },
440
+ [resolveWallet, assets, keyMap, runOperation],
441
+ )
252
442
 
253
443
  return (
254
444
  <div className="min-h-screen bg-background text-foreground">
@@ -256,52 +446,120 @@ export function SweepApp({ legacyKeys: initialKeys, wallet: externalWallet, swee
256
446
  <div className="mx-auto max-w-lg p-4 space-y-4 py-12">
257
447
  <div className="text-center space-y-2 mb-4">
258
448
  <h1 className="text-3xl font-bold tracking-tight">1Sat Sweep</h1>
259
- <p className="text-sm text-muted-foreground">Transfer or sweep legacy assets</p>
449
+ <p className="text-sm text-muted-foreground">
450
+ Transfer or sweep legacy assets
451
+ </p>
260
452
  </div>
261
453
 
262
454
  {!externalWallet && (
263
- <ConnectWallet onConnected={() => setWalletConnected(true)} onDisconnected={() => setWalletConnected(false)} connected={walletConnected} />
455
+ <ConnectWallet
456
+ onConnected={() => setWalletConnected(true)}
457
+ onDisconnected={() => setWalletConnected(false)}
458
+ connected={walletConnected}
459
+ />
264
460
  )}
265
461
 
266
462
  {!initialKeys && (
267
- <WifInput onScan={handleScan} scanning={scanning} disabled={sweeping} />
463
+ <WifInput
464
+ onScan={handleScan}
465
+ scanning={scanning}
466
+ disabled={sweeping}
467
+ />
268
468
  )}
269
469
 
270
470
  {scanning && (
271
- <p className="text-sm text-center text-muted-foreground animate-pulse">{scanProgress}</p>
471
+ <p className="text-sm text-center text-muted-foreground animate-pulse">
472
+ {scanProgress}
473
+ </p>
272
474
  )}
273
475
 
274
476
  {assets && !sweeping && (
275
477
  <div className="space-y-3">
276
- <FundingSection funding={assets.funding} totalBsv={assets.totalBsv} sweepAmount={sweepAmount} onSweepAmountChange={setSweepAmount} onSweep={handleSweepBsv} onSend={sweepOnly ? undefined : handleSendBsv} walletConnected={walletConnected} />
478
+ <FundingSection
479
+ funding={assets.funding}
480
+ totalBsv={assets.totalBsv}
481
+ sweepAmount={sweepAmount}
482
+ onSweepAmountChange={setSweepAmount}
483
+ onSweep={handleSweepBsv}
484
+ onSend={sweepOnly ? undefined : handleSendBsv}
485
+ walletConnected={walletConnected}
486
+ />
277
487
 
278
488
  {tabs.length > 0 && (
279
- <Tabs value={activeTab} onValueChange={(v) => setActiveTab(v as TabId)}>
489
+ <Tabs
490
+ value={activeTab}
491
+ onValueChange={(v) => setActiveTab(v as TabId)}
492
+ >
280
493
  <TabsList className="w-full">
281
494
  {tabs.map((tab) => (
282
- <TabsTrigger key={tab.id} value={tab.id} className="flex-1 gap-1.5">
495
+ <TabsTrigger
496
+ key={tab.id}
497
+ value={tab.id}
498
+ className="flex-1 gap-1.5"
499
+ >
283
500
  {tab.label}
284
- <Badge variant="secondary" className="text-[10px] px-1.5 py-0">{tab.count}</Badge>
501
+ <Badge
502
+ variant="secondary"
503
+ className="text-[10px] px-1.5 py-0"
504
+ >
505
+ {tab.count}
506
+ </Badge>
285
507
  </TabsTrigger>
286
508
  ))}
287
509
  </TabsList>
288
510
  <TabsContent value="ordinals">
289
- <OrdinalsSection ordinals={assets.ordinals} selectedOrdinals={selectedOrdinals} onToggle={handleToggleOrdinal} onSelectAll={handleSelectAllOrdinals} onDeselectAll={handleDeselectAllOrdinals} onSweep={handleSweepOrdinals} onSend={sweepOnly ? undefined : handleSendOrdinals} onBurn={sweepOnly ? undefined : handleBurnOrdinals} walletConnected={walletConnected} />
511
+ <OrdinalsSection
512
+ ordinals={assets.ordinals}
513
+ selectedOrdinals={selectedOrdinals}
514
+ onToggle={handleToggleOrdinal}
515
+ onSelectAll={handleSelectAllOrdinals}
516
+ onDeselectAll={handleDeselectAllOrdinals}
517
+ onSweep={handleSweepOrdinals}
518
+ onSend={sweepOnly ? undefined : handleSendOrdinals}
519
+ onBurn={sweepOnly ? undefined : handleBurnOrdinals}
520
+ walletConnected={walletConnected}
521
+ />
290
522
  </TabsContent>
291
523
  <TabsContent value="opns">
292
- <OpnsSection opnsNames={assets.opnsNames} selectedOpns={selectedOpns} onToggle={handleToggleOpns} onSelectAll={handleSelectAllOpns} onDeselectAll={handleDeselectAllOpns} onSweep={handleSweepOpns} onSend={sweepOnly ? undefined : handleSendOpns} onBurn={sweepOnly ? undefined : handleBurnOpns} walletConnected={walletConnected} />
524
+ <OpnsSection
525
+ opnsNames={assets.opnsNames}
526
+ selectedOpns={selectedOpns}
527
+ onToggle={handleToggleOpns}
528
+ onSelectAll={handleSelectAllOpns}
529
+ onDeselectAll={handleDeselectAllOpns}
530
+ onSweep={handleSweepOpns}
531
+ onSend={sweepOnly ? undefined : handleSendOpns}
532
+ onBurn={sweepOnly ? undefined : handleBurnOpns}
533
+ walletConnected={walletConnected}
534
+ />
535
+ </TabsContent>
536
+ <TabsContent value="bsv21">
537
+ <Bsv21Section
538
+ tokens={assets.bsv21Tokens}
539
+ onSweep={handleSweepBsv21Token}
540
+ walletConnected={walletConnected}
541
+ />
542
+ </TabsContent>
543
+ <TabsContent value="bsv20">
544
+ <Bsv20Section tokens={assets.bsv20Tokens} />
545
+ </TabsContent>
546
+ <TabsContent value="locks">
547
+ <LockedSection locked={assets.locked} />
548
+ </TabsContent>
549
+ <TabsContent value="run">
550
+ <RunSection run={assets.run} />
293
551
  </TabsContent>
294
- <TabsContent value="bsv21"><Bsv21Section tokens={assets.bsv21Tokens} onSweep={handleSweepBsv21Token} walletConnected={walletConnected} /></TabsContent>
295
- <TabsContent value="bsv20"><Bsv20Section tokens={assets.bsv20Tokens} /></TabsContent>
296
- <TabsContent value="locks"><LockedSection locked={assets.locked} /></TabsContent>
297
- <TabsContent value="run"><RunSection run={assets.run} /></TabsContent>
298
552
  </Tabs>
299
553
  )}
300
554
  </div>
301
555
  )}
302
556
 
303
- <TxHistory sweeping={sweeping} progress={sweepProgress} history={txHistory} />
557
+ <TxHistory
558
+ sweeping={sweeping}
559
+ progress={sweepProgress}
560
+ history={txHistory}
561
+ />
304
562
  </div>
305
563
  </div>
306
- );
564
+ )
307
565
  }