@1sat/sweep-ui 0.0.95 → 0.0.97
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.map +1 -1
- package/dist/components/asset-preview.d.ts +6 -7
- package/dist/components/asset-preview.d.ts.map +1 -1
- package/dist/components/opns-section.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +284 -178
- package/dist/lib/scanner.d.ts +1 -2
- package/dist/lib/scanner.d.ts.map +1 -1
- package/dist/lib/sweeper.d.ts +29 -13
- package/dist/lib/sweeper.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/components/SweepApp.tsx +109 -61
- package/src/components/asset-preview.tsx +67 -62
- package/src/components/opns-section.tsx +6 -0
- package/src/index.ts +1 -1
- package/src/lib/scanner.ts +1 -3
- package/src/lib/sweeper.ts +143 -57
package/dist/lib/scanner.d.ts
CHANGED
|
@@ -7,10 +7,9 @@ export interface EnrichedOrdinal extends IndexedOutput {
|
|
|
7
7
|
name?: string;
|
|
8
8
|
contentUrl: string;
|
|
9
9
|
}
|
|
10
|
-
export interface ScannedAssets extends Omit<ScanResult, 'ordinals' | 'opnsNames'> {
|
|
10
|
+
export interface ScannedAssets extends Omit<ScanResult, 'ordinals' | 'opnsNames' | 'listings'> {
|
|
11
11
|
ordinals: EnrichedOrdinal[];
|
|
12
12
|
opnsNames: EnrichedOrdinal[];
|
|
13
|
-
listings: EnrichedOrdinal[];
|
|
14
13
|
bsv21Tokens: TokenBalance[];
|
|
15
14
|
totalBsv: number;
|
|
16
15
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/lib/scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,YAAY,EAEjB,MAAM,eAAe,CAAA;AACtB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAIhD,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,CAAA;AAEtD,MAAM,WAAW,eAAgB,SAAQ,aAAa;IACrD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,aAChB,SAAQ,IAAI,CAAC,UAAU,EAAE,UAAU,GAAG,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/lib/scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,YAAY,EAEjB,MAAM,eAAe,CAAA;AACtB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAIhD,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,CAAA;AAEtD,MAAM,WAAW,eAAgB,SAAQ,aAAa;IACrD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,aAChB,SAAQ,IAAI,CAAC,UAAU,EAAE,UAAU,GAAG,WAAW,GAAG,UAAU,CAAC;IAC/D,QAAQ,EAAE,eAAe,EAAE,CAAA;IAC3B,SAAS,EAAE,eAAe,EAAE,CAAA;IAC5B,WAAW,EAAE,YAAY,EAAE,CAAA;IAC3B,QAAQ,EAAE,MAAM,CAAA;CAChB;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEjD;AA2CD,wBAAsB,WAAW,CAChC,OAAO,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,YAAY,KAAK,IAAI,GACpC,OAAO,CAAC,aAAa,CAAC,CAGxB;AAED,wBAAsB,aAAa,CAClC,SAAS,EAAE,MAAM,EAAE,EACnB,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,YAAY,KAAK,IAAI,GACpC,OAAO,CAAC,aAAa,CAAC,CAIxB"}
|
package/dist/lib/sweeper.d.ts
CHANGED
|
@@ -1,36 +1,33 @@
|
|
|
1
|
+
import { type Bsv20Balance, SWEEP_BATCH_SIZE } from '@1sat/actions';
|
|
1
2
|
import type { IndexedOutput } from '@1sat/types';
|
|
2
3
|
import type { PrivateKey, WalletInterface } from '@bsv/sdk';
|
|
3
|
-
import type { TokenBalance } from './scanner';
|
|
4
|
-
|
|
5
|
-
export declare const SWEEP_BATCH_SIZE = 25;
|
|
4
|
+
import type { ScannedAssets, TokenBalance } from './scanner';
|
|
5
|
+
export { SWEEP_BATCH_SIZE };
|
|
6
6
|
export interface SweepResult {
|
|
7
7
|
bsvTxid?: string;
|
|
8
8
|
ordinalTxids: string[];
|
|
9
|
-
|
|
9
|
+
bsv20Txids: string[];
|
|
10
10
|
bsv21Txids: string[];
|
|
11
11
|
errors: string[];
|
|
12
|
-
/** Outpoints successfully swept (ordinals/OpNS). */
|
|
12
|
+
/** Outpoints successfully swept (ordinals/OpNS, including listed OrdLocks). */
|
|
13
13
|
sweptOutpoints: string[];
|
|
14
|
-
/** Listing outpoints cancelled into the BRC-100 wallet. */
|
|
15
|
-
cancelledListings: string[];
|
|
16
14
|
}
|
|
17
15
|
/**
|
|
18
|
-
* Sweep BSV funding
|
|
19
|
-
*
|
|
16
|
+
* Sweep BSV funding then ordinals into the connected wallet.
|
|
17
|
+
* Same class order as `1sat sweep import` for these two classes.
|
|
18
|
+
* Listed OrdLocks belong in `ordinals` and cancel in that spend.
|
|
20
19
|
*/
|
|
21
20
|
export declare function executeSweep(params: {
|
|
22
21
|
wallet: WalletInterface;
|
|
23
22
|
keys: Map<string, PrivateKey>;
|
|
24
23
|
funding: IndexedOutput[];
|
|
25
24
|
ordinals: IndexedOutput[];
|
|
26
|
-
/** OPL-4696: listed OrdLock UTXOs — cancelled into BRC-100 via sweepOrdinals. */
|
|
27
|
-
listings?: IndexedOutput[];
|
|
28
25
|
amount?: number;
|
|
29
26
|
onProgress: (stage: string) => void;
|
|
30
27
|
}): Promise<SweepResult>;
|
|
31
28
|
/**
|
|
32
|
-
* Sweep
|
|
33
|
-
*
|
|
29
|
+
* Sweep one BSV-21 token. Listed OrdLocks are each their own tx so one
|
|
30
|
+
* invalid listing cannot sink the rest. Unlisted UTXOs of the token share a spend.
|
|
34
31
|
*/
|
|
35
32
|
export declare function sweepBsv21Token(params: {
|
|
36
33
|
wallet: WalletInterface;
|
|
@@ -39,6 +36,25 @@ export declare function sweepBsv21Token(params: {
|
|
|
39
36
|
onProgress: (stage: string) => void;
|
|
40
37
|
}): Promise<{
|
|
41
38
|
txid?: string;
|
|
39
|
+
txids: string[];
|
|
42
40
|
error?: string;
|
|
43
41
|
}>;
|
|
42
|
+
/** Sweep one BSV-20 ticker into the connected wallet. */
|
|
43
|
+
export declare function sweepBsv20Token(params: {
|
|
44
|
+
wallet: WalletInterface;
|
|
45
|
+
keys: Map<string, PrivateKey>;
|
|
46
|
+
token: Bsv20Balance;
|
|
47
|
+
onProgress: (stage: string) => void;
|
|
48
|
+
}): Promise<{
|
|
49
|
+
txid?: string;
|
|
50
|
+
error?: string;
|
|
51
|
+
}>;
|
|
52
|
+
/** Same class order as `1sat sweep import`. Continues later classes if one fails. */
|
|
53
|
+
export declare function sweepAllClasses(params: {
|
|
54
|
+
wallet: WalletInterface;
|
|
55
|
+
keys: Map<string, PrivateKey>;
|
|
56
|
+
assets: ScannedAssets;
|
|
57
|
+
amount?: number;
|
|
58
|
+
onProgress: (stage: string) => void;
|
|
59
|
+
}): Promise<SweepResult>;
|
|
44
60
|
//# sourceMappingURL=sweeper.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sweeper.d.ts","sourceRoot":"","sources":["../../src/lib/sweeper.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sweeper.d.ts","sourceRoot":"","sources":["../../src/lib/sweeper.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,YAAY,EACjB,gBAAgB,EAShB,MAAM,eAAe,CAAA;AACtB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAG5D,OAAO,EAAE,gBAAgB,EAAE,CAAA;AAE3B,MAAM,WAAW,WAAW;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,+EAA+E;IAC/E,cAAc,EAAE,MAAM,EAAE,CAAA;CACxB;AA8BD;;;;GAIG;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,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAqC9D;AAED,yDAAyD;AACzD,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,CA6B7C;AAED,qFAAqF;AACrF,wBAAsB,eAAe,CAAC,MAAM,EAAE;IAC7C,MAAM,EAAE,eAAe,CAAA;IACvB,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IAC7B,MAAM,EAAE,aAAa,CAAA;IACrB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACnC,GAAG,OAAO,CAAC,WAAW,CAAC,CAmEvB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@1sat/sweep-ui",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.97",
|
|
4
4
|
"description": "Sweep UI components for migrating legacy BSV assets",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
],
|
|
30
30
|
"license": "MIT",
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@1sat/actions": "0.0.
|
|
32
|
+
"@1sat/actions": "0.0.218",
|
|
33
33
|
"@1sat/client": "0.0.53",
|
|
34
34
|
"@1sat/connect": "0.0.93",
|
|
35
|
-
"@1sat/types": "0.0.
|
|
35
|
+
"@1sat/types": "0.0.46",
|
|
36
36
|
"@1sat/utils": "0.0.36",
|
|
37
37
|
"bitcoin-backup": "^0.0.11",
|
|
38
38
|
"class-variance-authority": "^0.7.1",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { groupBsv20Tokens } from '@1sat/actions'
|
|
1
2
|
import { PrivateKey, type WalletInterface } from '@bsv/sdk'
|
|
2
3
|
import { useCallback, useEffect, useMemo, useState } from 'react'
|
|
3
4
|
import { Toaster, toast } from 'sonner'
|
|
@@ -12,14 +13,18 @@ import {
|
|
|
12
13
|
deriveAddress,
|
|
13
14
|
scanAddresses,
|
|
14
15
|
} from '../lib/scanner'
|
|
15
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
executeSweep,
|
|
18
|
+
sweepAllClasses,
|
|
19
|
+
sweepBsv20Token,
|
|
20
|
+
sweepBsv21Token,
|
|
21
|
+
} from '../lib/sweeper'
|
|
16
22
|
import { getWallet } from '../lib/wallet'
|
|
17
23
|
import type { LegacyKeys } from '../types'
|
|
18
24
|
import {
|
|
19
25
|
Bsv20Section,
|
|
20
26
|
Bsv21Section,
|
|
21
27
|
FundingSection,
|
|
22
|
-
ListingsSection,
|
|
23
28
|
LockedSection,
|
|
24
29
|
OrdinalsSection,
|
|
25
30
|
RunSection,
|
|
@@ -28,10 +33,11 @@ import { ConnectWallet } from './connect-wallet'
|
|
|
28
33
|
import { OpnsSection } from './opns-section'
|
|
29
34
|
import { TxHistory, type TxRecord } from './tx-history'
|
|
30
35
|
import { Badge } from './ui/badge'
|
|
36
|
+
import { Button } from './ui/button'
|
|
31
37
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from './ui/tabs'
|
|
32
38
|
import { WifInput } from './wif-input'
|
|
33
39
|
|
|
34
|
-
type TabId = 'ordinals' | 'opns' | '
|
|
40
|
+
type TabId = 'ordinals' | 'opns' | 'bsv20' | 'bsv21' | 'locks' | 'run'
|
|
35
41
|
|
|
36
42
|
export interface SweepAppProps {
|
|
37
43
|
legacyKeys?: LegacyKeys
|
|
@@ -94,6 +100,15 @@ export function SweepApp({
|
|
|
94
100
|
])
|
|
95
101
|
}, [])
|
|
96
102
|
|
|
103
|
+
const groupedBsv20 = useMemo(
|
|
104
|
+
() => (assets ? groupBsv20Tokens(assets.bsv20Tokens) : []),
|
|
105
|
+
[assets],
|
|
106
|
+
)
|
|
107
|
+
const unparsedBsv20 = assets
|
|
108
|
+
? assets.bsv20Tokens.length -
|
|
109
|
+
groupedBsv20.reduce((n, t) => n + t.outputs.length, 0)
|
|
110
|
+
: 0
|
|
111
|
+
|
|
97
112
|
const tabs = useMemo(() => {
|
|
98
113
|
if (!assets) return []
|
|
99
114
|
const t: { id: TabId; label: string; count: number }[] = []
|
|
@@ -105,16 +120,24 @@ export function SweepApp({
|
|
|
105
120
|
})
|
|
106
121
|
if (assets.opnsNames.length > 0)
|
|
107
122
|
t.push({ id: 'opns', label: 'OpNS', count: assets.opnsNames.length })
|
|
123
|
+
if (groupedBsv20.length > 0 || unparsedBsv20 > 0)
|
|
124
|
+
t.push({
|
|
125
|
+
id: 'bsv20',
|
|
126
|
+
label: 'BSV-20',
|
|
127
|
+
count: groupedBsv20.reduce((n, tok) => n + tok.outputs.length, 0),
|
|
128
|
+
})
|
|
108
129
|
if (assets.bsv21Tokens.length > 0)
|
|
109
|
-
t.push({
|
|
110
|
-
|
|
111
|
-
|
|
130
|
+
t.push({
|
|
131
|
+
id: 'bsv21',
|
|
132
|
+
label: 'BSV-21',
|
|
133
|
+
count: assets.bsv21Tokens.reduce((n, tok) => n + tok.outputs.length, 0),
|
|
134
|
+
})
|
|
112
135
|
if (assets.locked.length > 0)
|
|
113
136
|
t.push({ id: 'locks', label: 'Locks', count: assets.locked.length })
|
|
114
137
|
if (assets.run.length > 0)
|
|
115
138
|
t.push({ id: 'run', label: 'RUN', count: assets.run.length })
|
|
116
139
|
return t
|
|
117
|
-
}, [assets])
|
|
140
|
+
}, [assets, groupedBsv20, unparsedBsv20])
|
|
118
141
|
|
|
119
142
|
const handleToggleOrdinal = useCallback((outpoint: string) => {
|
|
120
143
|
setSelectedOrdinals((prev) => {
|
|
@@ -187,21 +210,21 @@ export function SweepApp({
|
|
|
187
210
|
)
|
|
188
211
|
setAssets(result)
|
|
189
212
|
|
|
213
|
+
const grouped = groupBsv20Tokens(result.bsv20Tokens)
|
|
190
214
|
const total =
|
|
191
215
|
result.funding.length +
|
|
192
216
|
result.ordinals.length +
|
|
193
217
|
result.opnsNames.length +
|
|
194
218
|
result.bsv21Tokens.reduce((n, t) => n + t.outputs.length, 0) +
|
|
195
|
-
|
|
196
|
-
result.listings.length +
|
|
219
|
+
grouped.reduce((n, t) => n + t.outputs.length, 0) +
|
|
197
220
|
result.locked.length +
|
|
198
221
|
result.run.length
|
|
199
222
|
if (total === 0) toast.info('No assets found at legacy addresses')
|
|
200
223
|
|
|
201
224
|
if (result.ordinals.length > 0) setActiveTab('ordinals')
|
|
202
225
|
else if (result.opnsNames.length > 0) setActiveTab('opns')
|
|
226
|
+
else if (grouped.length > 0) setActiveTab('bsv20')
|
|
203
227
|
else if (result.bsv21Tokens.length > 0) setActiveTab('bsv21')
|
|
204
|
-
else if (result.bsv20Tokens.length > 0) setActiveTab('bsv20')
|
|
205
228
|
else if (result.locked.length > 0) setActiveTab('locks')
|
|
206
229
|
} catch (e) {
|
|
207
230
|
console.error('Scan failed:', e)
|
|
@@ -218,7 +241,7 @@ export function SweepApp({
|
|
|
218
241
|
const runOperation = useCallback(
|
|
219
242
|
async (label: string, op: () => Promise<string>) => {
|
|
220
243
|
setSweeping(true)
|
|
221
|
-
setSweepProgress(label
|
|
244
|
+
setSweepProgress(`${label}...`)
|
|
222
245
|
try {
|
|
223
246
|
const txid = await op()
|
|
224
247
|
if (txid) addTx(label, txid)
|
|
@@ -257,15 +280,10 @@ export function SweepApp({
|
|
|
257
280
|
keys: keyMap,
|
|
258
281
|
funding: getSelectedFunding(),
|
|
259
282
|
ordinals: [],
|
|
260
|
-
listings: assets.listings,
|
|
261
283
|
amount: sweepAmount ?? undefined,
|
|
262
284
|
onProgress: setSweepProgress,
|
|
263
285
|
})
|
|
264
|
-
|
|
265
|
-
if (result.errors.length > 0) {
|
|
266
|
-
if (result.cancelledListings.length > 0) await refreshAssets()
|
|
267
|
-
throw new Error(result.errors[0])
|
|
268
|
-
}
|
|
286
|
+
if (result.errors.length > 0) throw new Error(result.errors[0])
|
|
269
287
|
return result.bsvTxid ?? ''
|
|
270
288
|
})
|
|
271
289
|
}, [
|
|
@@ -276,41 +294,6 @@ export function SweepApp({
|
|
|
276
294
|
getSelectedFunding,
|
|
277
295
|
runOperation,
|
|
278
296
|
keyMap,
|
|
279
|
-
addTx,
|
|
280
|
-
refreshAssets,
|
|
281
|
-
])
|
|
282
|
-
|
|
283
|
-
const handleCancelListings = useCallback(async () => {
|
|
284
|
-
const wallet = resolveWallet()
|
|
285
|
-
if (!wallet || !legacyKeys || !assets || assets.listings.length === 0)
|
|
286
|
-
return
|
|
287
|
-
await runOperation('Cancel listings', async () => {
|
|
288
|
-
const result = await executeSweep({
|
|
289
|
-
wallet,
|
|
290
|
-
keys: keyMap,
|
|
291
|
-
funding: [],
|
|
292
|
-
ordinals: [],
|
|
293
|
-
listings: assets.listings,
|
|
294
|
-
onProgress: setSweepProgress,
|
|
295
|
-
})
|
|
296
|
-
if (result.errors.length > 0) {
|
|
297
|
-
for (const txid of result.listingTxids) addTx('Cancel listings', txid)
|
|
298
|
-
if (result.cancelledListings.length > 0) await refreshAssets()
|
|
299
|
-
throw new Error(result.errors[0])
|
|
300
|
-
}
|
|
301
|
-
for (let i = 0; i < result.listingTxids.length - 1; i++) {
|
|
302
|
-
addTx('Cancel listings', result.listingTxids[i])
|
|
303
|
-
}
|
|
304
|
-
return result.listingTxids[result.listingTxids.length - 1] ?? ''
|
|
305
|
-
})
|
|
306
|
-
}, [
|
|
307
|
-
resolveWallet,
|
|
308
|
-
legacyKeys,
|
|
309
|
-
assets,
|
|
310
|
-
keyMap,
|
|
311
|
-
runOperation,
|
|
312
|
-
addTx,
|
|
313
|
-
refreshAssets,
|
|
314
297
|
])
|
|
315
298
|
|
|
316
299
|
const handleSendBsv = useCallback(
|
|
@@ -534,14 +517,69 @@ export function SweepApp({
|
|
|
534
517
|
token,
|
|
535
518
|
onProgress: setSweepProgress,
|
|
536
519
|
})
|
|
520
|
+
for (const txid of result.txids.slice(0, -1)) addTx('BSV-21', txid)
|
|
537
521
|
if (result.error) throw new Error(result.error)
|
|
538
522
|
return result.txid ?? ''
|
|
539
523
|
},
|
|
540
524
|
)
|
|
541
525
|
},
|
|
542
|
-
[resolveWallet, assets, keyMap, runOperation],
|
|
526
|
+
[resolveWallet, assets, keyMap, runOperation, addTx],
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
const handleSweepBsv20Token = useCallback(
|
|
530
|
+
async (tick: string) => {
|
|
531
|
+
const wallet = resolveWallet()
|
|
532
|
+
if (!wallet || !assets) return
|
|
533
|
+
const token = groupedBsv20.find((t) => t.tick === tick)
|
|
534
|
+
if (!token) return
|
|
535
|
+
await runOperation(`Sweep ${token.tick}`, async () => {
|
|
536
|
+
const result = await sweepBsv20Token({
|
|
537
|
+
wallet,
|
|
538
|
+
keys: keyMap,
|
|
539
|
+
token,
|
|
540
|
+
onProgress: setSweepProgress,
|
|
541
|
+
})
|
|
542
|
+
if (result.error) throw new Error(result.error)
|
|
543
|
+
return result.txid ?? ''
|
|
544
|
+
})
|
|
545
|
+
},
|
|
546
|
+
[resolveWallet, assets, groupedBsv20, keyMap, runOperation],
|
|
543
547
|
)
|
|
544
548
|
|
|
549
|
+
const handleSweepAll = useCallback(async () => {
|
|
550
|
+
const wallet = resolveWallet()
|
|
551
|
+
if (!wallet || !legacyKeys || !assets) return
|
|
552
|
+
await runOperation('Sweep all', async () => {
|
|
553
|
+
const result = await sweepAllClasses({
|
|
554
|
+
wallet,
|
|
555
|
+
keys: keyMap,
|
|
556
|
+
assets,
|
|
557
|
+
amount: sweepAmount ?? undefined,
|
|
558
|
+
onProgress: setSweepProgress,
|
|
559
|
+
})
|
|
560
|
+
if (result.bsvTxid) addTx('BSV', result.bsvTxid)
|
|
561
|
+
for (const txid of result.ordinalTxids) addTx('Ordinals', txid)
|
|
562
|
+
for (const txid of result.bsv20Txids) addTx('BSV-20', txid)
|
|
563
|
+
for (const txid of result.bsv21Txids) addTx('BSV-21', txid)
|
|
564
|
+
if (result.errors.length > 0) throw new Error(result.errors[0])
|
|
565
|
+
return (
|
|
566
|
+
result.bsvTxid ??
|
|
567
|
+
result.ordinalTxids.at(-1) ??
|
|
568
|
+
result.bsv20Txids.at(-1) ??
|
|
569
|
+
result.bsv21Txids.at(-1) ??
|
|
570
|
+
''
|
|
571
|
+
)
|
|
572
|
+
})
|
|
573
|
+
}, [
|
|
574
|
+
resolveWallet,
|
|
575
|
+
legacyKeys,
|
|
576
|
+
assets,
|
|
577
|
+
keyMap,
|
|
578
|
+
sweepAmount,
|
|
579
|
+
runOperation,
|
|
580
|
+
addTx,
|
|
581
|
+
])
|
|
582
|
+
|
|
545
583
|
return (
|
|
546
584
|
<div className="min-h-screen bg-background text-foreground">
|
|
547
585
|
<Toaster position="top-right" />
|
|
@@ -577,6 +615,16 @@ export function SweepApp({
|
|
|
577
615
|
|
|
578
616
|
{assets && !sweeping && (
|
|
579
617
|
<div className="space-y-3">
|
|
618
|
+
<Button
|
|
619
|
+
className="w-full"
|
|
620
|
+
disabled={!walletConnected}
|
|
621
|
+
onClick={handleSweepAll}
|
|
622
|
+
title={
|
|
623
|
+
walletConnected ? undefined : 'Connect BRC-100 wallet to sweep'
|
|
624
|
+
}
|
|
625
|
+
>
|
|
626
|
+
Sweep all to wallet
|
|
627
|
+
</Button>
|
|
580
628
|
<FundingSection
|
|
581
629
|
funding={assets.funding}
|
|
582
630
|
totalBsv={assets.totalBsv}
|
|
@@ -586,11 +634,6 @@ export function SweepApp({
|
|
|
586
634
|
onSend={sweepOnly ? undefined : handleSendBsv}
|
|
587
635
|
walletConnected={walletConnected}
|
|
588
636
|
/>
|
|
589
|
-
<ListingsSection
|
|
590
|
-
listings={assets.listings}
|
|
591
|
-
walletConnected={walletConnected}
|
|
592
|
-
onCancel={handleCancelListings}
|
|
593
|
-
/>
|
|
594
637
|
|
|
595
638
|
{tabs.length > 0 && (
|
|
596
639
|
<Tabs
|
|
@@ -642,6 +685,14 @@ export function SweepApp({
|
|
|
642
685
|
walletConnected={walletConnected}
|
|
643
686
|
/>
|
|
644
687
|
</TabsContent>
|
|
688
|
+
<TabsContent value="bsv20">
|
|
689
|
+
<Bsv20Section
|
|
690
|
+
tokens={groupedBsv20}
|
|
691
|
+
unparsedCount={unparsedBsv20}
|
|
692
|
+
onSweep={handleSweepBsv20Token}
|
|
693
|
+
walletConnected={walletConnected}
|
|
694
|
+
/>
|
|
695
|
+
</TabsContent>
|
|
645
696
|
<TabsContent value="bsv21">
|
|
646
697
|
<Bsv21Section
|
|
647
698
|
tokens={assets.bsv21Tokens}
|
|
@@ -649,9 +700,6 @@ export function SweepApp({
|
|
|
649
700
|
walletConnected={walletConnected}
|
|
650
701
|
/>
|
|
651
702
|
</TabsContent>
|
|
652
|
-
<TabsContent value="bsv20">
|
|
653
|
-
<Bsv20Section tokens={assets.bsv20Tokens} />
|
|
654
|
-
</TabsContent>
|
|
655
703
|
<TabsContent value="locks">
|
|
656
704
|
<LockedSection locked={assets.locked} />
|
|
657
705
|
</TabsContent>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Bsv20Balance, isListedOutput } from '@1sat/actions'
|
|
1
2
|
import type { IndexedOutput } from '@1sat/types'
|
|
2
3
|
import { useState } from 'react'
|
|
3
4
|
import type { EnrichedOrdinal, TokenBalance } from '../lib/scanner'
|
|
@@ -53,6 +54,13 @@ function OrdinalCard({
|
|
|
53
54
|
)}
|
|
54
55
|
</div>
|
|
55
56
|
<div className="min-h-[2rem] flex flex-col justify-end">
|
|
57
|
+
{isListedOutput(ordinal) && (
|
|
58
|
+
<div className="mb-1">
|
|
59
|
+
<span className="inline-block px-1 py-0.5 text-[9px] rounded bg-amber-500/20 text-amber-400">
|
|
60
|
+
listed
|
|
61
|
+
</span>
|
|
62
|
+
</div>
|
|
63
|
+
)}
|
|
56
64
|
{subtype && (
|
|
57
65
|
<div className="mb-1">
|
|
58
66
|
<span className="inline-block max-w-full px-1 py-0.5 text-[9px] rounded bg-blue-500/20 text-blue-400 truncate">
|
|
@@ -388,6 +396,7 @@ function TokenRow({
|
|
|
388
396
|
onSweep?: (tokenId: string) => void
|
|
389
397
|
walletConnected: boolean
|
|
390
398
|
}) {
|
|
399
|
+
const listed = tb.outputs.filter(isListedOutput).length
|
|
391
400
|
return (
|
|
392
401
|
<div
|
|
393
402
|
className={`flex items-center justify-between p-3 rounded-lg border ${tb.isActive ? 'bg-muted/30 border-purple-500/10' : 'bg-muted/20 border-muted/20 opacity-60'}`}
|
|
@@ -404,7 +413,7 @@ function TokenRow({
|
|
|
404
413
|
<div>
|
|
405
414
|
<div className="flex items-center gap-2">
|
|
406
415
|
<span className="font-medium text-foreground">
|
|
407
|
-
{tb.symbol || tb.tokenId.slice(0, 8)
|
|
416
|
+
{tb.symbol || `${tb.tokenId.slice(0, 8)}...`}
|
|
408
417
|
</span>
|
|
409
418
|
{tb.isActive ? (
|
|
410
419
|
<span className="px-1.5 py-0.5 text-[9px] rounded bg-green-600/20 text-green-700 dark:text-green-400">
|
|
@@ -420,12 +429,13 @@ function TokenRow({
|
|
|
420
429
|
{formatTokenAmount(tb.totalAmount.toString(), tb.decimals)}{' '}
|
|
421
430
|
{tb.symbol || ''}
|
|
422
431
|
<span className="ml-2">
|
|
423
|
-
({tb.outputs.length} output{tb.outputs.length !== 1 ? 's' : ''}
|
|
432
|
+
({tb.outputs.length} output{tb.outputs.length !== 1 ? 's' : ''}
|
|
433
|
+
{listed ? `, ${listed} listed` : ''})
|
|
424
434
|
</span>
|
|
425
435
|
</div>
|
|
426
436
|
</div>
|
|
427
437
|
</div>
|
|
428
|
-
{
|
|
438
|
+
{onSweep && (
|
|
429
439
|
<Button
|
|
430
440
|
size="sm"
|
|
431
441
|
onClick={() => onSweep(tb.tokenId)}
|
|
@@ -490,74 +500,69 @@ export function Bsv21Section({
|
|
|
490
500
|
)
|
|
491
501
|
}
|
|
492
502
|
|
|
493
|
-
export function Bsv20Section({
|
|
494
|
-
|
|
503
|
+
export function Bsv20Section({
|
|
504
|
+
tokens,
|
|
505
|
+
unparsedCount = 0,
|
|
506
|
+
onSweep,
|
|
507
|
+
walletConnected,
|
|
508
|
+
}: {
|
|
509
|
+
tokens: Bsv20Balance[]
|
|
510
|
+
unparsedCount?: number
|
|
511
|
+
onSweep?: (tick: string) => void
|
|
512
|
+
walletConnected: boolean
|
|
513
|
+
}) {
|
|
514
|
+
if (tokens.length === 0 && unparsedCount === 0) return null
|
|
495
515
|
return (
|
|
496
|
-
<div className="border border-
|
|
497
|
-
<div className="flex items-center gap-2 mb-
|
|
498
|
-
<span className="h-2 w-2 rounded-full bg-
|
|
499
|
-
<span className="text-sm font-semibold text-
|
|
516
|
+
<div className="border border-teal-500/20 bg-teal-500/5 p-4 rounded-lg">
|
|
517
|
+
<div className="flex items-center gap-2 mb-3">
|
|
518
|
+
<span className="h-2 w-2 rounded-full bg-teal-500" />
|
|
519
|
+
<span className="text-sm font-semibold text-teal-500">
|
|
500
520
|
BSV-20 Tokens
|
|
501
521
|
</span>
|
|
502
522
|
</div>
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
523
|
+
{unparsedCount > 0 && (
|
|
524
|
+
<p className="text-xs text-muted-foreground mb-3">
|
|
525
|
+
{unparsedCount} UTXO{unparsedCount !== 1 ? 's' : ''} with no parseable
|
|
526
|
+
tick/amt — leftover, not swept.
|
|
527
|
+
</p>
|
|
528
|
+
)}
|
|
529
|
+
<div className="space-y-3">
|
|
530
|
+
{tokens.map((tb) => {
|
|
531
|
+
const listed = tb.outputs.filter(isListedOutput).length
|
|
510
532
|
return (
|
|
511
|
-
<
|
|
512
|
-
key={
|
|
513
|
-
className="
|
|
533
|
+
<div
|
|
534
|
+
key={tb.tick}
|
|
535
|
+
className="flex items-center justify-between p-3 rounded-lg border bg-muted/30 border-teal-500/10"
|
|
514
536
|
>
|
|
515
|
-
|
|
516
|
-
|
|
537
|
+
<div>
|
|
538
|
+
<div className="font-medium text-foreground">{tb.tick}</div>
|
|
539
|
+
<div className="text-xs text-muted-foreground">
|
|
540
|
+
{tb.totalAmount.toString()}
|
|
541
|
+
<span className="ml-2">
|
|
542
|
+
({tb.outputs.length} output
|
|
543
|
+
{tb.outputs.length !== 1 ? 's' : ''}
|
|
544
|
+
{listed ? `, ${listed} listed` : ''})
|
|
545
|
+
</span>
|
|
546
|
+
</div>
|
|
547
|
+
</div>
|
|
548
|
+
{onSweep && (
|
|
549
|
+
<Button
|
|
550
|
+
size="sm"
|
|
551
|
+
onClick={() => onSweep(tb.tick)}
|
|
552
|
+
disabled={!walletConnected}
|
|
553
|
+
title={
|
|
554
|
+
walletConnected
|
|
555
|
+
? undefined
|
|
556
|
+
: 'Connect BRC-100 wallet to sweep'
|
|
557
|
+
}
|
|
558
|
+
>
|
|
559
|
+
Sweep to Wallet
|
|
560
|
+
</Button>
|
|
561
|
+
)}
|
|
562
|
+
</div>
|
|
517
563
|
)
|
|
518
564
|
})}
|
|
519
|
-
{tokens.length > 10 && (
|
|
520
|
-
<span className="text-xs text-muted-foreground">
|
|
521
|
-
+{tokens.length - 10} more
|
|
522
|
-
</span>
|
|
523
|
-
)}
|
|
524
|
-
</div>
|
|
525
|
-
</div>
|
|
526
|
-
)
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
export function ListingsSection({
|
|
530
|
-
listings,
|
|
531
|
-
walletConnected,
|
|
532
|
-
onCancel,
|
|
533
|
-
}: {
|
|
534
|
-
listings: EnrichedOrdinal[]
|
|
535
|
-
walletConnected?: boolean
|
|
536
|
-
onCancel?: () => void
|
|
537
|
-
}) {
|
|
538
|
-
if (listings.length === 0) return null
|
|
539
|
-
return (
|
|
540
|
-
<div className="border border-primary/20 bg-primary/5 p-4 rounded-lg space-y-3">
|
|
541
|
-
<div className="flex items-center gap-2">
|
|
542
|
-
<span className="h-2 w-2 rounded-full bg-primary" />
|
|
543
|
-
<span className="text-sm font-semibold text-primary">
|
|
544
|
-
OrdLock Listings
|
|
545
|
-
</span>
|
|
546
565
|
</div>
|
|
547
|
-
<p className="text-xs text-muted-foreground">
|
|
548
|
-
{listings.length} listed UTXO{listings.length !== 1 ? 's' : ''}. These
|
|
549
|
-
are cancelled into your BRC-100 wallet on BSV sweep.
|
|
550
|
-
</p>
|
|
551
|
-
{onCancel && (
|
|
552
|
-
<Button
|
|
553
|
-
size="sm"
|
|
554
|
-
disabled={!walletConnected}
|
|
555
|
-
onClick={onCancel}
|
|
556
|
-
className="w-full"
|
|
557
|
-
>
|
|
558
|
-
Cancel listings into wallet
|
|
559
|
-
</Button>
|
|
560
|
-
)}
|
|
561
566
|
</div>
|
|
562
567
|
)
|
|
563
568
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isListedOutput } from '@1sat/actions'
|
|
1
2
|
import { useEffect, useState } from 'react'
|
|
2
3
|
import type { EnrichedOrdinal } from '../lib/scanner'
|
|
3
4
|
import { getServices } from '../lib/services'
|
|
@@ -162,6 +163,11 @@ export function OpnsSection({
|
|
|
162
163
|
<span className="text-sm text-foreground truncate">
|
|
163
164
|
{displayName}
|
|
164
165
|
</span>
|
|
166
|
+
{isListedOutput(item) && (
|
|
167
|
+
<span className="shrink-0 text-[10px] font-medium px-1.5 py-0.5 rounded bg-amber-500/20 text-amber-400">
|
|
168
|
+
listed
|
|
169
|
+
</span>
|
|
170
|
+
)}
|
|
165
171
|
{!validating &&
|
|
166
172
|
overlayValid.has(item.outpoint) &&
|
|
167
173
|
(overlayValid.get(item.outpoint) ? (
|
package/src/index.ts
CHANGED
|
@@ -9,7 +9,6 @@ export {
|
|
|
9
9
|
OrdinalsSection,
|
|
10
10
|
Bsv21Section,
|
|
11
11
|
Bsv20Section,
|
|
12
|
-
ListingsSection,
|
|
13
12
|
LockedSection,
|
|
14
13
|
RunSection,
|
|
15
14
|
} from './components/asset-preview'
|
|
@@ -53,6 +52,7 @@ export {
|
|
|
53
52
|
} from './lib/scanner'
|
|
54
53
|
export {
|
|
55
54
|
executeSweep,
|
|
55
|
+
sweepAllClasses,
|
|
56
56
|
SWEEP_BATCH_SIZE,
|
|
57
57
|
type SweepResult,
|
|
58
58
|
} from './lib/sweeper'
|
package/src/lib/scanner.ts
CHANGED
|
@@ -18,10 +18,9 @@ export interface EnrichedOrdinal extends IndexedOutput {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export interface ScannedAssets
|
|
21
|
-
extends Omit<ScanResult, 'ordinals' | 'opnsNames'> {
|
|
21
|
+
extends Omit<ScanResult, 'ordinals' | 'opnsNames' | 'listings'> {
|
|
22
22
|
ordinals: EnrichedOrdinal[]
|
|
23
23
|
opnsNames: EnrichedOrdinal[]
|
|
24
|
-
listings: EnrichedOrdinal[]
|
|
25
24
|
bsv21Tokens: TokenBalance[]
|
|
26
25
|
totalBsv: number
|
|
27
26
|
}
|
|
@@ -93,7 +92,6 @@ function toScannedAssets(result: ScanResult): ScannedAssets {
|
|
|
93
92
|
funding: result.funding,
|
|
94
93
|
ordinals: result.ordinals.map(enrichOrdinal),
|
|
95
94
|
opnsNames: result.opnsNames.map(enrichOrdinal),
|
|
96
|
-
listings: result.listings.map(enrichOrdinal),
|
|
97
95
|
bsv21Tokens: enrichTokenBalances(result.bsv21Tokens),
|
|
98
96
|
bsv20Tokens: result.bsv20Tokens,
|
|
99
97
|
locked: result.locked,
|