@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/src/lib/sweeper.ts
CHANGED
|
@@ -1,28 +1,30 @@
|
|
|
1
1
|
import {
|
|
2
|
+
type Bsv20Balance,
|
|
3
|
+
SWEEP_BATCH_SIZE,
|
|
4
|
+
bsv21SweepBatches,
|
|
2
5
|
createContext,
|
|
6
|
+
groupBsv20Tokens,
|
|
3
7
|
prepareSweepInputs,
|
|
4
8
|
sweepBsv,
|
|
9
|
+
sweepBsv20,
|
|
5
10
|
sweepBsv21,
|
|
6
11
|
sweepOrdinals,
|
|
7
12
|
} from '@1sat/actions'
|
|
8
13
|
import type { IndexedOutput } from '@1sat/types'
|
|
9
14
|
import type { PrivateKey, WalletInterface } from '@bsv/sdk'
|
|
10
|
-
import type { TokenBalance } from './scanner'
|
|
15
|
+
import type { ScannedAssets, TokenBalance } from './scanner'
|
|
11
16
|
import { getServices } from './services'
|
|
12
17
|
|
|
13
|
-
|
|
14
|
-
export const SWEEP_BATCH_SIZE = 25
|
|
18
|
+
export { SWEEP_BATCH_SIZE }
|
|
15
19
|
|
|
16
20
|
export interface SweepResult {
|
|
17
21
|
bsvTxid?: string
|
|
18
22
|
ordinalTxids: string[]
|
|
19
|
-
|
|
23
|
+
bsv20Txids: string[]
|
|
20
24
|
bsv21Txids: string[]
|
|
21
25
|
errors: string[]
|
|
22
|
-
/** Outpoints successfully swept (ordinals/OpNS). */
|
|
26
|
+
/** Outpoints successfully swept (ordinals/OpNS, including listed OrdLocks). */
|
|
23
27
|
sweptOutpoints: string[]
|
|
24
|
-
/** Listing outpoints cancelled into the BRC-100 wallet. */
|
|
25
|
-
cancelledListings: string[]
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
function getOwner(output: IndexedOutput): string | undefined {
|
|
@@ -54,65 +56,27 @@ function chunk<T>(items: T[], size: number): T[][] {
|
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
/**
|
|
57
|
-
* Sweep BSV funding
|
|
58
|
-
*
|
|
59
|
+
* Sweep BSV funding then ordinals into the connected wallet.
|
|
60
|
+
* Same class order as `1sat sweep import` for these two classes.
|
|
61
|
+
* Listed OrdLocks belong in `ordinals` and cancel in that spend.
|
|
59
62
|
*/
|
|
60
63
|
export async function executeSweep(params: {
|
|
61
64
|
wallet: WalletInterface
|
|
62
65
|
keys: Map<string, PrivateKey>
|
|
63
66
|
funding: IndexedOutput[]
|
|
64
67
|
ordinals: IndexedOutput[]
|
|
65
|
-
/** OPL-4696: listed OrdLock UTXOs — cancelled into BRC-100 via sweepOrdinals. */
|
|
66
|
-
listings?: IndexedOutput[]
|
|
67
68
|
amount?: number
|
|
68
69
|
onProgress: (stage: string) => void
|
|
69
70
|
}): Promise<SweepResult> {
|
|
70
|
-
const {
|
|
71
|
-
wallet,
|
|
72
|
-
keys,
|
|
73
|
-
funding,
|
|
74
|
-
ordinals,
|
|
75
|
-
listings = [],
|
|
76
|
-
amount,
|
|
77
|
-
onProgress,
|
|
78
|
-
} = params
|
|
71
|
+
const { wallet, keys, funding, ordinals, amount, onProgress } = params
|
|
79
72
|
const ctx = createContext(wallet, { services: getServices(), chain: 'main' })
|
|
80
73
|
|
|
81
74
|
const result: SweepResult = {
|
|
82
75
|
ordinalTxids: [],
|
|
83
|
-
|
|
76
|
+
bsv20Txids: [],
|
|
84
77
|
bsv21Txids: [],
|
|
85
78
|
errors: [],
|
|
86
79
|
sweptOutpoints: [],
|
|
87
|
-
cancelledListings: [],
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (listings.length > 0) {
|
|
91
|
-
const batches = chunk(listings, SWEEP_BATCH_SIZE)
|
|
92
|
-
for (let b = 0; b < batches.length; b++) {
|
|
93
|
-
const batch = batches[b]
|
|
94
|
-
onProgress(
|
|
95
|
-
`Cancelling ${batch.length} OrdLock listing${batch.length !== 1 ? 's' : ''} into wallet...`,
|
|
96
|
-
)
|
|
97
|
-
try {
|
|
98
|
-
const inputs = await prepareSweepInputs(ctx, batch)
|
|
99
|
-
const cancelResult = await sweepOrdinals.execute(ctx, {
|
|
100
|
-
inputs,
|
|
101
|
-
keys: buildKeys(batch, keys, inputs),
|
|
102
|
-
})
|
|
103
|
-
if (cancelResult.error) throw new Error(cancelResult.error)
|
|
104
|
-
const txid = cancelResult.txid?.trim()
|
|
105
|
-
if (!txid) throw new Error('Cancellation returned no transaction ID')
|
|
106
|
-
result.listingTxids.push(txid)
|
|
107
|
-
result.cancelledListings.push(...batch.map((o) => o.outpoint))
|
|
108
|
-
} catch (e) {
|
|
109
|
-
result.errors.push(
|
|
110
|
-
`Listings batch ${b + 1}: ${e instanceof Error ? e.message : String(e)}`,
|
|
111
|
-
)
|
|
112
|
-
onProgress('Sweep stopped with errors')
|
|
113
|
-
return result
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
80
|
}
|
|
117
81
|
|
|
118
82
|
if (funding.length > 0) {
|
|
@@ -171,19 +135,64 @@ export async function executeSweep(params: {
|
|
|
171
135
|
}
|
|
172
136
|
|
|
173
137
|
/**
|
|
174
|
-
* Sweep
|
|
175
|
-
*
|
|
138
|
+
* Sweep one BSV-21 token. Listed OrdLocks are each their own tx so one
|
|
139
|
+
* invalid listing cannot sink the rest. Unlisted UTXOs of the token share a spend.
|
|
176
140
|
*/
|
|
177
141
|
export async function sweepBsv21Token(params: {
|
|
178
142
|
wallet: WalletInterface
|
|
179
143
|
keys: Map<string, PrivateKey>
|
|
180
144
|
token: TokenBalance
|
|
181
145
|
onProgress: (stage: string) => void
|
|
146
|
+
}): Promise<{ txid?: string; txids: string[]; error?: string }> {
|
|
147
|
+
const { wallet, keys, token, onProgress } = params
|
|
148
|
+
const ctx = createContext(wallet, { services: getServices(), chain: 'main' })
|
|
149
|
+
const txids: string[] = []
|
|
150
|
+
const errors: string[] = []
|
|
151
|
+
const name = token.symbol ?? token.tokenId.slice(0, 8)
|
|
152
|
+
|
|
153
|
+
for (const batch of bsv21SweepBatches(token.outputs)) {
|
|
154
|
+
onProgress(`Sweeping ${name}...`)
|
|
155
|
+
try {
|
|
156
|
+
const sweepInputs = await prepareSweepInputs(ctx, batch)
|
|
157
|
+
const sweepInputMap = new Map(sweepInputs.map((s) => [s.outpoint, s]))
|
|
158
|
+
const inputs = batch.map((out) => {
|
|
159
|
+
const base = sweepInputMap.get(out.outpoint)
|
|
160
|
+
if (!base) throw new Error(`Missing sweep input for ${out.outpoint}`)
|
|
161
|
+
return {
|
|
162
|
+
...base,
|
|
163
|
+
tokenId: token.tokenId,
|
|
164
|
+
amount: token.amounts.get(out.outpoint) ?? '0',
|
|
165
|
+
}
|
|
166
|
+
})
|
|
167
|
+
const result = await sweepBsv21.execute(ctx, {
|
|
168
|
+
inputs,
|
|
169
|
+
keys: buildKeys(batch, keys),
|
|
170
|
+
})
|
|
171
|
+
if (result.error) errors.push(result.error)
|
|
172
|
+
else if (result.txid) txids.push(result.txid)
|
|
173
|
+
} catch (e) {
|
|
174
|
+
errors.push(e instanceof Error ? e.message : String(e))
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
txid: txids.at(-1),
|
|
180
|
+
txids,
|
|
181
|
+
error: errors[0],
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Sweep one BSV-20 ticker into the connected wallet. */
|
|
186
|
+
export async function sweepBsv20Token(params: {
|
|
187
|
+
wallet: WalletInterface
|
|
188
|
+
keys: Map<string, PrivateKey>
|
|
189
|
+
token: Bsv20Balance
|
|
190
|
+
onProgress: (stage: string) => void
|
|
182
191
|
}): Promise<{ txid?: string; error?: string }> {
|
|
183
192
|
const { wallet, keys, token, onProgress } = params
|
|
184
193
|
const ctx = createContext(wallet, { services: getServices(), chain: 'main' })
|
|
185
194
|
|
|
186
|
-
onProgress(`Sweeping ${token.
|
|
195
|
+
onProgress(`Sweeping ${token.tick}...`)
|
|
187
196
|
|
|
188
197
|
try {
|
|
189
198
|
const sweepInputs = await prepareSweepInputs(ctx, token.outputs)
|
|
@@ -194,17 +203,94 @@ export async function sweepBsv21Token(params: {
|
|
|
194
203
|
if (!base) throw new Error(`Missing sweep input for ${out.outpoint}`)
|
|
195
204
|
return {
|
|
196
205
|
...base,
|
|
197
|
-
|
|
206
|
+
tick: token.tick,
|
|
198
207
|
amount: token.amounts.get(out.outpoint) ?? '0',
|
|
199
208
|
}
|
|
200
209
|
})
|
|
201
210
|
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
211
|
+
const result = await sweepBsv20.execute(ctx, {
|
|
212
|
+
inputs,
|
|
213
|
+
keys: buildKeys(token.outputs, keys),
|
|
214
|
+
})
|
|
205
215
|
if (result.error) return { error: result.error }
|
|
206
216
|
return { txid: result.txid }
|
|
207
217
|
} catch (e) {
|
|
208
218
|
return { error: e instanceof Error ? e.message : String(e) }
|
|
209
219
|
}
|
|
210
220
|
}
|
|
221
|
+
|
|
222
|
+
/** Same class order as `1sat sweep import`. Continues later classes if one fails. */
|
|
223
|
+
export async function sweepAllClasses(params: {
|
|
224
|
+
wallet: WalletInterface
|
|
225
|
+
keys: Map<string, PrivateKey>
|
|
226
|
+
assets: ScannedAssets
|
|
227
|
+
amount?: number
|
|
228
|
+
onProgress: (stage: string) => void
|
|
229
|
+
}): Promise<SweepResult> {
|
|
230
|
+
const { wallet, keys, assets, amount, onProgress } = params
|
|
231
|
+
const result: SweepResult = {
|
|
232
|
+
ordinalTxids: [],
|
|
233
|
+
bsv20Txids: [],
|
|
234
|
+
bsv21Txids: [],
|
|
235
|
+
errors: [],
|
|
236
|
+
sweptOutpoints: [],
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const first = await executeSweep({
|
|
240
|
+
wallet,
|
|
241
|
+
keys,
|
|
242
|
+
funding: assets.funding,
|
|
243
|
+
ordinals: assets.ordinals,
|
|
244
|
+
amount,
|
|
245
|
+
onProgress,
|
|
246
|
+
})
|
|
247
|
+
result.bsvTxid = first.bsvTxid
|
|
248
|
+
result.ordinalTxids.push(...first.ordinalTxids)
|
|
249
|
+
result.sweptOutpoints.push(...first.sweptOutpoints)
|
|
250
|
+
result.errors.push(...first.errors)
|
|
251
|
+
|
|
252
|
+
if (assets.opnsNames.length > 0) {
|
|
253
|
+
const opns = await executeSweep({
|
|
254
|
+
wallet,
|
|
255
|
+
keys,
|
|
256
|
+
funding: [],
|
|
257
|
+
ordinals: assets.opnsNames,
|
|
258
|
+
onProgress,
|
|
259
|
+
})
|
|
260
|
+
result.ordinalTxids.push(...opns.ordinalTxids)
|
|
261
|
+
result.sweptOutpoints.push(...opns.sweptOutpoints)
|
|
262
|
+
result.errors.push(...opns.errors)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
for (const token of groupBsv20Tokens(assets.bsv20Tokens)) {
|
|
266
|
+
const swept = await sweepBsv20Token({
|
|
267
|
+
wallet,
|
|
268
|
+
keys,
|
|
269
|
+
token,
|
|
270
|
+
onProgress,
|
|
271
|
+
})
|
|
272
|
+
if (swept.error) result.errors.push(`BSV-20 ${token.tick}: ${swept.error}`)
|
|
273
|
+
else if (swept.txid) result.bsv20Txids.push(swept.txid)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
for (const token of assets.bsv21Tokens) {
|
|
277
|
+
if (token.outputs.length === 0) continue
|
|
278
|
+
const swept = await sweepBsv21Token({
|
|
279
|
+
wallet,
|
|
280
|
+
keys,
|
|
281
|
+
token,
|
|
282
|
+
onProgress,
|
|
283
|
+
})
|
|
284
|
+
result.bsv21Txids.push(...swept.txids)
|
|
285
|
+
if (swept.error) {
|
|
286
|
+
result.errors.push(
|
|
287
|
+
`BSV-21 ${token.symbol ?? token.tokenId.slice(0, 8)}: ${swept.error}`,
|
|
288
|
+
)
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
onProgress(
|
|
293
|
+
result.errors.length > 0 ? 'Sweep stopped with errors' : 'Sweep complete',
|
|
294
|
+
)
|
|
295
|
+
return result
|
|
296
|
+
}
|