@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.
- package/dist/components/SweepApp.d.ts +3 -3
- package/dist/components/SweepApp.d.ts.map +1 -1
- package/dist/components/asset-preview.d.ts +5 -5
- package/dist/components/asset-preview.d.ts.map +1 -1
- package/dist/components/connect-wallet.d.ts +1 -1
- package/dist/components/connect-wallet.d.ts.map +1 -1
- package/dist/components/opns-section.d.ts +2 -2
- package/dist/components/opns-section.d.ts.map +1 -1
- package/dist/components/sweep-progress.d.ts +1 -1
- package/dist/components/sweep-progress.d.ts.map +1 -1
- package/dist/components/tx-history.d.ts.map +1 -1
- package/dist/components/ui/badge.d.ts +3 -3
- package/dist/components/ui/badge.d.ts.map +1 -1
- package/dist/components/ui/button.d.ts +3 -3
- package/dist/components/ui/button.d.ts.map +1 -1
- package/dist/components/ui/card.d.ts +9 -9
- package/dist/components/ui/card.d.ts.map +1 -1
- package/dist/components/ui/input.d.ts +2 -2
- package/dist/components/ui/input.d.ts.map +1 -1
- package/dist/components/ui/tabs.d.ts +5 -5
- package/dist/components/ui/tabs.d.ts.map +1 -1
- package/dist/components/wif-input.d.ts +1 -1
- package/dist/components/wif-input.d.ts.map +1 -1
- package/dist/index.d.ts +19 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1911 -1757
- package/dist/lib/legacy-send.d.ts +2 -2
- package/dist/lib/legacy-send.d.ts.map +1 -1
- package/dist/lib/scanner.d.ts +3 -3
- package/dist/lib/scanner.d.ts.map +1 -1
- package/dist/lib/services.d.ts +1 -1
- package/dist/lib/services.d.ts.map +1 -1
- package/dist/lib/sweeper.d.ts +3 -3
- package/dist/lib/sweeper.d.ts.map +1 -1
- package/dist/lib/utils.d.ts +1 -1
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/wallet.d.ts +2 -2
- package/dist/lib/wallet.d.ts.map +1 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +53 -44
- package/src/components/SweepApp.tsx +480 -222
- package/src/components/asset-preview.tsx +380 -97
- package/src/components/connect-wallet.tsx +50 -25
- package/src/components/opns-section.tsx +167 -60
- package/src/components/sweep-progress.tsx +40 -17
- package/src/components/tx-history.tsx +30 -17
- package/src/components/ui/badge.tsx +17 -14
- package/src/components/ui/button.tsx +26 -22
- package/src/components/ui/card.tsx +76 -17
- package/src/components/ui/input.tsx +7 -7
- package/src/components/ui/tabs.tsx +51 -12
- package/src/components/wif-input.tsx +243 -135
- package/src/index.ts +54 -19
- package/src/lib/legacy-send.ts +110 -106
- package/src/lib/scanner.ts +45 -40
- package/src/lib/services.ts +11 -9
- package/src/lib/sweeper.ts +67 -54
- package/src/lib/utils.ts +11 -11
- package/src/lib/wallet.ts +16 -13
- package/src/types.ts +3 -3
|
@@ -1,254 +1,444 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
import
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
|
|
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({
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const [
|
|
31
|
-
const [
|
|
32
|
-
const [
|
|
33
|
-
const [
|
|
34
|
-
const [
|
|
35
|
-
const [
|
|
36
|
-
const [
|
|
37
|
-
const [
|
|
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(
|
|
51
|
-
|
|
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(
|
|
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) => [
|
|
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)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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) => {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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) => {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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 = [
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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 = [
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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(
|
|
119
|
-
toast.error(e instanceof Error ? e.message :
|
|
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(
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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(
|
|
163
|
-
const result = await executeSweep({
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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) =>
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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) =>
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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) =>
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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) =>
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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">
|
|
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
|
|
455
|
+
<ConnectWallet
|
|
456
|
+
onConnected={() => setWalletConnected(true)}
|
|
457
|
+
onDisconnected={() => setWalletConnected(false)}
|
|
458
|
+
connected={walletConnected}
|
|
459
|
+
/>
|
|
264
460
|
)}
|
|
265
461
|
|
|
266
462
|
{!initialKeys && (
|
|
267
|
-
<WifInput
|
|
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">
|
|
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
|
|
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
|
|
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
|
|
495
|
+
<TabsTrigger
|
|
496
|
+
key={tab.id}
|
|
497
|
+
value={tab.id}
|
|
498
|
+
className="flex-1 gap-1.5"
|
|
499
|
+
>
|
|
283
500
|
{tab.label}
|
|
284
|
-
<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
|
|
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
|
|
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
|
|
557
|
+
<TxHistory
|
|
558
|
+
sweeping={sweeping}
|
|
559
|
+
progress={sweepProgress}
|
|
560
|
+
history={txHistory}
|
|
561
|
+
/>
|
|
304
562
|
</div>
|
|
305
563
|
</div>
|
|
306
|
-
)
|
|
564
|
+
)
|
|
307
565
|
}
|