@1sat/sweep-ui 0.0.19 → 0.0.20
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
package/dist/index.js
CHANGED
|
@@ -1,1101 +1,1256 @@
|
|
|
1
1
|
// src/components/SweepApp.tsx
|
|
2
|
+
import { PrivateKey as PrivateKey3 } from "@bsv/sdk";
|
|
2
3
|
import { useCallback as useCallback2, useEffect as useEffect2, useMemo, useState as useState5 } from "react";
|
|
3
4
|
import { Toaster, toast } from "sonner";
|
|
4
5
|
|
|
5
|
-
// src/
|
|
6
|
-
import {
|
|
6
|
+
// src/lib/legacy-send.ts
|
|
7
|
+
import { MAP_PREFIX } from "@1sat/types";
|
|
8
|
+
import { parseOutpoint } from "@1sat/utils";
|
|
9
|
+
import { OP, P2PKH, PrivateKey as PrivateKey2, Script, Transaction, Utils } from "@bsv/sdk";
|
|
7
10
|
|
|
8
|
-
// src/lib/
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
function formatSats(sats) {
|
|
15
|
-
return sats.toLocaleString();
|
|
16
|
-
}
|
|
17
|
-
function formatTokenAmount(rawAmount, decimals) {
|
|
18
|
-
if (decimals === 0)
|
|
19
|
-
return rawAmount;
|
|
20
|
-
const padded = rawAmount.padStart(decimals + 1, "0");
|
|
21
|
-
const intPart = padded.slice(0, -decimals) || "0";
|
|
22
|
-
const decPart = padded.slice(-decimals).replace(/0+$/, "");
|
|
23
|
-
return decPart ? `${intPart}.${decPart}` : intPart;
|
|
24
|
-
}
|
|
25
|
-
function truncate(s, len = 8) {
|
|
26
|
-
if (s.length <= len * 2 + 3)
|
|
27
|
-
return s;
|
|
28
|
-
return `${s.slice(0, len)}...${s.slice(-len)}`;
|
|
29
|
-
}
|
|
11
|
+
// src/lib/scanner.ts
|
|
12
|
+
import {
|
|
13
|
+
scanAddresses as coreScanAddresses
|
|
14
|
+
} from "@1sat/actions";
|
|
15
|
+
import { PrivateKey } from "@bsv/sdk";
|
|
30
16
|
|
|
31
|
-
// src/
|
|
32
|
-
import {
|
|
33
|
-
var
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
destructive: "bg-destructive text-white focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90",
|
|
39
|
-
outline: "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
40
|
-
ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
41
|
-
link: "text-primary underline-offset-4 [a&]:hover:underline"
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
|
-
defaultVariants: {
|
|
45
|
-
variant: "default"
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
function Badge({
|
|
49
|
-
className,
|
|
50
|
-
variant = "default",
|
|
51
|
-
...props
|
|
52
|
-
}) {
|
|
53
|
-
return /* @__PURE__ */ jsx("span", {
|
|
54
|
-
"data-slot": "badge",
|
|
55
|
-
"data-variant": variant,
|
|
56
|
-
className: cn(badgeVariants({ variant }), className),
|
|
57
|
-
...props
|
|
58
|
-
});
|
|
17
|
+
// src/lib/services.ts
|
|
18
|
+
import { OneSatServices } from "@1sat/client";
|
|
19
|
+
var _services = null;
|
|
20
|
+
var _baseUrl;
|
|
21
|
+
function configureServices(baseUrl) {
|
|
22
|
+
_baseUrl = baseUrl;
|
|
23
|
+
_services = null;
|
|
59
24
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const context = React.useContext(TabsContext);
|
|
67
|
-
if (!context) {
|
|
68
|
-
throw new Error("Tabs components must be used within a <Tabs> provider");
|
|
25
|
+
function getServices() {
|
|
26
|
+
if (!_services) {
|
|
27
|
+
const url = _baseUrl ?? (typeof window !== "undefined" ? window.location.origin : "");
|
|
28
|
+
if (!url)
|
|
29
|
+
throw new Error("No base URL configured. Call configureServices() first.");
|
|
30
|
+
_services = new OneSatServices("main", url);
|
|
69
31
|
}
|
|
70
|
-
return
|
|
32
|
+
return _services;
|
|
71
33
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
"data-slot": "tabs",
|
|
77
|
-
className: cn("flex flex-col gap-2", className),
|
|
78
|
-
...props
|
|
79
|
-
})
|
|
80
|
-
});
|
|
34
|
+
|
|
35
|
+
// src/lib/scanner.ts
|
|
36
|
+
function deriveAddress(wif) {
|
|
37
|
+
return PrivateKey.fromWif(wif.trim()).toPublicKey().toAddress();
|
|
81
38
|
}
|
|
82
|
-
function
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
className: cn("inline-flex h-9 items-center justify-start gap-1 rounded-lg bg-muted p-1 text-muted-foreground", className),
|
|
86
|
-
...props
|
|
87
|
-
});
|
|
39
|
+
function getEvent(events, prefix) {
|
|
40
|
+
const e = events.find((ev) => ev.startsWith(prefix));
|
|
41
|
+
return e ? e.slice(prefix.length) : undefined;
|
|
88
42
|
}
|
|
89
|
-
function
|
|
90
|
-
|
|
91
|
-
const isActive = context.value === value;
|
|
92
|
-
return /* @__PURE__ */ jsx2("button", {
|
|
93
|
-
"data-slot": "tabs-trigger",
|
|
94
|
-
"data-active": isActive ? "" : undefined,
|
|
95
|
-
className: cn("inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50", isActive && "bg-background text-foreground shadow-sm", className),
|
|
96
|
-
onClick: () => context.onValueChange(value),
|
|
97
|
-
...props
|
|
98
|
-
});
|
|
43
|
+
function getEvents(events, prefix) {
|
|
44
|
+
return events.filter((e) => e.startsWith(prefix)).map((e) => e.slice(prefix.length));
|
|
99
45
|
}
|
|
100
|
-
function
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
46
|
+
function enrichOrdinal(out) {
|
|
47
|
+
const events = out.events ?? [];
|
|
48
|
+
const origin = getEvent(events, "origin:");
|
|
49
|
+
const types = getEvents(events, "type:");
|
|
50
|
+
const contentType = types.find((t) => t.includes("/")) ?? types[0];
|
|
51
|
+
const name = getEvent(events, "name:");
|
|
52
|
+
const contentUrl = getServices().ordfs.getContentUrl(origin ?? out.outpoint, {
|
|
53
|
+
raw: true
|
|
108
54
|
});
|
|
55
|
+
return { ...out, origin, contentType, name, contentUrl };
|
|
109
56
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
118
|
-
var buttonVariants = cva2("inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
|
|
119
|
-
variants: {
|
|
120
|
-
variant: {
|
|
121
|
-
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
122
|
-
destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
|
|
123
|
-
outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
|
|
124
|
-
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
125
|
-
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
126
|
-
link: "text-primary underline-offset-4 hover:underline"
|
|
127
|
-
},
|
|
128
|
-
size: {
|
|
129
|
-
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
130
|
-
xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
131
|
-
sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
|
|
132
|
-
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
133
|
-
icon: "size-9",
|
|
134
|
-
"icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
|
|
135
|
-
"icon-sm": "size-8",
|
|
136
|
-
"icon-lg": "size-10"
|
|
137
|
-
}
|
|
138
|
-
},
|
|
139
|
-
defaultVariants: {
|
|
140
|
-
variant: "default",
|
|
141
|
-
size: "default"
|
|
57
|
+
function resolveIconUrl(tokenId, icon) {
|
|
58
|
+
if (!icon)
|
|
59
|
+
return "";
|
|
60
|
+
let outpoint = icon;
|
|
61
|
+
if (icon.startsWith("_")) {
|
|
62
|
+
const txid = tokenId.split("_")[0];
|
|
63
|
+
outpoint = `${txid}${icon}`;
|
|
142
64
|
}
|
|
143
|
-
|
|
144
|
-
function Button({
|
|
145
|
-
className,
|
|
146
|
-
variant = "default",
|
|
147
|
-
size = "default",
|
|
148
|
-
...props
|
|
149
|
-
}) {
|
|
150
|
-
return /* @__PURE__ */ jsx3("button", {
|
|
151
|
-
"data-slot": "button",
|
|
152
|
-
"data-variant": variant,
|
|
153
|
-
"data-size": size,
|
|
154
|
-
className: cn(buttonVariants({ variant, size, className })),
|
|
155
|
-
...props
|
|
156
|
-
});
|
|
65
|
+
return getServices().ordfs.getContentUrl(outpoint);
|
|
157
66
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
const result = await connect();
|
|
164
|
-
if (!result)
|
|
165
|
-
throw new Error("No wallet available");
|
|
166
|
-
connection = result;
|
|
167
|
-
return result;
|
|
67
|
+
function enrichTokenBalances(tokens) {
|
|
68
|
+
return tokens.map((t) => ({
|
|
69
|
+
...t,
|
|
70
|
+
icon: resolveIconUrl(t.tokenId, t.icon)
|
|
71
|
+
}));
|
|
168
72
|
}
|
|
169
|
-
function
|
|
170
|
-
|
|
73
|
+
async function scanAddress(address, onProgress) {
|
|
74
|
+
const result = await coreScanAddresses(getServices(), [address], onProgress);
|
|
75
|
+
return toScannedAssets(result);
|
|
171
76
|
}
|
|
172
|
-
function
|
|
173
|
-
|
|
77
|
+
async function scanAddresses(addresses, onProgress) {
|
|
78
|
+
const unique = [...new Set(addresses)];
|
|
79
|
+
const result = await coreScanAddresses(getServices(), unique, onProgress);
|
|
80
|
+
return toScannedAssets(result);
|
|
174
81
|
}
|
|
175
|
-
function
|
|
176
|
-
return
|
|
82
|
+
function toScannedAssets(result) {
|
|
83
|
+
return {
|
|
84
|
+
funding: result.funding,
|
|
85
|
+
ordinals: result.ordinals.map(enrichOrdinal),
|
|
86
|
+
opnsNames: result.opnsNames.map(enrichOrdinal),
|
|
87
|
+
bsv21Tokens: enrichTokenBalances(result.bsv21Tokens),
|
|
88
|
+
bsv20Tokens: result.bsv20Tokens,
|
|
89
|
+
locked: result.locked,
|
|
90
|
+
run: result.run,
|
|
91
|
+
totalFundingSats: result.totalFundingSats,
|
|
92
|
+
totalBsv: result.totalFundingSats
|
|
93
|
+
};
|
|
177
94
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
95
|
+
|
|
96
|
+
// src/lib/legacy-send.ts
|
|
97
|
+
async function fetchSourceTx(txid) {
|
|
98
|
+
const services = getServices();
|
|
99
|
+
const beef = await services.getBeefForTxid(txid);
|
|
100
|
+
const found = beef.findTxid(txid);
|
|
101
|
+
if (!found?.tx)
|
|
102
|
+
throw new Error(`Transaction ${txid} not found in BEEF`);
|
|
103
|
+
return found.tx;
|
|
181
104
|
}
|
|
182
|
-
function
|
|
183
|
-
|
|
105
|
+
function buildKeyMap(keys) {
|
|
106
|
+
const map = new Map;
|
|
107
|
+
const payKey = PrivateKey2.fromWif(keys.payPk);
|
|
108
|
+
const ordKey = PrivateKey2.fromWif(keys.ordPk);
|
|
109
|
+
map.set(deriveAddress(keys.payPk), payKey);
|
|
110
|
+
map.set(deriveAddress(keys.ordPk), ordKey);
|
|
111
|
+
if (keys.identityPk) {
|
|
112
|
+
map.set(deriveAddress(keys.identityPk), PrivateKey2.fromWif(keys.identityPk));
|
|
113
|
+
}
|
|
114
|
+
return map;
|
|
184
115
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
try {
|
|
195
|
-
await connectWallet();
|
|
196
|
-
onConnected();
|
|
197
|
-
} catch (e) {
|
|
198
|
-
setError(e instanceof Error ? e.message : "Failed to connect wallet");
|
|
199
|
-
} finally {
|
|
200
|
-
setConnecting(false);
|
|
116
|
+
function keyForOutput(output, keyMap, fallback) {
|
|
117
|
+
if (output.events) {
|
|
118
|
+
for (const event of output.events) {
|
|
119
|
+
if (event.startsWith("own:") || event.startsWith("p2pkh:")) {
|
|
120
|
+
const addr = event.split(":")[1];
|
|
121
|
+
const key = keyMap.get(addr);
|
|
122
|
+
if (key)
|
|
123
|
+
return key;
|
|
124
|
+
}
|
|
201
125
|
}
|
|
202
126
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
127
|
+
return fallback;
|
|
128
|
+
}
|
|
129
|
+
async function legacySendBsv(params) {
|
|
130
|
+
const { funding, keys, destination, amount } = params;
|
|
131
|
+
if (!funding.length)
|
|
132
|
+
throw new Error("No funding UTXOs");
|
|
133
|
+
if (!destination)
|
|
134
|
+
throw new Error("No destination address");
|
|
135
|
+
const keyMap = buildKeyMap(keys);
|
|
136
|
+
const payKey = PrivateKey2.fromWif(keys.payPk);
|
|
137
|
+
const sourceAddress = payKey.toPublicKey().toAddress();
|
|
138
|
+
const p2pkh = new P2PKH;
|
|
139
|
+
const tx = new Transaction;
|
|
140
|
+
for (const utxo of funding) {
|
|
141
|
+
const { txid, vout } = parseOutpoint(utxo.outpoint);
|
|
142
|
+
const key = keyForOutput(utxo, keyMap, payKey);
|
|
143
|
+
tx.addInput({
|
|
144
|
+
sourceTXID: txid,
|
|
145
|
+
sourceOutputIndex: vout,
|
|
146
|
+
sourceTransaction: await fetchSourceTx(txid),
|
|
147
|
+
unlockingScriptTemplate: p2pkh.unlock(key),
|
|
148
|
+
sequence: 4294967295
|
|
149
|
+
});
|
|
206
150
|
}
|
|
207
|
-
if (
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
getProvider() === "brc100" ? "BRC-100" : "OneSat",
|
|
221
|
-
" · ",
|
|
222
|
-
getIdentityKey()?.slice(0, 12),
|
|
223
|
-
"..."
|
|
224
|
-
]
|
|
225
|
-
})
|
|
226
|
-
]
|
|
227
|
-
}),
|
|
228
|
-
/* @__PURE__ */ jsx4(Button, {
|
|
229
|
-
variant: "ghost",
|
|
230
|
-
size: "sm",
|
|
231
|
-
className: "h-6 w-6 p-0",
|
|
232
|
-
onClick: handleDisconnect,
|
|
233
|
-
children: /* @__PURE__ */ jsx4(X, {
|
|
234
|
-
className: "h-3 w-3"
|
|
235
|
-
})
|
|
236
|
-
})
|
|
237
|
-
]
|
|
151
|
+
if (amount) {
|
|
152
|
+
tx.addOutput({
|
|
153
|
+
lockingScript: p2pkh.lock(destination),
|
|
154
|
+
satoshis: amount
|
|
155
|
+
});
|
|
156
|
+
tx.addOutput({
|
|
157
|
+
lockingScript: p2pkh.lock(sourceAddress),
|
|
158
|
+
change: true
|
|
159
|
+
});
|
|
160
|
+
} else {
|
|
161
|
+
tx.addOutput({
|
|
162
|
+
lockingScript: p2pkh.lock(destination),
|
|
163
|
+
change: true
|
|
238
164
|
});
|
|
239
165
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
disabled: connecting,
|
|
249
|
-
children: [
|
|
250
|
-
connecting ? /* @__PURE__ */ jsx4(Loader2, {
|
|
251
|
-
className: "h-3 w-3 animate-spin"
|
|
252
|
-
}) : /* @__PURE__ */ jsx4(Wallet, {
|
|
253
|
-
className: "h-3 w-3"
|
|
254
|
-
}),
|
|
255
|
-
connecting ? "Connecting..." : "Connect BRC-100 Wallet (optional)"
|
|
256
|
-
]
|
|
257
|
-
}),
|
|
258
|
-
error && /* @__PURE__ */ jsx4("p", {
|
|
259
|
-
className: "text-xs text-destructive text-center",
|
|
260
|
-
children: error
|
|
261
|
-
})
|
|
262
|
-
]
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// src/components/wif-input.tsx
|
|
267
|
-
import { useCallback, useRef, useState as useState2 } from "react";
|
|
268
|
-
import { KeyRound, Loader2 as Loader22, Search, Upload, X as X2 } from "lucide-react";
|
|
269
|
-
|
|
270
|
-
// src/components/ui/card.tsx
|
|
271
|
-
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
272
|
-
function Card({ className, ...props }) {
|
|
273
|
-
return /* @__PURE__ */ jsx5("div", {
|
|
274
|
-
"data-slot": "card",
|
|
275
|
-
className: cn("flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-sm", className),
|
|
276
|
-
...props
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
function CardHeader({ className, ...props }) {
|
|
280
|
-
return /* @__PURE__ */ jsx5("div", {
|
|
281
|
-
"data-slot": "card-header",
|
|
282
|
-
className: cn("@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6", className),
|
|
283
|
-
...props
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
|
-
function CardTitle({ className, ...props }) {
|
|
287
|
-
return /* @__PURE__ */ jsx5("div", {
|
|
288
|
-
"data-slot": "card-title",
|
|
289
|
-
className: cn("leading-none font-semibold", className),
|
|
290
|
-
...props
|
|
291
|
-
});
|
|
166
|
+
await tx.fee();
|
|
167
|
+
await tx.sign();
|
|
168
|
+
const rawTx = tx.toBinary();
|
|
169
|
+
const result = await getServices().arcade.submitTransaction(rawTx);
|
|
170
|
+
return {
|
|
171
|
+
txid: result.txid,
|
|
172
|
+
rawtx: Utils.toHex(rawTx)
|
|
173
|
+
};
|
|
292
174
|
}
|
|
293
|
-
function
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
175
|
+
async function legacySendOrdinals(params) {
|
|
176
|
+
const { ordinals, funding, keys, destination } = params;
|
|
177
|
+
if (!ordinals.length)
|
|
178
|
+
throw new Error("No ordinals to send");
|
|
179
|
+
if (!funding.length)
|
|
180
|
+
throw new Error("No funding UTXOs for fees");
|
|
181
|
+
if (!destination)
|
|
182
|
+
throw new Error("No destination address");
|
|
183
|
+
const keyMap = buildKeyMap(keys);
|
|
184
|
+
const payKey = PrivateKey2.fromWif(keys.payPk);
|
|
185
|
+
const sourceAddress = payKey.toPublicKey().toAddress();
|
|
186
|
+
const p2pkh = new P2PKH;
|
|
187
|
+
const tx = new Transaction;
|
|
188
|
+
for (const ord of ordinals) {
|
|
189
|
+
const { txid, vout } = parseOutpoint(ord.outpoint);
|
|
190
|
+
const key = keyForOutput(ord, keyMap, payKey);
|
|
191
|
+
tx.addInput({
|
|
192
|
+
sourceTXID: txid,
|
|
193
|
+
sourceOutputIndex: vout,
|
|
194
|
+
sourceTransaction: await fetchSourceTx(txid),
|
|
195
|
+
unlockingScriptTemplate: p2pkh.unlock(key),
|
|
196
|
+
sequence: 4294967295
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
for (const _ord of ordinals) {
|
|
200
|
+
tx.addOutput({
|
|
201
|
+
lockingScript: p2pkh.lock(destination),
|
|
202
|
+
satoshis: 1
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
for (const utxo of funding) {
|
|
206
|
+
const { txid, vout } = parseOutpoint(utxo.outpoint);
|
|
207
|
+
const key = keyForOutput(utxo, keyMap, payKey);
|
|
208
|
+
tx.addInput({
|
|
209
|
+
sourceTXID: txid,
|
|
210
|
+
sourceOutputIndex: vout,
|
|
211
|
+
sourceTransaction: await fetchSourceTx(txid),
|
|
212
|
+
unlockingScriptTemplate: p2pkh.unlock(key),
|
|
213
|
+
sequence: 4294967295
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
tx.addOutput({
|
|
217
|
+
lockingScript: p2pkh.lock(sourceAddress),
|
|
218
|
+
change: true
|
|
298
219
|
});
|
|
220
|
+
await tx.fee();
|
|
221
|
+
await tx.sign();
|
|
222
|
+
const rawTx = tx.toBinary();
|
|
223
|
+
const result = await getServices().arcade.submitTransaction(rawTx);
|
|
224
|
+
return {
|
|
225
|
+
txid: result.txid,
|
|
226
|
+
rawtx: Utils.toHex(rawTx)
|
|
227
|
+
};
|
|
299
228
|
}
|
|
300
|
-
function
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
229
|
+
async function legacyBurnOrdinals(params) {
|
|
230
|
+
const { ordinals, funding, keys } = params;
|
|
231
|
+
if (!ordinals.length)
|
|
232
|
+
throw new Error("No ordinals to burn");
|
|
233
|
+
const keyMap = buildKeyMap(keys);
|
|
234
|
+
const payKey = PrivateKey2.fromWif(keys.payPk);
|
|
235
|
+
const sourceAddress = payKey.toPublicKey().toAddress();
|
|
236
|
+
const p2pkh = new P2PKH;
|
|
237
|
+
const tx = new Transaction;
|
|
238
|
+
for (const ord of ordinals) {
|
|
239
|
+
const { txid, vout } = parseOutpoint(ord.outpoint);
|
|
240
|
+
const key = keyForOutput(ord, keyMap, payKey);
|
|
241
|
+
tx.addInput({
|
|
242
|
+
sourceTXID: txid,
|
|
243
|
+
sourceOutputIndex: vout,
|
|
244
|
+
sourceTransaction: await fetchSourceTx(txid),
|
|
245
|
+
unlockingScriptTemplate: p2pkh.unlock(key),
|
|
246
|
+
sequence: 4294967295
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
for (const utxo of funding) {
|
|
250
|
+
const { txid, vout } = parseOutpoint(utxo.outpoint);
|
|
251
|
+
const key = keyForOutput(utxo, keyMap, payKey);
|
|
252
|
+
tx.addInput({
|
|
253
|
+
sourceTXID: txid,
|
|
254
|
+
sourceOutputIndex: vout,
|
|
255
|
+
sourceTransaction: await fetchSourceTx(txid),
|
|
256
|
+
unlockingScriptTemplate: p2pkh.unlock(key),
|
|
257
|
+
sequence: 4294967295
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
const burnScript = new Script().writeOpCode(OP.OP_FALSE).writeOpCode(OP.OP_RETURN).writeBin(Utils.toArray(MAP_PREFIX)).writeBin(Utils.toArray("SET")).writeBin(Utils.toArray("app")).writeBin(Utils.toArray("1sat-sweep")).writeBin(Utils.toArray("type")).writeBin(Utils.toArray("ord")).writeBin(Utils.toArray("op")).writeBin(Utils.toArray("burn"));
|
|
261
|
+
tx.addOutput({ satoshis: 0, lockingScript: burnScript });
|
|
262
|
+
tx.addOutput({
|
|
263
|
+
lockingScript: p2pkh.lock(sourceAddress),
|
|
264
|
+
change: true
|
|
305
265
|
});
|
|
266
|
+
await tx.fee();
|
|
267
|
+
await tx.sign();
|
|
268
|
+
const rawTx = tx.toBinary();
|
|
269
|
+
const result = await getServices().arcade.submitTransaction(rawTx);
|
|
270
|
+
return {
|
|
271
|
+
txid: result.txid,
|
|
272
|
+
rawtx: Utils.toHex(rawTx)
|
|
273
|
+
};
|
|
306
274
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
275
|
+
|
|
276
|
+
// src/lib/sweeper.ts
|
|
277
|
+
import {
|
|
278
|
+
createContext,
|
|
279
|
+
prepareSweepInputs,
|
|
280
|
+
sweepBsv,
|
|
281
|
+
sweepBsv21,
|
|
282
|
+
sweepOrdinals
|
|
283
|
+
} from "@1sat/actions";
|
|
284
|
+
function getOwner(output) {
|
|
285
|
+
return output.events?.find((e) => e.startsWith("own:"))?.slice(4);
|
|
313
286
|
}
|
|
314
|
-
function
|
|
315
|
-
return
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
287
|
+
function buildKeys(outputs, keyMap) {
|
|
288
|
+
return outputs.map((output) => {
|
|
289
|
+
const owner = getOwner(output);
|
|
290
|
+
const key = owner ? keyMap.get(owner) : undefined;
|
|
291
|
+
if (!key)
|
|
292
|
+
throw new Error(`No key for output ${output.outpoint} (owner: ${owner})`);
|
|
293
|
+
return key;
|
|
319
294
|
});
|
|
320
295
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
296
|
+
async function executeSweep(params) {
|
|
297
|
+
const { wallet, keys, funding, ordinals, amount, onProgress } = params;
|
|
298
|
+
const ctx = createContext(wallet, { services: getServices(), chain: "main" });
|
|
299
|
+
const result = {
|
|
300
|
+
ordinalTxids: [],
|
|
301
|
+
bsv21Txids: [],
|
|
302
|
+
errors: []
|
|
303
|
+
};
|
|
304
|
+
if (funding.length > 0) {
|
|
305
|
+
onProgress(`Sweeping ${funding.length} BSV UTXOs...`);
|
|
306
|
+
try {
|
|
307
|
+
const inputs = await prepareSweepInputs(ctx, funding);
|
|
308
|
+
const bsvResult = await sweepBsv.execute(ctx, {
|
|
309
|
+
inputs,
|
|
310
|
+
keys: buildKeys(funding, keys),
|
|
311
|
+
amount
|
|
312
|
+
});
|
|
313
|
+
if (bsvResult.error)
|
|
314
|
+
result.errors.push(`BSV: ${bsvResult.error}`);
|
|
315
|
+
else if (bsvResult.txid)
|
|
316
|
+
result.bsvTxid = bsvResult.txid;
|
|
317
|
+
} catch (e) {
|
|
318
|
+
result.errors.push(`BSV: ${e instanceof Error ? e.message : String(e)}`);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if (ordinals.length > 0) {
|
|
322
|
+
onProgress(`Sweeping ${ordinals.length} ordinals...`);
|
|
323
|
+
try {
|
|
324
|
+
const inputs = await prepareSweepInputs(ctx, ordinals);
|
|
325
|
+
const ordResult = await sweepOrdinals.execute(ctx, {
|
|
326
|
+
inputs,
|
|
327
|
+
keys: buildKeys(ordinals, keys)
|
|
328
|
+
});
|
|
329
|
+
if (ordResult.error)
|
|
330
|
+
result.errors.push(`Ordinals: ${ordResult.error}`);
|
|
331
|
+
else if (ordResult.txid)
|
|
332
|
+
result.ordinalTxids.push(ordResult.txid);
|
|
333
|
+
} catch (e) {
|
|
334
|
+
result.errors.push(`Ordinals: ${e instanceof Error ? e.message : String(e)}`);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
onProgress("Sweep complete");
|
|
338
|
+
return result;
|
|
355
339
|
}
|
|
356
|
-
function
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
340
|
+
async function sweepBsv21Token(params) {
|
|
341
|
+
const { wallet, keys, token, onProgress } = params;
|
|
342
|
+
const ctx = createContext(wallet, { services: getServices(), chain: "main" });
|
|
343
|
+
onProgress(`Sweeping ${token.symbol ?? token.tokenId.slice(0, 8)}...`);
|
|
344
|
+
try {
|
|
345
|
+
const inputs = token.outputs.map((out) => ({
|
|
346
|
+
outpoint: out.outpoint,
|
|
347
|
+
tokenId: token.tokenId,
|
|
348
|
+
amount: token.amounts.get(out.outpoint) ?? "0"
|
|
349
|
+
}));
|
|
350
|
+
const tokenKeys = buildKeys(token.outputs, keys);
|
|
351
|
+
const result = await sweepBsv21.execute(ctx, { inputs, keys: tokenKeys });
|
|
352
|
+
if (result.error)
|
|
353
|
+
return { error: result.error };
|
|
354
|
+
return { txid: result.txid };
|
|
355
|
+
} catch (e) {
|
|
356
|
+
return { error: e instanceof Error ? e.message : String(e) };
|
|
362
357
|
}
|
|
363
|
-
return _services;
|
|
364
358
|
}
|
|
365
359
|
|
|
366
|
-
// src/lib/
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
360
|
+
// src/lib/wallet.ts
|
|
361
|
+
import {
|
|
362
|
+
connectWallet as connect
|
|
363
|
+
} from "@1sat/connect";
|
|
364
|
+
var connection = null;
|
|
365
|
+
async function connectWallet() {
|
|
366
|
+
const result = await connect();
|
|
367
|
+
if (!result)
|
|
368
|
+
throw new Error("No wallet available");
|
|
369
|
+
connection = result;
|
|
370
|
+
return result;
|
|
373
371
|
}
|
|
374
|
-
function
|
|
375
|
-
return
|
|
372
|
+
function getWallet() {
|
|
373
|
+
return connection?.wallet ?? null;
|
|
376
374
|
}
|
|
377
|
-
function
|
|
378
|
-
|
|
379
|
-
const origin = getEvent(events, "origin:");
|
|
380
|
-
const types = getEvents(events, "type:");
|
|
381
|
-
const contentType = types.find((t) => t.includes("/")) ?? types[0];
|
|
382
|
-
const name = getEvent(events, "name:");
|
|
383
|
-
const contentUrl = getServices().ordfs.getContentUrl(origin ?? out.outpoint, { raw: true });
|
|
384
|
-
return { ...out, origin, contentType, name, contentUrl };
|
|
375
|
+
function getIdentityKey() {
|
|
376
|
+
return connection?.identityKey ?? null;
|
|
385
377
|
}
|
|
386
|
-
function
|
|
387
|
-
|
|
388
|
-
return "";
|
|
389
|
-
let outpoint = icon;
|
|
390
|
-
if (icon.startsWith("_")) {
|
|
391
|
-
const txid = tokenId.split("_")[0];
|
|
392
|
-
outpoint = `${txid}${icon}`;
|
|
393
|
-
}
|
|
394
|
-
return getServices().ordfs.getContentUrl(outpoint);
|
|
378
|
+
function getProvider() {
|
|
379
|
+
return connection?.provider ?? null;
|
|
395
380
|
}
|
|
396
|
-
function
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
icon: resolveIconUrl(t.tokenId, t.icon)
|
|
400
|
-
}));
|
|
381
|
+
function disconnectWallet() {
|
|
382
|
+
connection?.disconnect();
|
|
383
|
+
connection = null;
|
|
401
384
|
}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
return toScannedAssets(result);
|
|
385
|
+
function isConnected() {
|
|
386
|
+
return connection !== null;
|
|
405
387
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
388
|
+
|
|
389
|
+
// src/components/asset-preview.tsx
|
|
390
|
+
import { useState } from "react";
|
|
391
|
+
|
|
392
|
+
// src/lib/utils.ts
|
|
393
|
+
import { clsx } from "clsx";
|
|
394
|
+
import { twMerge } from "tailwind-merge";
|
|
395
|
+
function cn(...inputs) {
|
|
396
|
+
return twMerge(clsx(inputs));
|
|
410
397
|
}
|
|
411
|
-
function
|
|
412
|
-
return
|
|
413
|
-
funding: result.funding,
|
|
414
|
-
ordinals: result.ordinals.map(enrichOrdinal),
|
|
415
|
-
opnsNames: result.opnsNames.map(enrichOrdinal),
|
|
416
|
-
bsv21Tokens: enrichTokenBalances(result.bsv21Tokens),
|
|
417
|
-
bsv20Tokens: result.bsv20Tokens,
|
|
418
|
-
locked: result.locked,
|
|
419
|
-
run: result.run,
|
|
420
|
-
totalFundingSats: result.totalFundingSats,
|
|
421
|
-
totalBsv: result.totalFundingSats
|
|
422
|
-
};
|
|
398
|
+
function formatSats(sats) {
|
|
399
|
+
return sats.toLocaleString();
|
|
423
400
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
return { ...keys, identityPk: deriveIdentityKey(keys.payPk, keys.ordPk).toWif() };
|
|
401
|
+
function formatTokenAmount(rawAmount, decimals) {
|
|
402
|
+
if (decimals === 0)
|
|
403
|
+
return rawAmount;
|
|
404
|
+
const padded = rawAmount.padStart(decimals + 1, "0");
|
|
405
|
+
const intPart = padded.slice(0, -decimals) || "0";
|
|
406
|
+
const decPart = padded.slice(-decimals).replace(/0+$/, "");
|
|
407
|
+
return decPart ? `${intPart}.${decPart}` : intPart;
|
|
432
408
|
}
|
|
433
|
-
function
|
|
434
|
-
if (
|
|
435
|
-
return
|
|
436
|
-
|
|
437
|
-
ordPk: backup.ordPk,
|
|
438
|
-
identityPk: backup.identityPk
|
|
439
|
-
});
|
|
440
|
-
}
|
|
441
|
-
if (isYoursWalletBackup(backup)) {
|
|
442
|
-
return withIdentityKey({
|
|
443
|
-
payPk: backup.payPk,
|
|
444
|
-
ordPk: backup.ordPk,
|
|
445
|
-
identityPk: backup.identityPk
|
|
446
|
-
});
|
|
447
|
-
}
|
|
448
|
-
if (isWifBackup(backup)) {
|
|
449
|
-
return withIdentityKey({
|
|
450
|
-
payPk: backup.wif,
|
|
451
|
-
ordPk: backup.wif
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
return null;
|
|
409
|
+
function truncate(s, len = 8) {
|
|
410
|
+
if (s.length <= len * 2 + 3)
|
|
411
|
+
return s;
|
|
412
|
+
return `${s.slice(0, len)}...${s.slice(-len)}`;
|
|
455
413
|
}
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
return null;
|
|
470
|
-
}
|
|
414
|
+
|
|
415
|
+
// src/components/ui/badge.tsx
|
|
416
|
+
import { cva } from "class-variance-authority";
|
|
417
|
+
import { jsx } from "react/jsx-runtime";
|
|
418
|
+
var badgeVariants = cva("inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3", {
|
|
419
|
+
variants: {
|
|
420
|
+
variant: {
|
|
421
|
+
default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
|
|
422
|
+
secondary: "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
|
|
423
|
+
destructive: "bg-destructive text-white focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90",
|
|
424
|
+
outline: "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
425
|
+
ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
|
426
|
+
link: "text-primary underline-offset-4 [a&]:hover:underline"
|
|
471
427
|
}
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
428
|
+
},
|
|
429
|
+
defaultVariants: {
|
|
430
|
+
variant: "default"
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
function Badge({
|
|
434
|
+
className,
|
|
435
|
+
variant = "default",
|
|
436
|
+
...props
|
|
437
|
+
}) {
|
|
438
|
+
return /* @__PURE__ */ jsx("span", {
|
|
439
|
+
"data-slot": "badge",
|
|
440
|
+
"data-variant": variant,
|
|
441
|
+
className: cn(badgeVariants({ variant }), className),
|
|
442
|
+
...props
|
|
443
|
+
});
|
|
481
444
|
}
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
}, []);
|
|
506
|
-
const processBackupText = useCallback(async (text) => {
|
|
507
|
-
setError("");
|
|
508
|
-
setBackupText(text);
|
|
509
|
-
const directKeys = tryParseBackup(text);
|
|
510
|
-
if (directKeys) {
|
|
511
|
-
setKeys(directKeys);
|
|
512
|
-
setBackupType("JSON");
|
|
513
|
-
return;
|
|
514
|
-
}
|
|
515
|
-
try {
|
|
516
|
-
const parsed = JSON.parse(text);
|
|
517
|
-
if (parsed.encryptedBackup) {
|
|
518
|
-
setBackupText(parsed.encryptedBackup);
|
|
519
|
-
setNeedsPassword(true);
|
|
520
|
-
return;
|
|
521
|
-
}
|
|
522
|
-
if (parsed.encryptedKeys) {
|
|
523
|
-
setBackupText(parsed.encryptedKeys);
|
|
524
|
-
setNeedsPassword(true);
|
|
525
|
-
return;
|
|
526
|
-
}
|
|
527
|
-
if (parsed.accounts && parsed.selectedAccount) {
|
|
528
|
-
const account = parsed.accounts[parsed.selectedAccount];
|
|
529
|
-
if (account?.encryptedKeys) {
|
|
530
|
-
setBackupText(account.encryptedKeys);
|
|
531
|
-
setNeedsPassword(true);
|
|
532
|
-
return;
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
} catch {}
|
|
536
|
-
if (looksEncrypted(text)) {
|
|
537
|
-
setNeedsPassword(true);
|
|
538
|
-
return;
|
|
539
|
-
}
|
|
540
|
-
setError("Unrecognized backup format");
|
|
541
|
-
}, []);
|
|
542
|
-
const handleDecrypt = useCallback(async () => {
|
|
543
|
-
if (!backupText || !password)
|
|
544
|
-
return;
|
|
545
|
-
setDecrypting(true);
|
|
546
|
-
setError("");
|
|
547
|
-
try {
|
|
548
|
-
const decrypted = await decryptBackup(backupText, password);
|
|
549
|
-
const extracted = extractKeys(decrypted);
|
|
550
|
-
if (!extracted) {
|
|
551
|
-
setError(`Unsupported backup type: ${getBackupType(decrypted)}`);
|
|
552
|
-
return;
|
|
553
|
-
}
|
|
554
|
-
setKeys(extracted);
|
|
555
|
-
setBackupType(getBackupType(decrypted));
|
|
556
|
-
setNeedsPassword(false);
|
|
557
|
-
} catch (e) {
|
|
558
|
-
setError(e instanceof Error ? e.message : "Decryption failed");
|
|
559
|
-
} finally {
|
|
560
|
-
setDecrypting(false);
|
|
561
|
-
}
|
|
562
|
-
}, [backupText, password]);
|
|
563
|
-
const handleFileUpload = useCallback((e) => {
|
|
564
|
-
const file = e.target.files?.[0];
|
|
565
|
-
if (!file)
|
|
566
|
-
return;
|
|
567
|
-
const reader = new FileReader;
|
|
568
|
-
reader.onload = () => {
|
|
569
|
-
processBackupText(reader.result.trim());
|
|
570
|
-
};
|
|
571
|
-
reader.readAsText(file);
|
|
572
|
-
e.target.value = "";
|
|
573
|
-
}, [processBackupText]);
|
|
574
|
-
const handleScan = useCallback(() => {
|
|
575
|
-
if (keys) {
|
|
576
|
-
onScan(keys);
|
|
577
|
-
} else if (mode === "wif") {
|
|
578
|
-
const pay = payWif.trim();
|
|
579
|
-
const ord = sameKey ? pay : ordWif.trim();
|
|
580
|
-
if (pay)
|
|
581
|
-
onScan({ payPk: pay, ordPk: ord });
|
|
445
|
+
|
|
446
|
+
// src/components/ui/button.tsx
|
|
447
|
+
import { cva as cva2 } from "class-variance-authority";
|
|
448
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
449
|
+
var buttonVariants = cva2("inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", {
|
|
450
|
+
variants: {
|
|
451
|
+
variant: {
|
|
452
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
453
|
+
destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
|
|
454
|
+
outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
|
|
455
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
456
|
+
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
457
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
458
|
+
},
|
|
459
|
+
size: {
|
|
460
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
461
|
+
xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
462
|
+
sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
|
|
463
|
+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
464
|
+
icon: "size-9",
|
|
465
|
+
"icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
|
|
466
|
+
"icon-sm": "size-8",
|
|
467
|
+
"icon-lg": "size-10"
|
|
582
468
|
}
|
|
583
|
-
},
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
469
|
+
},
|
|
470
|
+
defaultVariants: {
|
|
471
|
+
variant: "default",
|
|
472
|
+
size: "default"
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
function Button({
|
|
476
|
+
className,
|
|
477
|
+
variant = "default",
|
|
478
|
+
size = "default",
|
|
479
|
+
...props
|
|
480
|
+
}) {
|
|
481
|
+
return /* @__PURE__ */ jsx2("button", {
|
|
482
|
+
"data-slot": "button",
|
|
483
|
+
"data-variant": variant,
|
|
484
|
+
"data-size": size,
|
|
485
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
486
|
+
...props
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// src/components/ui/input.tsx
|
|
491
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
492
|
+
function Input({ className, type, ...props }) {
|
|
493
|
+
return /* @__PURE__ */ jsx3("input", {
|
|
494
|
+
type,
|
|
495
|
+
"data-slot": "input",
|
|
496
|
+
className: cn("h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30", "focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50", "aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40", className),
|
|
497
|
+
...props
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// src/components/asset-preview.tsx
|
|
502
|
+
import { jsx as jsx4, jsxs } from "react/jsx-runtime";
|
|
503
|
+
var ORDINALS_PER_PAGE = 20;
|
|
504
|
+
function isImageType(ct) {
|
|
505
|
+
return ct.startsWith("image/") && ct !== "image/svg+xml";
|
|
506
|
+
}
|
|
507
|
+
function OrdinalCard({
|
|
508
|
+
ordinal,
|
|
509
|
+
isSelected,
|
|
510
|
+
onToggle
|
|
511
|
+
}) {
|
|
512
|
+
const ct = ordinal.contentType ?? "";
|
|
513
|
+
const isImage = isImageType(ct);
|
|
514
|
+
const subtype = ct.includes("/") ? ct.split("/")[1] : ct;
|
|
515
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
516
|
+
className: `relative p-2 rounded-lg border cursor-pointer transition-all ${isSelected ? "border-blue-500 bg-blue-500/10 ring-1 ring-blue-500/30" : "border-border/50 hover:border-border bg-black/20"}`,
|
|
517
|
+
onClick: onToggle,
|
|
518
|
+
children: [
|
|
519
|
+
/* @__PURE__ */ jsx4("div", {
|
|
520
|
+
className: "absolute top-1.5 right-1.5 z-10",
|
|
521
|
+
children: /* @__PURE__ */ jsx4("div", {
|
|
522
|
+
className: `w-4 h-4 rounded border-2 flex items-center justify-center text-[10px] ${isSelected ? "bg-blue-500 border-blue-500 text-white" : "border-muted-foreground/40"}`,
|
|
523
|
+
children: isSelected && "✓"
|
|
524
|
+
})
|
|
525
|
+
}),
|
|
526
|
+
/* @__PURE__ */ jsx4("div", {
|
|
527
|
+
className: "w-full aspect-square mb-1.5 rounded overflow-hidden bg-black/30 flex items-center justify-center",
|
|
528
|
+
children: !ordinal.contentUrl ? /* @__PURE__ */ jsx4("span", {
|
|
529
|
+
className: "text-muted-foreground text-lg",
|
|
530
|
+
children: "◆"
|
|
531
|
+
}) : isImage ? /* @__PURE__ */ jsx4("img", {
|
|
532
|
+
src: ordinal.contentUrl,
|
|
533
|
+
alt: ordinal.name || "Ordinal",
|
|
534
|
+
className: "w-full h-full object-cover",
|
|
535
|
+
loading: "lazy"
|
|
536
|
+
}) : /* @__PURE__ */ jsx4("iframe", {
|
|
537
|
+
src: ordinal.contentUrl,
|
|
538
|
+
title: ordinal.name || "Ordinal",
|
|
539
|
+
className: "w-full h-full border-0 pointer-events-none",
|
|
540
|
+
sandbox: "allow-scripts",
|
|
541
|
+
loading: "lazy"
|
|
542
|
+
})
|
|
543
|
+
}),
|
|
544
|
+
subtype && /* @__PURE__ */ jsx4("div", {
|
|
545
|
+
className: "mb-1",
|
|
546
|
+
children: /* @__PURE__ */ jsx4("span", {
|
|
547
|
+
className: "px-1 py-0.5 text-[9px] rounded bg-blue-500/20 text-blue-400 truncate",
|
|
548
|
+
children: subtype
|
|
549
|
+
})
|
|
550
|
+
}),
|
|
551
|
+
ordinal.name ? /* @__PURE__ */ jsx4("a", {
|
|
552
|
+
href: ordinal.contentUrl,
|
|
553
|
+
target: "_blank",
|
|
554
|
+
rel: "noopener noreferrer",
|
|
555
|
+
className: "text-[10px] text-foreground truncate font-medium hover:text-blue-400",
|
|
556
|
+
title: ordinal.name,
|
|
557
|
+
children: ordinal.name
|
|
558
|
+
}) : /* @__PURE__ */ jsxs("a", {
|
|
559
|
+
href: ordinal.contentUrl,
|
|
560
|
+
target: "_blank",
|
|
561
|
+
rel: "noopener noreferrer",
|
|
562
|
+
className: "text-[9px] text-muted-foreground truncate font-mono hover:text-blue-400",
|
|
563
|
+
children: [
|
|
564
|
+
ordinal.outpoint.substring(0, 8),
|
|
565
|
+
"..."
|
|
566
|
+
]
|
|
567
|
+
})
|
|
568
|
+
]
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
function FundingSection({
|
|
572
|
+
funding,
|
|
573
|
+
totalBsv,
|
|
574
|
+
sweepAmount,
|
|
575
|
+
onSweepAmountChange,
|
|
576
|
+
onSweep,
|
|
577
|
+
onSend,
|
|
578
|
+
walletConnected
|
|
579
|
+
}) {
|
|
580
|
+
const [address, setAddress] = useState("");
|
|
581
|
+
if (funding.length === 0)
|
|
582
|
+
return null;
|
|
583
|
+
const isMax = sweepAmount === null;
|
|
584
|
+
const displayAmount = isMax ? formatSats(totalBsv) : formatSats(sweepAmount);
|
|
585
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
586
|
+
className: "border border-green-500/20 bg-green-500/5 p-4 rounded-lg",
|
|
587
|
+
children: [
|
|
588
|
+
/* @__PURE__ */ jsxs("div", {
|
|
589
|
+
className: "flex items-center gap-2 mb-2",
|
|
590
|
+
children: [
|
|
591
|
+
/* @__PURE__ */ jsx4("span", {
|
|
592
|
+
className: "h-2 w-2 rounded-full bg-green-500"
|
|
593
|
+
}),
|
|
594
|
+
/* @__PURE__ */ jsx4("span", {
|
|
595
|
+
className: "text-sm font-semibold text-green-500",
|
|
596
|
+
children: "BSV Funding"
|
|
597
|
+
})
|
|
598
|
+
]
|
|
599
|
+
}),
|
|
600
|
+
/* @__PURE__ */ jsxs("div", {
|
|
601
|
+
className: "flex items-baseline justify-between mb-3",
|
|
602
|
+
children: [
|
|
603
|
+
/* @__PURE__ */ jsxs("div", {
|
|
590
604
|
children: [
|
|
591
|
-
/* @__PURE__ */
|
|
592
|
-
className: "
|
|
605
|
+
/* @__PURE__ */ jsxs("div", {
|
|
606
|
+
className: "text-2xl font-bold text-green-500",
|
|
593
607
|
children: [
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
}),
|
|
597
|
-
"Legacy Keys"
|
|
608
|
+
formatSats(totalBsv),
|
|
609
|
+
" sats"
|
|
598
610
|
]
|
|
599
611
|
}),
|
|
600
|
-
/* @__PURE__ */
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
className: "h-3 w-3"
|
|
607
|
-
})
|
|
612
|
+
/* @__PURE__ */ jsxs("div", {
|
|
613
|
+
className: "text-xs text-muted-foreground",
|
|
614
|
+
children: [
|
|
615
|
+
(totalBsv / 1e8).toFixed(8),
|
|
616
|
+
" BSV"
|
|
617
|
+
]
|
|
608
618
|
})
|
|
609
619
|
]
|
|
610
|
-
})
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
className: "space-y-3",
|
|
614
|
-
children: [
|
|
615
|
-
backupType && /* @__PURE__ */ jsx7("span", {
|
|
616
|
-
className: "text-xs px-2 py-0.5 rounded bg-blue-500/20 text-blue-400",
|
|
617
|
-
children: backupType
|
|
618
|
-
}),
|
|
619
|
-
/* @__PURE__ */ jsxs2("div", {
|
|
620
|
-
className: "space-y-1 text-xs text-muted-foreground font-mono",
|
|
621
|
-
children: [
|
|
622
|
-
/* @__PURE__ */ jsxs2("div", {
|
|
623
|
-
children: [
|
|
624
|
-
"Pay: ",
|
|
625
|
-
deriveAddress(keys.payPk)
|
|
626
|
-
]
|
|
627
|
-
}),
|
|
628
|
-
keys.ordPk !== keys.payPk && /* @__PURE__ */ jsxs2("div", {
|
|
629
|
-
children: [
|
|
630
|
-
"Ord: ",
|
|
631
|
-
deriveAddress(keys.ordPk)
|
|
632
|
-
]
|
|
633
|
-
}),
|
|
634
|
-
keys.identityPk && keys.identityPk !== keys.payPk && /* @__PURE__ */ jsxs2("div", {
|
|
635
|
-
children: [
|
|
636
|
-
"ID: ",
|
|
637
|
-
deriveAddress(keys.identityPk)
|
|
638
|
-
]
|
|
639
|
-
})
|
|
640
|
-
]
|
|
641
|
-
}),
|
|
642
|
-
/* @__PURE__ */ jsxs2(Button, {
|
|
643
|
-
onClick: handleScan,
|
|
644
|
-
disabled: disabled || scanning,
|
|
645
|
-
className: "w-full",
|
|
646
|
-
children: [
|
|
647
|
-
scanning ? /* @__PURE__ */ jsx7(Loader22, {
|
|
648
|
-
className: "h-4 w-4 animate-spin mr-2"
|
|
649
|
-
}) : /* @__PURE__ */ jsx7(Search, {
|
|
650
|
-
className: "h-4 w-4 mr-2"
|
|
651
|
-
}),
|
|
652
|
-
scanning ? "Scanning..." : "Scan for Assets"
|
|
653
|
-
]
|
|
654
|
-
})
|
|
655
|
-
]
|
|
656
|
-
})
|
|
657
|
-
]
|
|
658
|
-
});
|
|
659
|
-
}
|
|
660
|
-
if (mode === "choose") {
|
|
661
|
-
return /* @__PURE__ */ jsxs2(Card, {
|
|
662
|
-
children: [
|
|
663
|
-
/* @__PURE__ */ jsx7(CardHeader, {
|
|
664
|
-
children: /* @__PURE__ */ jsxs2(CardTitle, {
|
|
665
|
-
className: "flex items-center gap-2",
|
|
620
|
+
}),
|
|
621
|
+
/* @__PURE__ */ jsxs(Badge, {
|
|
622
|
+
variant: "secondary",
|
|
666
623
|
children: [
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
"Legacy Keys"
|
|
624
|
+
funding.length,
|
|
625
|
+
" UTXO",
|
|
626
|
+
funding.length !== 1 ? "s" : ""
|
|
671
627
|
]
|
|
672
628
|
})
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
children: /* @__PURE__ */ jsxs2("div", {
|
|
717
|
-
className: "flex items-center justify-between",
|
|
629
|
+
]
|
|
630
|
+
}),
|
|
631
|
+
/* @__PURE__ */ jsxs("div", {
|
|
632
|
+
className: "flex items-center gap-2",
|
|
633
|
+
children: [
|
|
634
|
+
/* @__PURE__ */ jsx4(Input, {
|
|
635
|
+
type: "number",
|
|
636
|
+
min: 0,
|
|
637
|
+
max: totalBsv,
|
|
638
|
+
placeholder: "Max",
|
|
639
|
+
value: isMax ? "" : sweepAmount,
|
|
640
|
+
onChange: (e) => {
|
|
641
|
+
const val = e.target.value;
|
|
642
|
+
onSweepAmountChange(val === "" ? null : Math.max(0, Math.min(totalBsv, Number(val))));
|
|
643
|
+
},
|
|
644
|
+
className: "flex-1 font-mono"
|
|
645
|
+
}),
|
|
646
|
+
/* @__PURE__ */ jsx4("span", {
|
|
647
|
+
className: "text-xs text-muted-foreground",
|
|
648
|
+
children: "sats"
|
|
649
|
+
}),
|
|
650
|
+
/* @__PURE__ */ jsx4(Button, {
|
|
651
|
+
variant: "outline",
|
|
652
|
+
size: "sm",
|
|
653
|
+
className: "h-9 text-xs",
|
|
654
|
+
onClick: () => onSweepAmountChange(null),
|
|
655
|
+
disabled: isMax,
|
|
656
|
+
children: "Max"
|
|
657
|
+
})
|
|
658
|
+
]
|
|
659
|
+
}),
|
|
660
|
+
/* @__PURE__ */ jsxs("div", {
|
|
661
|
+
className: "mt-3 space-y-2",
|
|
662
|
+
children: [
|
|
663
|
+
onSend && /* @__PURE__ */ jsx4(Input, {
|
|
664
|
+
type: "text",
|
|
665
|
+
placeholder: "Destination address...",
|
|
666
|
+
value: address,
|
|
667
|
+
onChange: (e) => setAddress(e.target.value),
|
|
668
|
+
className: "font-mono text-xs"
|
|
669
|
+
}),
|
|
670
|
+
/* @__PURE__ */ jsxs("div", {
|
|
671
|
+
className: "flex gap-2",
|
|
718
672
|
children: [
|
|
719
|
-
/* @__PURE__ */
|
|
720
|
-
|
|
673
|
+
onSend && /* @__PURE__ */ jsxs(Button, {
|
|
674
|
+
variant: "outline",
|
|
675
|
+
size: "sm",
|
|
676
|
+
className: "flex-1",
|
|
677
|
+
disabled: !address.trim(),
|
|
678
|
+
onClick: () => onSend(address.trim()),
|
|
721
679
|
children: [
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
"Import Backup"
|
|
680
|
+
"Send ",
|
|
681
|
+
displayAmount,
|
|
682
|
+
" sats"
|
|
726
683
|
]
|
|
727
684
|
}),
|
|
728
|
-
/* @__PURE__ */
|
|
729
|
-
variant: "ghost",
|
|
685
|
+
/* @__PURE__ */ jsx4(Button, {
|
|
730
686
|
size: "sm",
|
|
731
|
-
className: "
|
|
732
|
-
onClick:
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
687
|
+
className: "flex-1",
|
|
688
|
+
onClick: onSweep,
|
|
689
|
+
disabled: !walletConnected,
|
|
690
|
+
title: walletConnected ? undefined : "Connect BRC-100 wallet to sweep",
|
|
691
|
+
children: "Sweep to Wallet"
|
|
736
692
|
})
|
|
737
693
|
]
|
|
738
694
|
})
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
className: "h-4 w-4"
|
|
765
|
-
}),
|
|
766
|
-
"Or upload a file"
|
|
767
|
-
]
|
|
768
|
-
})
|
|
769
|
-
]
|
|
770
|
-
}) : /* @__PURE__ */ jsxs2(Fragment, {
|
|
771
|
-
children: [
|
|
772
|
-
/* @__PURE__ */ jsx7("p", {
|
|
773
|
-
className: "text-sm text-muted-foreground",
|
|
774
|
-
children: "This backup is encrypted. Enter the password to decrypt."
|
|
775
|
-
}),
|
|
776
|
-
/* @__PURE__ */ jsx7(Input, {
|
|
777
|
-
type: "password",
|
|
778
|
-
placeholder: "Backup password...",
|
|
779
|
-
value: password,
|
|
780
|
-
onChange: (e) => setPassword(e.target.value),
|
|
781
|
-
onKeyDown: (e) => {
|
|
782
|
-
if (e.key === "Enter")
|
|
783
|
-
handleDecrypt();
|
|
784
|
-
}
|
|
785
|
-
}),
|
|
786
|
-
/* @__PURE__ */ jsxs2(Button, {
|
|
787
|
-
onClick: handleDecrypt,
|
|
788
|
-
disabled: !password || decrypting,
|
|
789
|
-
className: "w-full",
|
|
790
|
-
children: [
|
|
791
|
-
decrypting ? /* @__PURE__ */ jsx7(Loader22, {
|
|
792
|
-
className: "h-4 w-4 animate-spin mr-2"
|
|
793
|
-
}) : null,
|
|
794
|
-
decrypting ? "Decrypting..." : "Decrypt"
|
|
795
|
-
]
|
|
796
|
-
})
|
|
797
|
-
]
|
|
798
|
-
}),
|
|
799
|
-
error && /* @__PURE__ */ jsx7("p", {
|
|
800
|
-
className: "text-sm text-destructive",
|
|
801
|
-
children: error
|
|
802
|
-
})
|
|
803
|
-
]
|
|
804
|
-
})
|
|
805
|
-
]
|
|
806
|
-
});
|
|
807
|
-
}
|
|
808
|
-
return /* @__PURE__ */ jsxs2(Card, {
|
|
695
|
+
]
|
|
696
|
+
})
|
|
697
|
+
]
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
function OrdinalsSection({
|
|
701
|
+
ordinals,
|
|
702
|
+
selectedOrdinals,
|
|
703
|
+
onToggle,
|
|
704
|
+
onSelectAll,
|
|
705
|
+
onDeselectAll,
|
|
706
|
+
onSweep,
|
|
707
|
+
onSend,
|
|
708
|
+
onBurn,
|
|
709
|
+
walletConnected
|
|
710
|
+
}) {
|
|
711
|
+
const [page, setPage] = useState(0);
|
|
712
|
+
const [address, setAddress] = useState("");
|
|
713
|
+
if (ordinals.length === 0)
|
|
714
|
+
return null;
|
|
715
|
+
const totalPages = Math.ceil(ordinals.length / ORDINALS_PER_PAGE);
|
|
716
|
+
const start = page * ORDINALS_PER_PAGE;
|
|
717
|
+
const pageItems = ordinals.slice(start, start + ORDINALS_PER_PAGE);
|
|
718
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
719
|
+
className: "border border-blue-500/20 bg-blue-500/5 p-4 rounded-lg",
|
|
809
720
|
children: [
|
|
810
|
-
/* @__PURE__ */
|
|
811
|
-
|
|
812
|
-
className: "flex items-center justify-between",
|
|
813
|
-
children: [
|
|
814
|
-
/* @__PURE__ */ jsxs2(CardTitle, {
|
|
815
|
-
className: "flex items-center gap-2",
|
|
816
|
-
children: [
|
|
817
|
-
/* @__PURE__ */ jsx7(KeyRound, {
|
|
818
|
-
className: "h-5 w-5"
|
|
819
|
-
}),
|
|
820
|
-
"Manual WIF Entry"
|
|
821
|
-
]
|
|
822
|
-
}),
|
|
823
|
-
/* @__PURE__ */ jsx7(Button, {
|
|
824
|
-
variant: "ghost",
|
|
825
|
-
size: "sm",
|
|
826
|
-
className: "h-6 w-6 p-0",
|
|
827
|
-
onClick: handleReset,
|
|
828
|
-
children: /* @__PURE__ */ jsx7(X2, {
|
|
829
|
-
className: "h-3 w-3"
|
|
830
|
-
})
|
|
831
|
-
})
|
|
832
|
-
]
|
|
833
|
-
})
|
|
834
|
-
}),
|
|
835
|
-
/* @__PURE__ */ jsxs2(CardContent, {
|
|
836
|
-
className: "space-y-4",
|
|
721
|
+
/* @__PURE__ */ jsxs("div", {
|
|
722
|
+
className: "flex items-start justify-between mb-3",
|
|
837
723
|
children: [
|
|
838
|
-
/* @__PURE__ */
|
|
839
|
-
className: "space-y-2",
|
|
724
|
+
/* @__PURE__ */ jsxs("div", {
|
|
840
725
|
children: [
|
|
841
|
-
/* @__PURE__ */
|
|
842
|
-
className: "
|
|
843
|
-
children:
|
|
726
|
+
/* @__PURE__ */ jsxs("div", {
|
|
727
|
+
className: "flex items-center gap-2 mb-1",
|
|
728
|
+
children: [
|
|
729
|
+
/* @__PURE__ */ jsx4("span", {
|
|
730
|
+
className: "h-2 w-2 rounded-full bg-blue-500"
|
|
731
|
+
}),
|
|
732
|
+
/* @__PURE__ */ jsx4("span", {
|
|
733
|
+
className: "text-sm font-semibold text-blue-500",
|
|
734
|
+
children: "Ordinals"
|
|
735
|
+
})
|
|
736
|
+
]
|
|
844
737
|
}),
|
|
845
|
-
/* @__PURE__ */
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
738
|
+
/* @__PURE__ */ jsxs("div", {
|
|
739
|
+
className: "text-xs text-muted-foreground",
|
|
740
|
+
children: [
|
|
741
|
+
ordinals.length,
|
|
742
|
+
" inscription",
|
|
743
|
+
ordinals.length !== 1 ? "s" : "",
|
|
744
|
+
selectedOrdinals.size > 0 && /* @__PURE__ */ jsxs("span", {
|
|
745
|
+
className: "text-blue-400 ml-1",
|
|
746
|
+
children: [
|
|
747
|
+
"(",
|
|
748
|
+
selectedOrdinals.size,
|
|
749
|
+
" selected)"
|
|
750
|
+
]
|
|
751
|
+
})
|
|
752
|
+
]
|
|
851
753
|
})
|
|
852
754
|
]
|
|
853
755
|
}),
|
|
854
|
-
/* @__PURE__ */
|
|
855
|
-
className: "flex
|
|
756
|
+
/* @__PURE__ */ jsxs("div", {
|
|
757
|
+
className: "flex gap-2",
|
|
856
758
|
children: [
|
|
857
|
-
/* @__PURE__ */
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
759
|
+
/* @__PURE__ */ jsx4(Button, {
|
|
760
|
+
variant: "outline",
|
|
761
|
+
size: "sm",
|
|
762
|
+
className: "h-7 text-[11px]",
|
|
763
|
+
onClick: onSelectAll,
|
|
764
|
+
children: "Select All"
|
|
862
765
|
}),
|
|
863
|
-
|
|
766
|
+
/* @__PURE__ */ jsx4(Button, {
|
|
767
|
+
variant: "outline",
|
|
768
|
+
size: "sm",
|
|
769
|
+
className: "h-7 text-[11px]",
|
|
770
|
+
onClick: onDeselectAll,
|
|
771
|
+
disabled: selectedOrdinals.size === 0,
|
|
772
|
+
children: "Deselect"
|
|
773
|
+
})
|
|
774
|
+
]
|
|
775
|
+
})
|
|
776
|
+
]
|
|
777
|
+
}),
|
|
778
|
+
/* @__PURE__ */ jsx4("div", {
|
|
779
|
+
className: "grid grid-cols-3 sm:grid-cols-4 md:grid-cols-5 gap-2 mb-3",
|
|
780
|
+
children: pageItems.map((ord) => /* @__PURE__ */ jsx4(OrdinalCard, {
|
|
781
|
+
ordinal: ord,
|
|
782
|
+
isSelected: selectedOrdinals.has(ord.outpoint),
|
|
783
|
+
onToggle: () => onToggle(ord.outpoint)
|
|
784
|
+
}, ord.outpoint))
|
|
785
|
+
}),
|
|
786
|
+
totalPages > 1 && /* @__PURE__ */ jsxs("div", {
|
|
787
|
+
className: "flex items-center justify-center gap-4",
|
|
788
|
+
children: [
|
|
789
|
+
/* @__PURE__ */ jsx4(Button, {
|
|
790
|
+
variant: "ghost",
|
|
791
|
+
size: "sm",
|
|
792
|
+
className: "text-xs",
|
|
793
|
+
onClick: () => setPage(page - 1),
|
|
794
|
+
disabled: page === 0,
|
|
795
|
+
children: "Prev"
|
|
796
|
+
}),
|
|
797
|
+
/* @__PURE__ */ jsxs("span", {
|
|
798
|
+
className: "text-xs text-muted-foreground",
|
|
799
|
+
children: [
|
|
800
|
+
"Page ",
|
|
801
|
+
page + 1,
|
|
802
|
+
" of ",
|
|
803
|
+
totalPages
|
|
864
804
|
]
|
|
865
805
|
}),
|
|
866
|
-
|
|
867
|
-
|
|
806
|
+
/* @__PURE__ */ jsx4(Button, {
|
|
807
|
+
variant: "ghost",
|
|
808
|
+
size: "sm",
|
|
809
|
+
className: "text-xs",
|
|
810
|
+
onClick: () => setPage(page + 1),
|
|
811
|
+
disabled: page >= totalPages - 1,
|
|
812
|
+
children: "Next"
|
|
813
|
+
})
|
|
814
|
+
]
|
|
815
|
+
}),
|
|
816
|
+
selectedOrdinals.size > 0 && /* @__PURE__ */ jsxs("div", {
|
|
817
|
+
className: "mt-3 space-y-2",
|
|
818
|
+
children: [
|
|
819
|
+
onSend && /* @__PURE__ */ jsx4(Input, {
|
|
820
|
+
type: "text",
|
|
821
|
+
placeholder: "Destination address...",
|
|
822
|
+
value: address,
|
|
823
|
+
onChange: (e) => setAddress(e.target.value),
|
|
824
|
+
className: "font-mono text-xs"
|
|
825
|
+
}),
|
|
826
|
+
/* @__PURE__ */ jsxs("div", {
|
|
827
|
+
className: "flex gap-2",
|
|
868
828
|
children: [
|
|
869
|
-
/* @__PURE__ */
|
|
870
|
-
|
|
871
|
-
|
|
829
|
+
onSend && /* @__PURE__ */ jsxs(Button, {
|
|
830
|
+
variant: "outline",
|
|
831
|
+
size: "sm",
|
|
832
|
+
className: "flex-1",
|
|
833
|
+
disabled: !address.trim(),
|
|
834
|
+
onClick: () => onSend(address.trim()),
|
|
835
|
+
children: [
|
|
836
|
+
"Send ",
|
|
837
|
+
selectedOrdinals.size,
|
|
838
|
+
" Ordinal",
|
|
839
|
+
selectedOrdinals.size !== 1 ? "s" : ""
|
|
840
|
+
]
|
|
872
841
|
}),
|
|
873
|
-
/* @__PURE__ */
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
842
|
+
/* @__PURE__ */ jsx4(Button, {
|
|
843
|
+
size: "sm",
|
|
844
|
+
className: "flex-1",
|
|
845
|
+
onClick: onSweep,
|
|
846
|
+
disabled: !walletConnected,
|
|
847
|
+
title: walletConnected ? undefined : "Connect BRC-100 wallet to sweep",
|
|
848
|
+
children: "Sweep to Wallet"
|
|
849
|
+
}),
|
|
850
|
+
onBurn && /* @__PURE__ */ jsx4(Button, {
|
|
851
|
+
size: "sm",
|
|
852
|
+
className: "bg-red-600 hover:bg-red-700 text-white",
|
|
853
|
+
onClick: () => {
|
|
854
|
+
if (window.confirm(`Permanently burn ${selectedOrdinals.size} ordinal${selectedOrdinals.size !== 1 ? "s" : ""}? This cannot be undone.`))
|
|
855
|
+
onBurn();
|
|
856
|
+
},
|
|
857
|
+
children: "Burn"
|
|
879
858
|
})
|
|
880
859
|
]
|
|
860
|
+
})
|
|
861
|
+
]
|
|
862
|
+
})
|
|
863
|
+
]
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
function TokenRow({
|
|
867
|
+
tb,
|
|
868
|
+
onSweep,
|
|
869
|
+
walletConnected
|
|
870
|
+
}) {
|
|
871
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
872
|
+
className: `flex items-center justify-between p-3 rounded-lg border ${tb.isActive ? "bg-black/20 border-purple-500/10" : "bg-black/10 border-muted/20 opacity-60"}`,
|
|
873
|
+
children: [
|
|
874
|
+
/* @__PURE__ */ jsxs("div", {
|
|
875
|
+
className: "flex items-center gap-3",
|
|
876
|
+
children: [
|
|
877
|
+
/* @__PURE__ */ jsx4("img", {
|
|
878
|
+
src: tb.icon,
|
|
879
|
+
alt: tb.symbol || "Token",
|
|
880
|
+
className: "w-8 h-8 rounded-full object-cover",
|
|
881
|
+
onError: (e) => {
|
|
882
|
+
e.target.style.display = "none";
|
|
883
|
+
}
|
|
881
884
|
}),
|
|
882
|
-
/* @__PURE__ */
|
|
883
|
-
onClick: handleScan,
|
|
884
|
-
disabled: disabled || scanning || !payWif.trim(),
|
|
885
|
-
className: "w-full",
|
|
885
|
+
/* @__PURE__ */ jsxs("div", {
|
|
886
886
|
children: [
|
|
887
|
-
|
|
888
|
-
className: "
|
|
889
|
-
|
|
890
|
-
|
|
887
|
+
/* @__PURE__ */ jsxs("div", {
|
|
888
|
+
className: "flex items-center gap-2",
|
|
889
|
+
children: [
|
|
890
|
+
/* @__PURE__ */ jsx4("span", {
|
|
891
|
+
className: "font-medium text-foreground",
|
|
892
|
+
children: tb.symbol || tb.tokenId.slice(0, 8) + "..."
|
|
893
|
+
}),
|
|
894
|
+
tb.isActive ? /* @__PURE__ */ jsx4("span", {
|
|
895
|
+
className: "px-1.5 py-0.5 text-[9px] rounded bg-green-600/20 text-green-700 dark:text-green-400",
|
|
896
|
+
children: "active"
|
|
897
|
+
}) : /* @__PURE__ */ jsx4("span", {
|
|
898
|
+
className: "px-1.5 py-0.5 text-[9px] rounded bg-muted text-muted-foreground",
|
|
899
|
+
children: "inactive"
|
|
900
|
+
})
|
|
901
|
+
]
|
|
891
902
|
}),
|
|
892
|
-
|
|
903
|
+
/* @__PURE__ */ jsxs("div", {
|
|
904
|
+
className: "text-xs text-muted-foreground",
|
|
905
|
+
children: [
|
|
906
|
+
formatTokenAmount(tb.totalAmount.toString(), tb.decimals),
|
|
907
|
+
" ",
|
|
908
|
+
tb.symbol || "",
|
|
909
|
+
/* @__PURE__ */ jsxs("span", {
|
|
910
|
+
className: "ml-2",
|
|
911
|
+
children: [
|
|
912
|
+
"(",
|
|
913
|
+
tb.outputs.length,
|
|
914
|
+
" output",
|
|
915
|
+
tb.outputs.length !== 1 ? "s" : "",
|
|
916
|
+
")"
|
|
917
|
+
]
|
|
918
|
+
})
|
|
919
|
+
]
|
|
920
|
+
})
|
|
893
921
|
]
|
|
894
922
|
})
|
|
895
923
|
]
|
|
924
|
+
}),
|
|
925
|
+
tb.isActive && onSweep && /* @__PURE__ */ jsx4(Button, {
|
|
926
|
+
size: "sm",
|
|
927
|
+
onClick: () => onSweep(tb.tokenId),
|
|
928
|
+
disabled: !walletConnected,
|
|
929
|
+
title: walletConnected ? undefined : "Connect BRC-100 wallet to sweep",
|
|
930
|
+
children: "Sweep to Wallet"
|
|
896
931
|
})
|
|
897
932
|
]
|
|
898
933
|
});
|
|
899
934
|
}
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
const subtype = ct.includes("/") ? ct.split("/")[1] : ct;
|
|
912
|
-
return /* @__PURE__ */ jsxs3("div", {
|
|
913
|
-
className: `relative p-2 rounded-lg border cursor-pointer transition-all ${isSelected ? "border-blue-500 bg-blue-500/10 ring-1 ring-blue-500/30" : "border-border/50 hover:border-border bg-black/20"}`,
|
|
914
|
-
onClick: onToggle,
|
|
935
|
+
function Bsv21Section({
|
|
936
|
+
tokens,
|
|
937
|
+
onSweep,
|
|
938
|
+
walletConnected
|
|
939
|
+
}) {
|
|
940
|
+
if (tokens.length === 0)
|
|
941
|
+
return null;
|
|
942
|
+
const active = tokens.filter((t) => t.isActive);
|
|
943
|
+
const inactive = tokens.filter((t) => !t.isActive);
|
|
944
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
945
|
+
className: "border border-purple-500/20 bg-purple-500/5 p-4 rounded-lg",
|
|
915
946
|
children: [
|
|
916
|
-
/* @__PURE__ */
|
|
917
|
-
className: "
|
|
918
|
-
children:
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
children: "◆"
|
|
928
|
-
}) : isImage ? /* @__PURE__ */ jsx8("img", {
|
|
929
|
-
src: ordinal.contentUrl,
|
|
930
|
-
alt: ordinal.name || "Ordinal",
|
|
931
|
-
className: "w-full h-full object-cover",
|
|
932
|
-
loading: "lazy"
|
|
933
|
-
}) : /* @__PURE__ */ jsx8("iframe", {
|
|
934
|
-
src: ordinal.contentUrl,
|
|
935
|
-
title: ordinal.name || "Ordinal",
|
|
936
|
-
className: "w-full h-full border-0 pointer-events-none",
|
|
937
|
-
sandbox: "allow-scripts",
|
|
938
|
-
loading: "lazy"
|
|
939
|
-
})
|
|
940
|
-
}),
|
|
941
|
-
subtype && /* @__PURE__ */ jsx8("div", {
|
|
942
|
-
className: "mb-1",
|
|
943
|
-
children: /* @__PURE__ */ jsx8("span", {
|
|
944
|
-
className: "px-1 py-0.5 text-[9px] rounded bg-blue-500/20 text-blue-400 truncate",
|
|
945
|
-
children: subtype
|
|
946
|
-
})
|
|
947
|
+
/* @__PURE__ */ jsxs("div", {
|
|
948
|
+
className: "flex items-center gap-2 mb-3",
|
|
949
|
+
children: [
|
|
950
|
+
/* @__PURE__ */ jsx4("span", {
|
|
951
|
+
className: "h-2 w-2 rounded-full bg-purple-500"
|
|
952
|
+
}),
|
|
953
|
+
/* @__PURE__ */ jsx4("span", {
|
|
954
|
+
className: "text-sm font-semibold text-purple-500",
|
|
955
|
+
children: "BSV-21 Tokens"
|
|
956
|
+
})
|
|
957
|
+
]
|
|
947
958
|
}),
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
target: "_blank",
|
|
951
|
-
rel: "noopener noreferrer",
|
|
952
|
-
className: "text-[10px] text-foreground truncate font-medium hover:text-blue-400",
|
|
953
|
-
title: ordinal.name,
|
|
954
|
-
children: ordinal.name
|
|
955
|
-
}) : /* @__PURE__ */ jsxs3("a", {
|
|
956
|
-
href: ordinal.contentUrl,
|
|
957
|
-
target: "_blank",
|
|
958
|
-
rel: "noopener noreferrer",
|
|
959
|
-
className: "text-[9px] text-muted-foreground truncate font-mono hover:text-blue-400",
|
|
959
|
+
/* @__PURE__ */ jsxs("div", {
|
|
960
|
+
className: "space-y-3",
|
|
960
961
|
children: [
|
|
961
|
-
|
|
962
|
-
|
|
962
|
+
active.map((tb) => /* @__PURE__ */ jsx4(TokenRow, {
|
|
963
|
+
tb,
|
|
964
|
+
onSweep,
|
|
965
|
+
walletConnected
|
|
966
|
+
}, tb.tokenId)),
|
|
967
|
+
inactive.length > 0 && active.length > 0 && /* @__PURE__ */ jsx4("div", {
|
|
968
|
+
className: "border-t border-purple-500/10 pt-3 mt-3",
|
|
969
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
970
|
+
className: "text-xs text-muted-foreground mb-2",
|
|
971
|
+
children: [
|
|
972
|
+
"Inactive overlays (",
|
|
973
|
+
inactive.length,
|
|
974
|
+
") — cannot be swept"
|
|
975
|
+
]
|
|
976
|
+
})
|
|
977
|
+
}),
|
|
978
|
+
inactive.map((tb) => /* @__PURE__ */ jsx4(TokenRow, {
|
|
979
|
+
tb,
|
|
980
|
+
walletConnected
|
|
981
|
+
}, tb.tokenId))
|
|
963
982
|
]
|
|
964
983
|
})
|
|
965
984
|
]
|
|
966
985
|
});
|
|
967
986
|
}
|
|
968
|
-
function
|
|
969
|
-
|
|
970
|
-
if (funding.length === 0)
|
|
987
|
+
function Bsv20Section({ tokens }) {
|
|
988
|
+
if (tokens.length === 0)
|
|
971
989
|
return null;
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
return /* @__PURE__ */ jsxs3("div", {
|
|
975
|
-
className: "border border-green-500/20 bg-green-500/5 p-4 rounded-lg",
|
|
990
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
991
|
+
className: "border border-muted/30 bg-muted/10 p-4 rounded-lg",
|
|
976
992
|
children: [
|
|
977
|
-
/* @__PURE__ */
|
|
993
|
+
/* @__PURE__ */ jsxs("div", {
|
|
978
994
|
className: "flex items-center gap-2 mb-2",
|
|
979
995
|
children: [
|
|
980
|
-
/* @__PURE__ */
|
|
981
|
-
className: "h-2 w-2 rounded-full bg-
|
|
996
|
+
/* @__PURE__ */ jsx4("span", {
|
|
997
|
+
className: "h-2 w-2 rounded-full bg-muted-foreground"
|
|
982
998
|
}),
|
|
983
|
-
/* @__PURE__ */
|
|
984
|
-
className: "text-sm font-semibold text-
|
|
985
|
-
children: "BSV
|
|
999
|
+
/* @__PURE__ */ jsx4("span", {
|
|
1000
|
+
className: "text-sm font-semibold text-muted-foreground",
|
|
1001
|
+
children: "BSV-20 Tokens"
|
|
986
1002
|
})
|
|
987
1003
|
]
|
|
988
1004
|
}),
|
|
989
|
-
/* @__PURE__ */
|
|
990
|
-
className: "
|
|
1005
|
+
/* @__PURE__ */ jsx4("p", {
|
|
1006
|
+
className: "text-xs text-muted-foreground mb-2",
|
|
1007
|
+
children: "Cannot be swept automatically."
|
|
1008
|
+
}),
|
|
1009
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1010
|
+
className: "flex flex-wrap gap-2",
|
|
991
1011
|
children: [
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
]
|
|
1000
|
-
}),
|
|
1001
|
-
/* @__PURE__ */ jsxs3("div", {
|
|
1002
|
-
className: "text-xs text-muted-foreground",
|
|
1003
|
-
children: [
|
|
1004
|
-
(totalBsv / 1e8).toFixed(8),
|
|
1005
|
-
" BSV"
|
|
1006
|
-
]
|
|
1007
|
-
})
|
|
1008
|
-
]
|
|
1012
|
+
tokens.slice(0, 10).map((o) => {
|
|
1013
|
+
const tickEvent = o.events?.find((e) => e.startsWith("tick:"));
|
|
1014
|
+
const tick = tickEvent ? tickEvent.slice(5) : "Token";
|
|
1015
|
+
return /* @__PURE__ */ jsx4("span", {
|
|
1016
|
+
className: "px-2 py-1 text-xs rounded bg-muted/30 text-muted-foreground",
|
|
1017
|
+
children: tick
|
|
1018
|
+
}, o.outpoint);
|
|
1009
1019
|
}),
|
|
1010
|
-
/* @__PURE__ */
|
|
1011
|
-
|
|
1020
|
+
tokens.length > 10 && /* @__PURE__ */ jsxs("span", {
|
|
1021
|
+
className: "text-xs text-muted-foreground",
|
|
1012
1022
|
children: [
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1023
|
+
"+",
|
|
1024
|
+
tokens.length - 10,
|
|
1025
|
+
" more"
|
|
1016
1026
|
]
|
|
1017
1027
|
})
|
|
1018
1028
|
]
|
|
1019
|
-
})
|
|
1020
|
-
|
|
1021
|
-
|
|
1029
|
+
})
|
|
1030
|
+
]
|
|
1031
|
+
});
|
|
1032
|
+
}
|
|
1033
|
+
function LockedSection({ locked }) {
|
|
1034
|
+
if (locked.length === 0)
|
|
1035
|
+
return null;
|
|
1036
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1037
|
+
className: "border border-yellow-500/20 bg-yellow-500/5 p-4 rounded-lg",
|
|
1038
|
+
children: [
|
|
1039
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1040
|
+
className: "flex items-center gap-2 mb-2",
|
|
1022
1041
|
children: [
|
|
1023
|
-
/* @__PURE__ */
|
|
1024
|
-
|
|
1025
|
-
min: 0,
|
|
1026
|
-
max: totalBsv,
|
|
1027
|
-
placeholder: "Max",
|
|
1028
|
-
value: isMax ? "" : sweepAmount,
|
|
1029
|
-
onChange: (e) => {
|
|
1030
|
-
const val = e.target.value;
|
|
1031
|
-
onSweepAmountChange(val === "" ? null : Math.max(0, Math.min(totalBsv, Number(val))));
|
|
1032
|
-
},
|
|
1033
|
-
className: "flex-1 font-mono"
|
|
1034
|
-
}),
|
|
1035
|
-
/* @__PURE__ */ jsx8("span", {
|
|
1036
|
-
className: "text-xs text-muted-foreground",
|
|
1037
|
-
children: "sats"
|
|
1042
|
+
/* @__PURE__ */ jsx4("span", {
|
|
1043
|
+
className: "h-2 w-2 rounded-full bg-yellow-500"
|
|
1038
1044
|
}),
|
|
1039
|
-
/* @__PURE__ */
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
className: "h-9 text-xs",
|
|
1043
|
-
onClick: () => onSweepAmountChange(null),
|
|
1044
|
-
disabled: isMax,
|
|
1045
|
-
children: "Max"
|
|
1045
|
+
/* @__PURE__ */ jsx4("span", {
|
|
1046
|
+
className: "text-sm font-semibold text-yellow-500",
|
|
1047
|
+
children: "Locked Outputs"
|
|
1046
1048
|
})
|
|
1047
1049
|
]
|
|
1048
1050
|
}),
|
|
1049
|
-
/* @__PURE__ */
|
|
1050
|
-
className: "
|
|
1051
|
+
/* @__PURE__ */ jsxs("p", {
|
|
1052
|
+
className: "text-xs text-muted-foreground",
|
|
1051
1053
|
children: [
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1054
|
+
locked.length,
|
|
1055
|
+
" locked output",
|
|
1056
|
+
locked.length !== 1 ? "s" : "",
|
|
1057
|
+
". These are in contracts and cannot be swept directly."
|
|
1058
|
+
]
|
|
1059
|
+
})
|
|
1060
|
+
]
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
function RunSection({ run }) {
|
|
1064
|
+
if (run.length === 0)
|
|
1065
|
+
return null;
|
|
1066
|
+
const totalSats = run.reduce((sum, o) => sum + (o.satoshis ?? 0), 0);
|
|
1067
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1068
|
+
className: "border border-orange-500/20 bg-orange-500/5 p-4 rounded-lg",
|
|
1069
|
+
children: [
|
|
1070
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1071
|
+
className: "flex items-center gap-2 mb-2",
|
|
1072
|
+
children: [
|
|
1073
|
+
/* @__PURE__ */ jsx4("span", {
|
|
1074
|
+
className: "h-2 w-2 rounded-full bg-orange-500"
|
|
1058
1075
|
}),
|
|
1059
|
-
/* @__PURE__ */
|
|
1060
|
-
className: "
|
|
1061
|
-
children:
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1076
|
+
/* @__PURE__ */ jsx4("span", {
|
|
1077
|
+
className: "text-sm font-semibold text-orange-500",
|
|
1078
|
+
children: "RUN Protocol Tokens"
|
|
1079
|
+
})
|
|
1080
|
+
]
|
|
1081
|
+
}),
|
|
1082
|
+
/* @__PURE__ */ jsxs("p", {
|
|
1083
|
+
className: "text-xs text-muted-foreground",
|
|
1084
|
+
children: [
|
|
1085
|
+
run.length,
|
|
1086
|
+
" output",
|
|
1087
|
+
run.length !== 1 ? "s" : "",
|
|
1088
|
+
" (",
|
|
1089
|
+
totalSats.toLocaleString(),
|
|
1090
|
+
" sats). These are RUN protocol token outputs and cannot be swept as BSV."
|
|
1091
|
+
]
|
|
1092
|
+
})
|
|
1093
|
+
]
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
// src/components/connect-wallet.tsx
|
|
1098
|
+
import { Loader2, Wallet, X } from "lucide-react";
|
|
1099
|
+
import { useState as useState2 } from "react";
|
|
1100
|
+
import { jsx as jsx5, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1101
|
+
function ConnectWallet({
|
|
1102
|
+
onConnected,
|
|
1103
|
+
onDisconnected,
|
|
1104
|
+
connected
|
|
1105
|
+
}) {
|
|
1106
|
+
const [connecting, setConnecting] = useState2(false);
|
|
1107
|
+
const [error, setError] = useState2(null);
|
|
1108
|
+
async function handleConnect() {
|
|
1109
|
+
setConnecting(true);
|
|
1110
|
+
setError(null);
|
|
1111
|
+
try {
|
|
1112
|
+
await connectWallet();
|
|
1113
|
+
onConnected();
|
|
1114
|
+
} catch (e) {
|
|
1115
|
+
setError(e instanceof Error ? e.message : "Failed to connect wallet");
|
|
1116
|
+
} finally {
|
|
1117
|
+
setConnecting(false);
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
function handleDisconnect() {
|
|
1121
|
+
disconnectWallet();
|
|
1122
|
+
onDisconnected();
|
|
1123
|
+
}
|
|
1124
|
+
if (connected) {
|
|
1125
|
+
return /* @__PURE__ */ jsxs2("div", {
|
|
1126
|
+
className: "flex items-center justify-between px-3 py-2 rounded-lg border border-green-500/20 bg-green-500/5",
|
|
1127
|
+
children: [
|
|
1128
|
+
/* @__PURE__ */ jsxs2("div", {
|
|
1129
|
+
className: "flex items-center gap-2 text-sm",
|
|
1130
|
+
children: [
|
|
1131
|
+
/* @__PURE__ */ jsx5("span", {
|
|
1132
|
+
className: "h-2 w-2 rounded-full bg-green-500"
|
|
1133
|
+
}),
|
|
1134
|
+
/* @__PURE__ */ jsxs2("span", {
|
|
1135
|
+
className: "text-muted-foreground",
|
|
1136
|
+
children: [
|
|
1137
|
+
getProvider() === "brc100" ? "BRC-100" : "OneSat",
|
|
1138
|
+
" ·",
|
|
1139
|
+
" ",
|
|
1140
|
+
getIdentityKey()?.slice(0, 12),
|
|
1141
|
+
"..."
|
|
1142
|
+
]
|
|
1143
|
+
})
|
|
1144
|
+
]
|
|
1145
|
+
}),
|
|
1146
|
+
/* @__PURE__ */ jsx5(Button, {
|
|
1147
|
+
variant: "ghost",
|
|
1148
|
+
size: "sm",
|
|
1149
|
+
className: "h-6 w-6 p-0",
|
|
1150
|
+
onClick: handleDisconnect,
|
|
1151
|
+
children: /* @__PURE__ */ jsx5(X, {
|
|
1152
|
+
className: "h-3 w-3"
|
|
1083
1153
|
})
|
|
1154
|
+
})
|
|
1155
|
+
]
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
return /* @__PURE__ */ jsxs2("div", {
|
|
1159
|
+
className: "space-y-1",
|
|
1160
|
+
children: [
|
|
1161
|
+
/* @__PURE__ */ jsxs2(Button, {
|
|
1162
|
+
variant: "outline",
|
|
1163
|
+
size: "sm",
|
|
1164
|
+
className: "w-full text-xs gap-2",
|
|
1165
|
+
onClick: handleConnect,
|
|
1166
|
+
disabled: connecting,
|
|
1167
|
+
children: [
|
|
1168
|
+
connecting ? /* @__PURE__ */ jsx5(Loader2, {
|
|
1169
|
+
className: "h-3 w-3 animate-spin"
|
|
1170
|
+
}) : /* @__PURE__ */ jsx5(Wallet, {
|
|
1171
|
+
className: "h-3 w-3"
|
|
1172
|
+
}),
|
|
1173
|
+
connecting ? "Connecting..." : "Connect BRC-100 Wallet (optional)"
|
|
1084
1174
|
]
|
|
1175
|
+
}),
|
|
1176
|
+
error && /* @__PURE__ */ jsx5("p", {
|
|
1177
|
+
className: "text-xs text-destructive text-center",
|
|
1178
|
+
children: error
|
|
1085
1179
|
})
|
|
1086
1180
|
]
|
|
1087
1181
|
});
|
|
1088
1182
|
}
|
|
1089
|
-
|
|
1090
|
-
|
|
1183
|
+
|
|
1184
|
+
// src/components/opns-section.tsx
|
|
1185
|
+
import { useEffect, useState as useState3 } from "react";
|
|
1186
|
+
import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1187
|
+
function OpnsSection({
|
|
1188
|
+
opnsNames,
|
|
1189
|
+
selectedOpns,
|
|
1190
|
+
onToggle,
|
|
1191
|
+
onSelectAll,
|
|
1192
|
+
onDeselectAll,
|
|
1193
|
+
onSweep,
|
|
1194
|
+
onSend,
|
|
1195
|
+
onBurn,
|
|
1196
|
+
walletConnected
|
|
1197
|
+
}) {
|
|
1091
1198
|
const [address, setAddress] = useState3("");
|
|
1092
|
-
|
|
1199
|
+
const [resolvedNames, setResolvedNames] = useState3(new Map);
|
|
1200
|
+
const [overlayValid, setOverlayValid] = useState3(new Map);
|
|
1201
|
+
const [validating, setValidating] = useState3(false);
|
|
1202
|
+
useEffect(() => {
|
|
1203
|
+
if (opnsNames.length === 0)
|
|
1204
|
+
return;
|
|
1205
|
+
const controller = new AbortController;
|
|
1206
|
+
const pending = new Map;
|
|
1207
|
+
Promise.all(opnsNames.map(async (item) => {
|
|
1208
|
+
try {
|
|
1209
|
+
const res = await fetch(item.contentUrl, {
|
|
1210
|
+
signal: controller.signal
|
|
1211
|
+
});
|
|
1212
|
+
if (res.ok)
|
|
1213
|
+
pending.set(item.outpoint, await res.text());
|
|
1214
|
+
} catch {}
|
|
1215
|
+
})).then(() => {
|
|
1216
|
+
if (!controller.signal.aborted)
|
|
1217
|
+
setResolvedNames(new Map(pending));
|
|
1218
|
+
});
|
|
1219
|
+
return () => controller.abort();
|
|
1220
|
+
}, [opnsNames]);
|
|
1221
|
+
useEffect(() => {
|
|
1222
|
+
if (opnsNames.length === 0)
|
|
1223
|
+
return;
|
|
1224
|
+
let cancelled = false;
|
|
1225
|
+
setValidating(true);
|
|
1226
|
+
const originToOutpoints = new Map;
|
|
1227
|
+
for (const item of opnsNames) {
|
|
1228
|
+
const origin = item.origin ?? item.outpoint;
|
|
1229
|
+
const list = originToOutpoints.get(origin) ?? [];
|
|
1230
|
+
list.push(item.outpoint);
|
|
1231
|
+
originToOutpoints.set(origin, list);
|
|
1232
|
+
}
|
|
1233
|
+
getServices().opns.validateOrigins([...originToOutpoints.keys()]).then((result) => {
|
|
1234
|
+
if (cancelled)
|
|
1235
|
+
return;
|
|
1236
|
+
const map = new Map;
|
|
1237
|
+
for (const [origin, valid] of Object.entries(result)) {
|
|
1238
|
+
for (const outpoint of originToOutpoints.get(origin) ?? [])
|
|
1239
|
+
map.set(outpoint, valid);
|
|
1240
|
+
}
|
|
1241
|
+
setOverlayValid(map);
|
|
1242
|
+
}).catch(() => {}).finally(() => {
|
|
1243
|
+
if (!cancelled)
|
|
1244
|
+
setValidating(false);
|
|
1245
|
+
});
|
|
1246
|
+
return () => {
|
|
1247
|
+
cancelled = true;
|
|
1248
|
+
};
|
|
1249
|
+
}, [opnsNames]);
|
|
1250
|
+
if (opnsNames.length === 0)
|
|
1093
1251
|
return null;
|
|
1094
|
-
const totalPages = Math.ceil(ordinals.length / ORDINALS_PER_PAGE);
|
|
1095
|
-
const start = page * ORDINALS_PER_PAGE;
|
|
1096
|
-
const pageItems = ordinals.slice(start, start + ORDINALS_PER_PAGE);
|
|
1097
1252
|
return /* @__PURE__ */ jsxs3("div", {
|
|
1098
|
-
className: "border border-
|
|
1253
|
+
className: "border border-orange-500/20 bg-orange-500/5 p-4 rounded-lg",
|
|
1099
1254
|
children: [
|
|
1100
1255
|
/* @__PURE__ */ jsxs3("div", {
|
|
1101
1256
|
className: "flex items-start justify-between mb-3",
|
|
@@ -1105,26 +1260,26 @@ function OrdinalsSection({ ordinals, selectedOrdinals, onToggle, onSelectAll, on
|
|
|
1105
1260
|
/* @__PURE__ */ jsxs3("div", {
|
|
1106
1261
|
className: "flex items-center gap-2 mb-1",
|
|
1107
1262
|
children: [
|
|
1108
|
-
/* @__PURE__ */
|
|
1109
|
-
className: "h-2 w-2 rounded-full bg-
|
|
1263
|
+
/* @__PURE__ */ jsx6("span", {
|
|
1264
|
+
className: "h-2 w-2 rounded-full bg-orange-500"
|
|
1110
1265
|
}),
|
|
1111
|
-
/* @__PURE__ */
|
|
1112
|
-
className: "text-sm font-semibold text-
|
|
1113
|
-
children: "
|
|
1266
|
+
/* @__PURE__ */ jsx6("span", {
|
|
1267
|
+
className: "text-sm font-semibold text-orange-500",
|
|
1268
|
+
children: "OPNS Domains"
|
|
1114
1269
|
})
|
|
1115
1270
|
]
|
|
1116
1271
|
}),
|
|
1117
1272
|
/* @__PURE__ */ jsxs3("div", {
|
|
1118
1273
|
className: "text-xs text-muted-foreground",
|
|
1119
1274
|
children: [
|
|
1120
|
-
|
|
1121
|
-
"
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
className: "text-
|
|
1275
|
+
opnsNames.length,
|
|
1276
|
+
" domain",
|
|
1277
|
+
opnsNames.length !== 1 ? "s" : "",
|
|
1278
|
+
selectedOpns.size > 0 && /* @__PURE__ */ jsxs3("span", {
|
|
1279
|
+
className: "text-orange-400 ml-1",
|
|
1125
1280
|
children: [
|
|
1126
1281
|
"(",
|
|
1127
|
-
|
|
1282
|
+
selectedOpns.size,
|
|
1128
1283
|
" selected)"
|
|
1129
1284
|
]
|
|
1130
1285
|
})
|
|
@@ -1135,67 +1290,57 @@ function OrdinalsSection({ ordinals, selectedOrdinals, onToggle, onSelectAll, on
|
|
|
1135
1290
|
/* @__PURE__ */ jsxs3("div", {
|
|
1136
1291
|
className: "flex gap-2",
|
|
1137
1292
|
children: [
|
|
1138
|
-
/* @__PURE__ */
|
|
1293
|
+
/* @__PURE__ */ jsx6(Button, {
|
|
1139
1294
|
variant: "outline",
|
|
1140
1295
|
size: "sm",
|
|
1141
1296
|
className: "h-7 text-[11px]",
|
|
1142
1297
|
onClick: onSelectAll,
|
|
1143
1298
|
children: "Select All"
|
|
1144
1299
|
}),
|
|
1145
|
-
/* @__PURE__ */
|
|
1300
|
+
/* @__PURE__ */ jsx6(Button, {
|
|
1146
1301
|
variant: "outline",
|
|
1147
1302
|
size: "sm",
|
|
1148
1303
|
className: "h-7 text-[11px]",
|
|
1149
1304
|
onClick: onDeselectAll,
|
|
1150
|
-
disabled:
|
|
1305
|
+
disabled: selectedOpns.size === 0,
|
|
1151
1306
|
children: "Deselect"
|
|
1152
1307
|
})
|
|
1153
1308
|
]
|
|
1154
1309
|
})
|
|
1155
1310
|
]
|
|
1156
1311
|
}),
|
|
1157
|
-
/* @__PURE__ */
|
|
1158
|
-
className: "
|
|
1159
|
-
children:
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
totalPages > 1 && /* @__PURE__ */ jsxs3("div", {
|
|
1166
|
-
className: "flex items-center justify-center gap-4",
|
|
1167
|
-
children: [
|
|
1168
|
-
/* @__PURE__ */ jsx8(Button, {
|
|
1169
|
-
variant: "ghost",
|
|
1170
|
-
size: "sm",
|
|
1171
|
-
className: "text-xs",
|
|
1172
|
-
onClick: () => setPage(page - 1),
|
|
1173
|
-
disabled: page === 0,
|
|
1174
|
-
children: "Prev"
|
|
1175
|
-
}),
|
|
1176
|
-
/* @__PURE__ */ jsxs3("span", {
|
|
1177
|
-
className: "text-xs text-muted-foreground",
|
|
1312
|
+
/* @__PURE__ */ jsx6("div", {
|
|
1313
|
+
className: "space-y-1",
|
|
1314
|
+
children: opnsNames.map((item) => {
|
|
1315
|
+
const isSelected = selectedOpns.has(item.outpoint);
|
|
1316
|
+
const displayName = resolvedNames.get(item.outpoint) ?? item.outpoint.substring(0, 8) + "...";
|
|
1317
|
+
return /* @__PURE__ */ jsxs3("div", {
|
|
1318
|
+
className: `flex items-center gap-3 px-3 py-2 rounded-lg cursor-pointer transition-all ${isSelected ? "border border-orange-500 bg-orange-500/10 ring-1 ring-orange-500/30" : "border border-border/50 hover:border-border bg-black/20"}`,
|
|
1319
|
+
onClick: () => onToggle(item.outpoint),
|
|
1178
1320
|
children: [
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1321
|
+
/* @__PURE__ */ jsx6("div", {
|
|
1322
|
+
className: `w-4 h-4 rounded border-2 flex items-center justify-center text-[10px] shrink-0 ${isSelected ? "bg-orange-500 border-orange-500 text-white" : "border-muted-foreground/40"}`,
|
|
1323
|
+
children: isSelected && "✓"
|
|
1324
|
+
}),
|
|
1325
|
+
/* @__PURE__ */ jsx6("span", {
|
|
1326
|
+
className: "text-sm text-foreground truncate",
|
|
1327
|
+
children: displayName
|
|
1328
|
+
}),
|
|
1329
|
+
!validating && overlayValid.has(item.outpoint) && (overlayValid.get(item.outpoint) ? /* @__PURE__ */ jsx6("span", {
|
|
1330
|
+
className: "shrink-0 text-[10px] font-medium px-1.5 py-0.5 rounded bg-green-500/20 text-green-400",
|
|
1331
|
+
children: "valid"
|
|
1332
|
+
}) : /* @__PURE__ */ jsx6("span", {
|
|
1333
|
+
className: "shrink-0 text-[10px] font-medium px-1.5 py-0.5 rounded bg-red-500/20 text-red-400",
|
|
1334
|
+
children: "invalid"
|
|
1335
|
+
}))
|
|
1183
1336
|
]
|
|
1184
|
-
})
|
|
1185
|
-
|
|
1186
|
-
variant: "ghost",
|
|
1187
|
-
size: "sm",
|
|
1188
|
-
className: "text-xs",
|
|
1189
|
-
onClick: () => setPage(page + 1),
|
|
1190
|
-
disabled: page >= totalPages - 1,
|
|
1191
|
-
children: "Next"
|
|
1192
|
-
})
|
|
1193
|
-
]
|
|
1337
|
+
}, item.outpoint);
|
|
1338
|
+
})
|
|
1194
1339
|
}),
|
|
1195
|
-
|
|
1340
|
+
selectedOpns.size > 0 && /* @__PURE__ */ jsxs3("div", {
|
|
1196
1341
|
className: "mt-3 space-y-2",
|
|
1197
1342
|
children: [
|
|
1198
|
-
onSend && /* @__PURE__ */
|
|
1343
|
+
onSend && /* @__PURE__ */ jsx6(Input, {
|
|
1199
1344
|
type: "text",
|
|
1200
1345
|
placeholder: "Destination address...",
|
|
1201
1346
|
value: address,
|
|
@@ -1213,12 +1358,12 @@ function OrdinalsSection({ ordinals, selectedOrdinals, onToggle, onSelectAll, on
|
|
|
1213
1358
|
onClick: () => onSend(address.trim()),
|
|
1214
1359
|
children: [
|
|
1215
1360
|
"Send ",
|
|
1216
|
-
|
|
1217
|
-
"
|
|
1218
|
-
|
|
1361
|
+
selectedOpns.size,
|
|
1362
|
+
" Domain",
|
|
1363
|
+
selectedOpns.size !== 1 ? "s" : ""
|
|
1219
1364
|
]
|
|
1220
1365
|
}),
|
|
1221
|
-
/* @__PURE__ */
|
|
1366
|
+
/* @__PURE__ */ jsx6(Button, {
|
|
1222
1367
|
size: "sm",
|
|
1223
1368
|
className: "flex-1",
|
|
1224
1369
|
onClick: onSweep,
|
|
@@ -1226,786 +1371,717 @@ function OrdinalsSection({ ordinals, selectedOrdinals, onToggle, onSelectAll, on
|
|
|
1226
1371
|
title: walletConnected ? undefined : "Connect BRC-100 wallet to sweep",
|
|
1227
1372
|
children: "Sweep to Wallet"
|
|
1228
1373
|
}),
|
|
1229
|
-
onBurn && /* @__PURE__ */
|
|
1374
|
+
onBurn && /* @__PURE__ */ jsx6(Button, {
|
|
1230
1375
|
size: "sm",
|
|
1231
1376
|
className: "bg-red-600 hover:bg-red-700 text-white",
|
|
1232
1377
|
onClick: () => {
|
|
1233
|
-
if (window.confirm(`Permanently burn ${
|
|
1378
|
+
if (window.confirm(`Permanently burn ${selectedOpns.size} domain${selectedOpns.size !== 1 ? "s" : ""}? This cannot be undone.`))
|
|
1234
1379
|
onBurn();
|
|
1235
1380
|
},
|
|
1236
1381
|
children: "Burn"
|
|
1237
1382
|
})
|
|
1238
|
-
]
|
|
1239
|
-
})
|
|
1240
|
-
]
|
|
1241
|
-
})
|
|
1242
|
-
]
|
|
1243
|
-
});
|
|
1244
|
-
}
|
|
1245
|
-
function TokenRow({ tb, onSweep, walletConnected }) {
|
|
1246
|
-
return /* @__PURE__ */ jsxs3("div", {
|
|
1247
|
-
className: `flex items-center justify-between p-3 rounded-lg border ${tb.isActive ? "bg-black/20 border-purple-500/10" : "bg-black/10 border-muted/20 opacity-60"}`,
|
|
1248
|
-
children: [
|
|
1249
|
-
/* @__PURE__ */ jsxs3("div", {
|
|
1250
|
-
className: "flex items-center gap-3",
|
|
1251
|
-
children: [
|
|
1252
|
-
/* @__PURE__ */ jsx8("img", {
|
|
1253
|
-
src: tb.icon,
|
|
1254
|
-
alt: tb.symbol || "Token",
|
|
1255
|
-
className: "w-8 h-8 rounded-full object-cover",
|
|
1256
|
-
onError: (e) => {
|
|
1257
|
-
e.target.style.display = "none";
|
|
1258
|
-
}
|
|
1259
|
-
}),
|
|
1260
|
-
/* @__PURE__ */ jsxs3("div", {
|
|
1261
|
-
children: [
|
|
1262
|
-
/* @__PURE__ */ jsxs3("div", {
|
|
1263
|
-
className: "flex items-center gap-2",
|
|
1264
|
-
children: [
|
|
1265
|
-
/* @__PURE__ */ jsx8("span", {
|
|
1266
|
-
className: "font-medium text-foreground",
|
|
1267
|
-
children: tb.symbol || tb.tokenId.slice(0, 8) + "..."
|
|
1268
|
-
}),
|
|
1269
|
-
tb.isActive ? /* @__PURE__ */ jsx8("span", {
|
|
1270
|
-
className: "px-1.5 py-0.5 text-[9px] rounded bg-green-600/20 text-green-700 dark:text-green-400",
|
|
1271
|
-
children: "active"
|
|
1272
|
-
}) : /* @__PURE__ */ jsx8("span", {
|
|
1273
|
-
className: "px-1.5 py-0.5 text-[9px] rounded bg-muted text-muted-foreground",
|
|
1274
|
-
children: "inactive"
|
|
1275
|
-
})
|
|
1276
|
-
]
|
|
1277
|
-
}),
|
|
1278
|
-
/* @__PURE__ */ jsxs3("div", {
|
|
1279
|
-
className: "text-xs text-muted-foreground",
|
|
1280
|
-
children: [
|
|
1281
|
-
formatTokenAmount(tb.totalAmount.toString(), tb.decimals),
|
|
1282
|
-
" ",
|
|
1283
|
-
tb.symbol || "",
|
|
1284
|
-
/* @__PURE__ */ jsxs3("span", {
|
|
1285
|
-
className: "ml-2",
|
|
1286
|
-
children: [
|
|
1287
|
-
"(",
|
|
1288
|
-
tb.outputs.length,
|
|
1289
|
-
" output",
|
|
1290
|
-
tb.outputs.length !== 1 ? "s" : "",
|
|
1291
|
-
")"
|
|
1292
|
-
]
|
|
1293
|
-
})
|
|
1294
|
-
]
|
|
1295
|
-
})
|
|
1296
|
-
]
|
|
1297
|
-
})
|
|
1298
|
-
]
|
|
1299
|
-
}),
|
|
1300
|
-
tb.isActive && onSweep && /* @__PURE__ */ jsx8(Button, {
|
|
1301
|
-
size: "sm",
|
|
1302
|
-
onClick: () => onSweep(tb.tokenId),
|
|
1303
|
-
disabled: !walletConnected,
|
|
1304
|
-
title: walletConnected ? undefined : "Connect BRC-100 wallet to sweep",
|
|
1305
|
-
children: "Sweep to Wallet"
|
|
1306
|
-
})
|
|
1307
|
-
]
|
|
1308
|
-
});
|
|
1309
|
-
}
|
|
1310
|
-
function Bsv21Section({ tokens, onSweep, walletConnected }) {
|
|
1311
|
-
if (tokens.length === 0)
|
|
1312
|
-
return null;
|
|
1313
|
-
const active = tokens.filter((t) => t.isActive);
|
|
1314
|
-
const inactive = tokens.filter((t) => !t.isActive);
|
|
1315
|
-
return /* @__PURE__ */ jsxs3("div", {
|
|
1316
|
-
className: "border border-purple-500/20 bg-purple-500/5 p-4 rounded-lg",
|
|
1317
|
-
children: [
|
|
1318
|
-
/* @__PURE__ */ jsxs3("div", {
|
|
1319
|
-
className: "flex items-center gap-2 mb-3",
|
|
1320
|
-
children: [
|
|
1321
|
-
/* @__PURE__ */ jsx8("span", {
|
|
1322
|
-
className: "h-2 w-2 rounded-full bg-purple-500"
|
|
1323
|
-
}),
|
|
1324
|
-
/* @__PURE__ */ jsx8("span", {
|
|
1325
|
-
className: "text-sm font-semibold text-purple-500",
|
|
1326
|
-
children: "BSV-21 Tokens"
|
|
1327
|
-
})
|
|
1328
|
-
]
|
|
1329
|
-
}),
|
|
1330
|
-
/* @__PURE__ */ jsxs3("div", {
|
|
1331
|
-
className: "space-y-3",
|
|
1332
|
-
children: [
|
|
1333
|
-
active.map((tb) => /* @__PURE__ */ jsx8(TokenRow, {
|
|
1334
|
-
tb,
|
|
1335
|
-
onSweep,
|
|
1336
|
-
walletConnected
|
|
1337
|
-
}, tb.tokenId)),
|
|
1338
|
-
inactive.length > 0 && active.length > 0 && /* @__PURE__ */ jsx8("div", {
|
|
1339
|
-
className: "border-t border-purple-500/10 pt-3 mt-3",
|
|
1340
|
-
children: /* @__PURE__ */ jsxs3("div", {
|
|
1341
|
-
className: "text-xs text-muted-foreground mb-2",
|
|
1342
|
-
children: [
|
|
1343
|
-
"Inactive overlays (",
|
|
1344
|
-
inactive.length,
|
|
1345
|
-
") — cannot be swept"
|
|
1346
|
-
]
|
|
1347
|
-
})
|
|
1348
|
-
}),
|
|
1349
|
-
inactive.map((tb) => /* @__PURE__ */ jsx8(TokenRow, {
|
|
1350
|
-
tb,
|
|
1351
|
-
walletConnected
|
|
1352
|
-
}, tb.tokenId))
|
|
1353
|
-
]
|
|
1354
|
-
})
|
|
1355
|
-
]
|
|
1356
|
-
});
|
|
1357
|
-
}
|
|
1358
|
-
function Bsv20Section({ tokens }) {
|
|
1359
|
-
if (tokens.length === 0)
|
|
1360
|
-
return null;
|
|
1361
|
-
return /* @__PURE__ */ jsxs3("div", {
|
|
1362
|
-
className: "border border-muted/30 bg-muted/10 p-4 rounded-lg",
|
|
1363
|
-
children: [
|
|
1364
|
-
/* @__PURE__ */ jsxs3("div", {
|
|
1365
|
-
className: "flex items-center gap-2 mb-2",
|
|
1366
|
-
children: [
|
|
1367
|
-
/* @__PURE__ */ jsx8("span", {
|
|
1368
|
-
className: "h-2 w-2 rounded-full bg-muted-foreground"
|
|
1369
|
-
}),
|
|
1370
|
-
/* @__PURE__ */ jsx8("span", {
|
|
1371
|
-
className: "text-sm font-semibold text-muted-foreground",
|
|
1372
|
-
children: "BSV-20 Tokens"
|
|
1373
|
-
})
|
|
1374
|
-
]
|
|
1375
|
-
}),
|
|
1376
|
-
/* @__PURE__ */ jsx8("p", {
|
|
1377
|
-
className: "text-xs text-muted-foreground mb-2",
|
|
1378
|
-
children: "Cannot be swept automatically."
|
|
1379
|
-
}),
|
|
1380
|
-
/* @__PURE__ */ jsxs3("div", {
|
|
1381
|
-
className: "flex flex-wrap gap-2",
|
|
1382
|
-
children: [
|
|
1383
|
-
tokens.slice(0, 10).map((o) => {
|
|
1384
|
-
const tickEvent = o.events?.find((e) => e.startsWith("tick:"));
|
|
1385
|
-
const tick = tickEvent ? tickEvent.slice(5) : "Token";
|
|
1386
|
-
return /* @__PURE__ */ jsx8("span", {
|
|
1387
|
-
className: "px-2 py-1 text-xs rounded bg-muted/30 text-muted-foreground",
|
|
1388
|
-
children: tick
|
|
1389
|
-
}, o.outpoint);
|
|
1390
|
-
}),
|
|
1391
|
-
tokens.length > 10 && /* @__PURE__ */ jsxs3("span", {
|
|
1392
|
-
className: "text-xs text-muted-foreground",
|
|
1393
|
-
children: [
|
|
1394
|
-
"+",
|
|
1395
|
-
tokens.length - 10,
|
|
1396
|
-
" more"
|
|
1397
|
-
]
|
|
1398
|
-
})
|
|
1399
|
-
]
|
|
1400
|
-
})
|
|
1401
|
-
]
|
|
1402
|
-
});
|
|
1403
|
-
}
|
|
1404
|
-
function LockedSection({ locked }) {
|
|
1405
|
-
if (locked.length === 0)
|
|
1406
|
-
return null;
|
|
1407
|
-
return /* @__PURE__ */ jsxs3("div", {
|
|
1408
|
-
className: "border border-yellow-500/20 bg-yellow-500/5 p-4 rounded-lg",
|
|
1409
|
-
children: [
|
|
1410
|
-
/* @__PURE__ */ jsxs3("div", {
|
|
1411
|
-
className: "flex items-center gap-2 mb-2",
|
|
1412
|
-
children: [
|
|
1413
|
-
/* @__PURE__ */ jsx8("span", {
|
|
1414
|
-
className: "h-2 w-2 rounded-full bg-yellow-500"
|
|
1415
|
-
}),
|
|
1416
|
-
/* @__PURE__ */ jsx8("span", {
|
|
1417
|
-
className: "text-sm font-semibold text-yellow-500",
|
|
1418
|
-
children: "Locked Outputs"
|
|
1383
|
+
]
|
|
1419
1384
|
})
|
|
1420
1385
|
]
|
|
1421
|
-
}),
|
|
1422
|
-
/* @__PURE__ */ jsxs3("p", {
|
|
1423
|
-
className: "text-xs text-muted-foreground",
|
|
1424
|
-
children: [
|
|
1425
|
-
locked.length,
|
|
1426
|
-
" locked output",
|
|
1427
|
-
locked.length !== 1 ? "s" : "",
|
|
1428
|
-
". These are in contracts and cannot be swept directly."
|
|
1429
|
-
]
|
|
1430
1386
|
})
|
|
1431
1387
|
]
|
|
1432
1388
|
});
|
|
1433
1389
|
}
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1390
|
+
|
|
1391
|
+
// src/components/tx-history.tsx
|
|
1392
|
+
import { ExternalLink, Loader2 as Loader22 } from "lucide-react";
|
|
1393
|
+
import { jsx as jsx7, jsxs as jsxs4, Fragment } from "react/jsx-runtime";
|
|
1394
|
+
var EXPLORER_BASE = "https://bananablocks.com/tx/";
|
|
1395
|
+
function TxHistory({ sweeping, progress, history }) {
|
|
1396
|
+
return /* @__PURE__ */ jsxs4(Fragment, {
|
|
1440
1397
|
children: [
|
|
1441
|
-
/* @__PURE__ */
|
|
1442
|
-
className: "
|
|
1398
|
+
sweeping && /* @__PURE__ */ jsxs4("div", {
|
|
1399
|
+
className: "text-center space-y-4 py-8",
|
|
1443
1400
|
children: [
|
|
1444
|
-
/* @__PURE__ */
|
|
1445
|
-
className: "h-
|
|
1401
|
+
/* @__PURE__ */ jsx7(Loader22, {
|
|
1402
|
+
className: "h-8 w-8 animate-spin mx-auto text-primary"
|
|
1446
1403
|
}),
|
|
1447
|
-
/* @__PURE__ */
|
|
1448
|
-
className: "text-sm
|
|
1449
|
-
children:
|
|
1404
|
+
/* @__PURE__ */ jsx7("p", {
|
|
1405
|
+
className: "text-sm text-muted-foreground animate-pulse",
|
|
1406
|
+
children: progress
|
|
1407
|
+
}),
|
|
1408
|
+
/* @__PURE__ */ jsx7("p", {
|
|
1409
|
+
className: "text-xs text-destructive/80",
|
|
1410
|
+
children: "Do not close this page."
|
|
1450
1411
|
})
|
|
1451
1412
|
]
|
|
1452
1413
|
}),
|
|
1453
|
-
/* @__PURE__ */
|
|
1454
|
-
className: "
|
|
1414
|
+
history.length > 0 && !sweeping && /* @__PURE__ */ jsxs4("div", {
|
|
1415
|
+
className: "border border-border/50 rounded-lg p-3 space-y-2",
|
|
1455
1416
|
children: [
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1417
|
+
/* @__PURE__ */ jsxs4("div", {
|
|
1418
|
+
className: "text-xs font-medium text-muted-foreground",
|
|
1419
|
+
children: [
|
|
1420
|
+
"Transactions (",
|
|
1421
|
+
history.length,
|
|
1422
|
+
")"
|
|
1423
|
+
]
|
|
1424
|
+
}),
|
|
1425
|
+
/* @__PURE__ */ jsx7("div", {
|
|
1426
|
+
className: "space-y-2 max-h-48 overflow-y-auto",
|
|
1427
|
+
children: [...history].reverse().map((tx, i) => /* @__PURE__ */ jsxs4("div", {
|
|
1428
|
+
className: "flex items-center justify-between gap-2 text-xs",
|
|
1429
|
+
children: [
|
|
1430
|
+
/* @__PURE__ */ jsxs4("div", {
|
|
1431
|
+
className: "flex items-center gap-2 min-w-0",
|
|
1432
|
+
children: [
|
|
1433
|
+
/* @__PURE__ */ jsx7("span", {
|
|
1434
|
+
className: tx.error ? "text-red-500" : "text-green-500",
|
|
1435
|
+
children: tx.error ? "✗" : "✓"
|
|
1436
|
+
}),
|
|
1437
|
+
/* @__PURE__ */ jsx7("span", {
|
|
1438
|
+
className: "text-muted-foreground truncate",
|
|
1439
|
+
children: tx.label
|
|
1440
|
+
})
|
|
1441
|
+
]
|
|
1442
|
+
}),
|
|
1443
|
+
tx.error ? /* @__PURE__ */ jsx7("span", {
|
|
1444
|
+
className: "text-red-500 text-[10px] truncate max-w-[200px]",
|
|
1445
|
+
children: tx.error
|
|
1446
|
+
}) : /* @__PURE__ */ jsxs4("a", {
|
|
1447
|
+
href: `${EXPLORER_BASE}${tx.txid}`,
|
|
1448
|
+
target: "_blank",
|
|
1449
|
+
rel: "noopener noreferrer",
|
|
1450
|
+
className: "text-blue-400 hover:text-blue-300 flex items-center gap-1 shrink-0",
|
|
1451
|
+
children: [
|
|
1452
|
+
/* @__PURE__ */ jsxs4("code", {
|
|
1453
|
+
className: "text-[10px] font-mono",
|
|
1454
|
+
children: [
|
|
1455
|
+
tx.txid.substring(0, 12),
|
|
1456
|
+
"..."
|
|
1457
|
+
]
|
|
1458
|
+
}),
|
|
1459
|
+
/* @__PURE__ */ jsx7(ExternalLink, {
|
|
1460
|
+
className: "h-3 w-3"
|
|
1461
|
+
})
|
|
1462
|
+
]
|
|
1463
|
+
})
|
|
1464
|
+
]
|
|
1465
|
+
}, `${tx.txid}-${i}`))
|
|
1466
|
+
})
|
|
1462
1467
|
]
|
|
1463
1468
|
})
|
|
1464
1469
|
]
|
|
1465
1470
|
});
|
|
1466
1471
|
}
|
|
1467
1472
|
|
|
1468
|
-
// src/components/
|
|
1469
|
-
import
|
|
1470
|
-
import { jsx as
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
const
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1473
|
+
// src/components/ui/tabs.tsx
|
|
1474
|
+
import * as React from "react";
|
|
1475
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
1476
|
+
var TabsContext = React.createContext(null);
|
|
1477
|
+
function useTabsContext() {
|
|
1478
|
+
const context = React.useContext(TabsContext);
|
|
1479
|
+
if (!context) {
|
|
1480
|
+
throw new Error("Tabs components must be used within a <Tabs> provider");
|
|
1481
|
+
}
|
|
1482
|
+
return context;
|
|
1483
|
+
}
|
|
1484
|
+
function Tabs({
|
|
1485
|
+
value,
|
|
1486
|
+
onValueChange,
|
|
1487
|
+
className,
|
|
1488
|
+
...props
|
|
1489
|
+
}) {
|
|
1490
|
+
return /* @__PURE__ */ jsx8(TabsContext.Provider, {
|
|
1491
|
+
value: { value, onValueChange },
|
|
1492
|
+
children: /* @__PURE__ */ jsx8("div", {
|
|
1493
|
+
"data-slot": "tabs",
|
|
1494
|
+
className: cn("flex flex-col gap-2", className),
|
|
1495
|
+
...props
|
|
1496
|
+
})
|
|
1497
|
+
});
|
|
1498
|
+
}
|
|
1499
|
+
function TabsList({ className, ...props }) {
|
|
1500
|
+
return /* @__PURE__ */ jsx8("div", {
|
|
1501
|
+
"data-slot": "tabs-list",
|
|
1502
|
+
className: cn("inline-flex h-9 items-center justify-start gap-1 rounded-lg bg-muted p-1 text-muted-foreground", className),
|
|
1503
|
+
...props
|
|
1504
|
+
});
|
|
1505
|
+
}
|
|
1506
|
+
function TabsTrigger({
|
|
1507
|
+
value,
|
|
1508
|
+
className,
|
|
1509
|
+
...props
|
|
1510
|
+
}) {
|
|
1511
|
+
const context = useTabsContext();
|
|
1512
|
+
const isActive = context.value === value;
|
|
1513
|
+
return /* @__PURE__ */ jsx8("button", {
|
|
1514
|
+
"data-slot": "tabs-trigger",
|
|
1515
|
+
"data-active": isActive ? "" : undefined,
|
|
1516
|
+
className: cn("inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50", isActive && "bg-background text-foreground shadow-sm", className),
|
|
1517
|
+
onClick: () => context.onValueChange(value),
|
|
1518
|
+
...props
|
|
1519
|
+
});
|
|
1520
|
+
}
|
|
1521
|
+
function TabsContent({
|
|
1522
|
+
value,
|
|
1523
|
+
className,
|
|
1524
|
+
...props
|
|
1525
|
+
}) {
|
|
1526
|
+
const context = useTabsContext();
|
|
1527
|
+
if (context.value !== value)
|
|
1528
|
+
return null;
|
|
1529
|
+
return /* @__PURE__ */ jsx8("div", {
|
|
1530
|
+
"data-slot": "tabs-content",
|
|
1531
|
+
className: cn("mt-2", className),
|
|
1532
|
+
...props
|
|
1533
|
+
});
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
// src/components/wif-input.tsx
|
|
1537
|
+
import { deriveIdentityKey } from "@1sat/utils";
|
|
1538
|
+
import {
|
|
1539
|
+
decryptBackup,
|
|
1540
|
+
getBackupType,
|
|
1541
|
+
isOneSatBackup,
|
|
1542
|
+
isWifBackup,
|
|
1543
|
+
isYoursWalletBackup
|
|
1544
|
+
} from "bitcoin-backup";
|
|
1545
|
+
import { KeyRound, Loader2 as Loader23, Search, Upload, X as X2 } from "lucide-react";
|
|
1546
|
+
import { useCallback, useRef, useState as useState4 } from "react";
|
|
1547
|
+
|
|
1548
|
+
// src/components/ui/card.tsx
|
|
1549
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
1550
|
+
function Card({ className, ...props }) {
|
|
1551
|
+
return /* @__PURE__ */ jsx9("div", {
|
|
1552
|
+
"data-slot": "card",
|
|
1553
|
+
className: cn("flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-sm", className),
|
|
1554
|
+
...props
|
|
1555
|
+
});
|
|
1556
|
+
}
|
|
1557
|
+
function CardHeader({ className, ...props }) {
|
|
1558
|
+
return /* @__PURE__ */ jsx9("div", {
|
|
1559
|
+
"data-slot": "card-header",
|
|
1560
|
+
className: cn("@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6", className),
|
|
1561
|
+
...props
|
|
1562
|
+
});
|
|
1563
|
+
}
|
|
1564
|
+
function CardTitle({ className, ...props }) {
|
|
1565
|
+
return /* @__PURE__ */ jsx9("div", {
|
|
1566
|
+
"data-slot": "card-title",
|
|
1567
|
+
className: cn("leading-none font-semibold", className),
|
|
1568
|
+
...props
|
|
1569
|
+
});
|
|
1570
|
+
}
|
|
1571
|
+
function CardDescription({ className, ...props }) {
|
|
1572
|
+
return /* @__PURE__ */ jsx9("div", {
|
|
1573
|
+
"data-slot": "card-description",
|
|
1574
|
+
className: cn("text-sm text-muted-foreground", className),
|
|
1575
|
+
...props
|
|
1576
|
+
});
|
|
1577
|
+
}
|
|
1578
|
+
function CardAction({ className, ...props }) {
|
|
1579
|
+
return /* @__PURE__ */ jsx9("div", {
|
|
1580
|
+
"data-slot": "card-action",
|
|
1581
|
+
className: cn("col-start-2 row-span-2 row-start-1 self-start justify-self-end", className),
|
|
1582
|
+
...props
|
|
1583
|
+
});
|
|
1584
|
+
}
|
|
1585
|
+
function CardContent({ className, ...props }) {
|
|
1586
|
+
return /* @__PURE__ */ jsx9("div", {
|
|
1587
|
+
"data-slot": "card-content",
|
|
1588
|
+
className: cn("px-6", className),
|
|
1589
|
+
...props
|
|
1590
|
+
});
|
|
1591
|
+
}
|
|
1592
|
+
function CardFooter({ className, ...props }) {
|
|
1593
|
+
return /* @__PURE__ */ jsx9("div", {
|
|
1594
|
+
"data-slot": "card-footer",
|
|
1595
|
+
className: cn("flex items-center px-6 [.border-t]:pt-6", className),
|
|
1596
|
+
...props
|
|
1597
|
+
});
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
// src/components/wif-input.tsx
|
|
1601
|
+
import { jsx as jsx10, jsxs as jsxs5, Fragment as Fragment2 } from "react/jsx-runtime";
|
|
1602
|
+
function withIdentityKey(keys) {
|
|
1603
|
+
if (keys.identityPk)
|
|
1604
|
+
return keys;
|
|
1605
|
+
return {
|
|
1606
|
+
...keys,
|
|
1607
|
+
identityPk: deriveIdentityKey(keys.payPk, keys.ordPk).toWif()
|
|
1608
|
+
};
|
|
1609
|
+
}
|
|
1610
|
+
function extractKeys(backup) {
|
|
1611
|
+
if (isOneSatBackup(backup)) {
|
|
1612
|
+
return withIdentityKey({
|
|
1613
|
+
payPk: backup.payPk,
|
|
1614
|
+
ordPk: backup.ordPk,
|
|
1615
|
+
identityPk: backup.identityPk
|
|
1616
|
+
});
|
|
1617
|
+
}
|
|
1618
|
+
if (isYoursWalletBackup(backup)) {
|
|
1619
|
+
return withIdentityKey({
|
|
1620
|
+
payPk: backup.payPk,
|
|
1621
|
+
ordPk: backup.ordPk,
|
|
1622
|
+
identityPk: backup.identityPk
|
|
1623
|
+
});
|
|
1624
|
+
}
|
|
1625
|
+
if (isWifBackup(backup)) {
|
|
1626
|
+
return withIdentityKey({
|
|
1627
|
+
payPk: backup.wif,
|
|
1628
|
+
ordPk: backup.wif
|
|
1629
|
+
});
|
|
1630
|
+
}
|
|
1631
|
+
return null;
|
|
1632
|
+
}
|
|
1633
|
+
function tryParseBackup(text) {
|
|
1634
|
+
try {
|
|
1635
|
+
const parsed = JSON.parse(text);
|
|
1636
|
+
if (parsed.payPk && parsed.ordPk) {
|
|
1637
|
+
return withIdentityKey({
|
|
1638
|
+
payPk: parsed.payPk,
|
|
1639
|
+
ordPk: parsed.ordPk,
|
|
1640
|
+
identityPk: parsed.identityPk
|
|
1641
|
+
});
|
|
1642
|
+
}
|
|
1643
|
+
if (parsed.accounts && parsed.selectedAccount) {
|
|
1644
|
+
const account = parsed.accounts[parsed.selectedAccount];
|
|
1645
|
+
if (account?.encryptedKeys) {
|
|
1646
|
+
return null;
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
} catch {}
|
|
1650
|
+
return null;
|
|
1651
|
+
}
|
|
1652
|
+
function looksEncrypted(text) {
|
|
1653
|
+
if (text.startsWith("{"))
|
|
1654
|
+
return false;
|
|
1655
|
+
if (text.startsWith("5") || text.startsWith("K") || text.startsWith("L"))
|
|
1656
|
+
return false;
|
|
1657
|
+
return text.length > 50;
|
|
1658
|
+
}
|
|
1659
|
+
function WifInput({ onScan, scanning, disabled }) {
|
|
1660
|
+
const [mode, setMode] = useState4("choose");
|
|
1661
|
+
const [keys, setKeys] = useState4(null);
|
|
1662
|
+
const [backupText, setBackupText] = useState4("");
|
|
1663
|
+
const [password, setPassword] = useState4("");
|
|
1664
|
+
const [needsPassword, setNeedsPassword] = useState4(false);
|
|
1665
|
+
const [decrypting, setDecrypting] = useState4(false);
|
|
1666
|
+
const [error, setError] = useState4("");
|
|
1667
|
+
const [backupType, setBackupType] = useState4("");
|
|
1668
|
+
const fileInputRef = useRef(null);
|
|
1669
|
+
const [payWif, setPayWif] = useState4("");
|
|
1670
|
+
const [ordWif, setOrdWif] = useState4("");
|
|
1671
|
+
const [sameKey, setSameKey] = useState4(true);
|
|
1672
|
+
const handleReset = useCallback(() => {
|
|
1673
|
+
setKeys(null);
|
|
1674
|
+
setBackupText("");
|
|
1675
|
+
setPassword("");
|
|
1676
|
+
setNeedsPassword(false);
|
|
1677
|
+
setError("");
|
|
1678
|
+
setBackupType("");
|
|
1679
|
+
setMode("choose");
|
|
1680
|
+
setPayWif("");
|
|
1681
|
+
setOrdWif("");
|
|
1682
|
+
}, []);
|
|
1683
|
+
const processBackupText = useCallback(async (text) => {
|
|
1684
|
+
setError("");
|
|
1685
|
+
setBackupText(text);
|
|
1686
|
+
const directKeys = tryParseBackup(text);
|
|
1687
|
+
if (directKeys) {
|
|
1688
|
+
setKeys(directKeys);
|
|
1689
|
+
setBackupType("JSON");
|
|
1478
1690
|
return;
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
}
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1691
|
+
}
|
|
1692
|
+
try {
|
|
1693
|
+
const parsed = JSON.parse(text);
|
|
1694
|
+
if (parsed.encryptedBackup) {
|
|
1695
|
+
setBackupText(parsed.encryptedBackup);
|
|
1696
|
+
setNeedsPassword(true);
|
|
1697
|
+
return;
|
|
1698
|
+
}
|
|
1699
|
+
if (parsed.encryptedKeys) {
|
|
1700
|
+
setBackupText(parsed.encryptedKeys);
|
|
1701
|
+
setNeedsPassword(true);
|
|
1702
|
+
return;
|
|
1703
|
+
}
|
|
1704
|
+
if (parsed.accounts && parsed.selectedAccount) {
|
|
1705
|
+
const account = parsed.accounts[parsed.selectedAccount];
|
|
1706
|
+
if (account?.encryptedKeys) {
|
|
1707
|
+
setBackupText(account.encryptedKeys);
|
|
1708
|
+
setNeedsPassword(true);
|
|
1709
|
+
return;
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
} catch {}
|
|
1713
|
+
if (looksEncrypted(text)) {
|
|
1714
|
+
setNeedsPassword(true);
|
|
1495
1715
|
return;
|
|
1496
|
-
let cancelled = false;
|
|
1497
|
-
setValidating(true);
|
|
1498
|
-
const originToOutpoints = new Map;
|
|
1499
|
-
for (const item of opnsNames) {
|
|
1500
|
-
const origin = item.origin ?? item.outpoint;
|
|
1501
|
-
const list = originToOutpoints.get(origin) ?? [];
|
|
1502
|
-
list.push(item.outpoint);
|
|
1503
|
-
originToOutpoints.set(origin, list);
|
|
1504
1716
|
}
|
|
1505
|
-
|
|
1506
|
-
|
|
1717
|
+
setError("Unrecognized backup format");
|
|
1718
|
+
}, []);
|
|
1719
|
+
const handleDecrypt = useCallback(async () => {
|
|
1720
|
+
if (!backupText || !password)
|
|
1721
|
+
return;
|
|
1722
|
+
setDecrypting(true);
|
|
1723
|
+
setError("");
|
|
1724
|
+
try {
|
|
1725
|
+
const decrypted = await decryptBackup(backupText, password);
|
|
1726
|
+
const extracted = extractKeys(decrypted);
|
|
1727
|
+
if (!extracted) {
|
|
1728
|
+
setError(`Unsupported backup type: ${getBackupType(decrypted)}`);
|
|
1507
1729
|
return;
|
|
1508
|
-
const map = new Map;
|
|
1509
|
-
for (const [origin, valid] of Object.entries(result)) {
|
|
1510
|
-
for (const outpoint of originToOutpoints.get(origin) ?? [])
|
|
1511
|
-
map.set(outpoint, valid);
|
|
1512
1730
|
}
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1731
|
+
setKeys(extracted);
|
|
1732
|
+
setBackupType(getBackupType(decrypted));
|
|
1733
|
+
setNeedsPassword(false);
|
|
1734
|
+
} catch (e) {
|
|
1735
|
+
setError(e instanceof Error ? e.message : "Decryption failed");
|
|
1736
|
+
} finally {
|
|
1737
|
+
setDecrypting(false);
|
|
1738
|
+
}
|
|
1739
|
+
}, [backupText, password]);
|
|
1740
|
+
const handleFileUpload = useCallback((e) => {
|
|
1741
|
+
const file = e.target.files?.[0];
|
|
1742
|
+
if (!file)
|
|
1743
|
+
return;
|
|
1744
|
+
const reader = new FileReader;
|
|
1745
|
+
reader.onload = () => {
|
|
1746
|
+
processBackupText(reader.result.trim());
|
|
1520
1747
|
};
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1748
|
+
reader.readAsText(file);
|
|
1749
|
+
e.target.value = "";
|
|
1750
|
+
}, [processBackupText]);
|
|
1751
|
+
const handleScan = useCallback(() => {
|
|
1752
|
+
if (keys) {
|
|
1753
|
+
onScan(keys);
|
|
1754
|
+
} else if (mode === "wif") {
|
|
1755
|
+
const pay = payWif.trim();
|
|
1756
|
+
const ord = sameKey ? pay : ordWif.trim();
|
|
1757
|
+
if (pay)
|
|
1758
|
+
onScan({ payPk: pay, ordPk: ord });
|
|
1759
|
+
}
|
|
1760
|
+
}, [keys, mode, payWif, ordWif, sameKey, onScan]);
|
|
1761
|
+
if (keys) {
|
|
1762
|
+
return /* @__PURE__ */ jsxs5(Card, {
|
|
1763
|
+
children: [
|
|
1764
|
+
/* @__PURE__ */ jsx10(CardHeader, {
|
|
1765
|
+
children: /* @__PURE__ */ jsxs5("div", {
|
|
1766
|
+
className: "flex items-center justify-between",
|
|
1531
1767
|
children: [
|
|
1532
|
-
/* @__PURE__ */
|
|
1533
|
-
className: "flex items-center gap-2
|
|
1768
|
+
/* @__PURE__ */ jsxs5(CardTitle, {
|
|
1769
|
+
className: "flex items-center gap-2",
|
|
1534
1770
|
children: [
|
|
1535
|
-
/* @__PURE__ */
|
|
1536
|
-
className: "h-
|
|
1771
|
+
/* @__PURE__ */ jsx10(KeyRound, {
|
|
1772
|
+
className: "h-5 w-5"
|
|
1537
1773
|
}),
|
|
1538
|
-
|
|
1539
|
-
className: "text-sm font-semibold text-orange-500",
|
|
1540
|
-
children: "OPNS Domains"
|
|
1541
|
-
})
|
|
1542
|
-
]
|
|
1543
|
-
}),
|
|
1544
|
-
/* @__PURE__ */ jsxs4("div", {
|
|
1545
|
-
className: "text-xs text-muted-foreground",
|
|
1546
|
-
children: [
|
|
1547
|
-
opnsNames.length,
|
|
1548
|
-
" domain",
|
|
1549
|
-
opnsNames.length !== 1 ? "s" : "",
|
|
1550
|
-
selectedOpns.size > 0 && /* @__PURE__ */ jsxs4("span", {
|
|
1551
|
-
className: "text-orange-400 ml-1",
|
|
1552
|
-
children: [
|
|
1553
|
-
"(",
|
|
1554
|
-
selectedOpns.size,
|
|
1555
|
-
" selected)"
|
|
1556
|
-
]
|
|
1557
|
-
})
|
|
1558
|
-
]
|
|
1559
|
-
})
|
|
1560
|
-
]
|
|
1561
|
-
}),
|
|
1562
|
-
/* @__PURE__ */ jsxs4("div", {
|
|
1563
|
-
className: "flex gap-2",
|
|
1564
|
-
children: [
|
|
1565
|
-
/* @__PURE__ */ jsx9(Button, {
|
|
1566
|
-
variant: "outline",
|
|
1567
|
-
size: "sm",
|
|
1568
|
-
className: "h-7 text-[11px]",
|
|
1569
|
-
onClick: onSelectAll,
|
|
1570
|
-
children: "Select All"
|
|
1571
|
-
}),
|
|
1572
|
-
/* @__PURE__ */ jsx9(Button, {
|
|
1573
|
-
variant: "outline",
|
|
1574
|
-
size: "sm",
|
|
1575
|
-
className: "h-7 text-[11px]",
|
|
1576
|
-
onClick: onDeselectAll,
|
|
1577
|
-
disabled: selectedOpns.size === 0,
|
|
1578
|
-
children: "Deselect"
|
|
1579
|
-
})
|
|
1580
|
-
]
|
|
1581
|
-
})
|
|
1582
|
-
]
|
|
1583
|
-
}),
|
|
1584
|
-
/* @__PURE__ */ jsx9("div", {
|
|
1585
|
-
className: "space-y-1",
|
|
1586
|
-
children: opnsNames.map((item) => {
|
|
1587
|
-
const isSelected = selectedOpns.has(item.outpoint);
|
|
1588
|
-
const displayName = resolvedNames.get(item.outpoint) ?? item.outpoint.substring(0, 8) + "...";
|
|
1589
|
-
return /* @__PURE__ */ jsxs4("div", {
|
|
1590
|
-
className: `flex items-center gap-3 px-3 py-2 rounded-lg cursor-pointer transition-all ${isSelected ? "border border-orange-500 bg-orange-500/10 ring-1 ring-orange-500/30" : "border border-border/50 hover:border-border bg-black/20"}`,
|
|
1591
|
-
onClick: () => onToggle(item.outpoint),
|
|
1592
|
-
children: [
|
|
1593
|
-
/* @__PURE__ */ jsx9("div", {
|
|
1594
|
-
className: `w-4 h-4 rounded border-2 flex items-center justify-center text-[10px] shrink-0 ${isSelected ? "bg-orange-500 border-orange-500 text-white" : "border-muted-foreground/40"}`,
|
|
1595
|
-
children: isSelected && "✓"
|
|
1596
|
-
}),
|
|
1597
|
-
/* @__PURE__ */ jsx9("span", {
|
|
1598
|
-
className: "text-sm text-foreground truncate",
|
|
1599
|
-
children: displayName
|
|
1600
|
-
}),
|
|
1601
|
-
!validating && overlayValid.has(item.outpoint) && (overlayValid.get(item.outpoint) ? /* @__PURE__ */ jsx9("span", {
|
|
1602
|
-
className: "shrink-0 text-[10px] font-medium px-1.5 py-0.5 rounded bg-green-500/20 text-green-400",
|
|
1603
|
-
children: "valid"
|
|
1604
|
-
}) : /* @__PURE__ */ jsx9("span", {
|
|
1605
|
-
className: "shrink-0 text-[10px] font-medium px-1.5 py-0.5 rounded bg-red-500/20 text-red-400",
|
|
1606
|
-
children: "invalid"
|
|
1607
|
-
}))
|
|
1608
|
-
]
|
|
1609
|
-
}, item.outpoint);
|
|
1610
|
-
})
|
|
1611
|
-
}),
|
|
1612
|
-
selectedOpns.size > 0 && /* @__PURE__ */ jsxs4("div", {
|
|
1613
|
-
className: "mt-3 space-y-2",
|
|
1614
|
-
children: [
|
|
1615
|
-
onSend && /* @__PURE__ */ jsx9(Input, {
|
|
1616
|
-
type: "text",
|
|
1617
|
-
placeholder: "Destination address...",
|
|
1618
|
-
value: address,
|
|
1619
|
-
onChange: (e) => setAddress(e.target.value),
|
|
1620
|
-
className: "font-mono text-xs"
|
|
1621
|
-
}),
|
|
1622
|
-
/* @__PURE__ */ jsxs4("div", {
|
|
1623
|
-
className: "flex gap-2",
|
|
1624
|
-
children: [
|
|
1625
|
-
onSend && /* @__PURE__ */ jsxs4(Button, {
|
|
1626
|
-
variant: "outline",
|
|
1627
|
-
size: "sm",
|
|
1628
|
-
className: "flex-1",
|
|
1629
|
-
disabled: !address.trim(),
|
|
1630
|
-
onClick: () => onSend(address.trim()),
|
|
1631
|
-
children: [
|
|
1632
|
-
"Send ",
|
|
1633
|
-
selectedOpns.size,
|
|
1634
|
-
" Domain",
|
|
1635
|
-
selectedOpns.size !== 1 ? "s" : ""
|
|
1774
|
+
"Legacy Keys"
|
|
1636
1775
|
]
|
|
1637
1776
|
}),
|
|
1638
|
-
/* @__PURE__ */
|
|
1639
|
-
|
|
1640
|
-
className: "flex-1",
|
|
1641
|
-
onClick: onSweep,
|
|
1642
|
-
disabled: !walletConnected,
|
|
1643
|
-
title: walletConnected ? undefined : "Connect BRC-100 wallet to sweep",
|
|
1644
|
-
children: "Sweep to Wallet"
|
|
1645
|
-
}),
|
|
1646
|
-
onBurn && /* @__PURE__ */ jsx9(Button, {
|
|
1777
|
+
/* @__PURE__ */ jsx10(Button, {
|
|
1778
|
+
variant: "ghost",
|
|
1647
1779
|
size: "sm",
|
|
1648
|
-
className: "
|
|
1649
|
-
onClick:
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
}
|
|
1653
|
-
|
|
1654
|
-
})
|
|
1655
|
-
]
|
|
1656
|
-
})
|
|
1657
|
-
]
|
|
1658
|
-
})
|
|
1659
|
-
]
|
|
1660
|
-
});
|
|
1661
|
-
}
|
|
1662
|
-
|
|
1663
|
-
// src/components/tx-history.tsx
|
|
1664
|
-
import { Loader2 as Loader23, ExternalLink } from "lucide-react";
|
|
1665
|
-
import { jsx as jsx10, jsxs as jsxs5, Fragment as Fragment2 } from "react/jsx-runtime";
|
|
1666
|
-
var EXPLORER_BASE = "https://bananablocks.com/tx/";
|
|
1667
|
-
function TxHistory({ sweeping, progress, history }) {
|
|
1668
|
-
return /* @__PURE__ */ jsxs5(Fragment2, {
|
|
1669
|
-
children: [
|
|
1670
|
-
sweeping && /* @__PURE__ */ jsxs5("div", {
|
|
1671
|
-
className: "text-center space-y-4 py-8",
|
|
1672
|
-
children: [
|
|
1673
|
-
/* @__PURE__ */ jsx10(Loader23, {
|
|
1674
|
-
className: "h-8 w-8 animate-spin mx-auto text-primary"
|
|
1675
|
-
}),
|
|
1676
|
-
/* @__PURE__ */ jsx10("p", {
|
|
1677
|
-
className: "text-sm text-muted-foreground animate-pulse",
|
|
1678
|
-
children: progress
|
|
1679
|
-
}),
|
|
1680
|
-
/* @__PURE__ */ jsx10("p", {
|
|
1681
|
-
className: "text-xs text-destructive/80",
|
|
1682
|
-
children: "Do not close this page."
|
|
1683
|
-
})
|
|
1684
|
-
]
|
|
1685
|
-
}),
|
|
1686
|
-
history.length > 0 && !sweeping && /* @__PURE__ */ jsxs5("div", {
|
|
1687
|
-
className: "border border-border/50 rounded-lg p-3 space-y-2",
|
|
1688
|
-
children: [
|
|
1689
|
-
/* @__PURE__ */ jsxs5("div", {
|
|
1690
|
-
className: "text-xs font-medium text-muted-foreground",
|
|
1691
|
-
children: [
|
|
1692
|
-
"Transactions (",
|
|
1693
|
-
history.length,
|
|
1694
|
-
")"
|
|
1780
|
+
className: "h-6 w-6 p-0",
|
|
1781
|
+
onClick: handleReset,
|
|
1782
|
+
children: /* @__PURE__ */ jsx10(X2, {
|
|
1783
|
+
className: "h-3 w-3"
|
|
1784
|
+
})
|
|
1785
|
+
})
|
|
1695
1786
|
]
|
|
1696
|
-
})
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1787
|
+
})
|
|
1788
|
+
}),
|
|
1789
|
+
/* @__PURE__ */ jsxs5(CardContent, {
|
|
1790
|
+
className: "space-y-3",
|
|
1791
|
+
children: [
|
|
1792
|
+
backupType && /* @__PURE__ */ jsx10("span", {
|
|
1793
|
+
className: "text-xs px-2 py-0.5 rounded bg-blue-500/20 text-blue-400",
|
|
1794
|
+
children: backupType
|
|
1795
|
+
}),
|
|
1796
|
+
/* @__PURE__ */ jsxs5("div", {
|
|
1797
|
+
className: "space-y-1 text-xs text-muted-foreground font-mono",
|
|
1701
1798
|
children: [
|
|
1702
1799
|
/* @__PURE__ */ jsxs5("div", {
|
|
1703
|
-
className: "flex items-center gap-2 min-w-0",
|
|
1704
1800
|
children: [
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
children: tx.error ? "✗" : "✓"
|
|
1708
|
-
}),
|
|
1709
|
-
/* @__PURE__ */ jsx10("span", {
|
|
1710
|
-
className: "text-muted-foreground truncate",
|
|
1711
|
-
children: tx.label
|
|
1712
|
-
})
|
|
1801
|
+
"Pay: ",
|
|
1802
|
+
deriveAddress(keys.payPk)
|
|
1713
1803
|
]
|
|
1714
1804
|
}),
|
|
1715
|
-
|
|
1716
|
-
className: "text-red-500 text-[10px] truncate max-w-[200px]",
|
|
1717
|
-
children: tx.error
|
|
1718
|
-
}) : /* @__PURE__ */ jsxs5("a", {
|
|
1719
|
-
href: `${EXPLORER_BASE}${tx.txid}`,
|
|
1720
|
-
target: "_blank",
|
|
1721
|
-
rel: "noopener noreferrer",
|
|
1722
|
-
className: "text-blue-400 hover:text-blue-300 flex items-center gap-1 shrink-0",
|
|
1805
|
+
keys.ordPk !== keys.payPk && /* @__PURE__ */ jsxs5("div", {
|
|
1723
1806
|
children: [
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
className: "h-3 w-3"
|
|
1733
|
-
})
|
|
1807
|
+
"Ord: ",
|
|
1808
|
+
deriveAddress(keys.ordPk)
|
|
1809
|
+
]
|
|
1810
|
+
}),
|
|
1811
|
+
keys.identityPk && keys.identityPk !== keys.payPk && /* @__PURE__ */ jsxs5("div", {
|
|
1812
|
+
children: [
|
|
1813
|
+
"ID: ",
|
|
1814
|
+
deriveAddress(keys.identityPk)
|
|
1734
1815
|
]
|
|
1735
1816
|
})
|
|
1736
1817
|
]
|
|
1737
|
-
},
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
}
|
|
1753
|
-
|
|
1754
|
-
return output.events?.find((e) => e.startsWith("own:"))?.slice(4);
|
|
1755
|
-
}
|
|
1756
|
-
function buildKeys(outputs, keyMap) {
|
|
1757
|
-
return outputs.map((output) => {
|
|
1758
|
-
const owner = getOwner(output);
|
|
1759
|
-
const key = owner ? keyMap.get(owner) : undefined;
|
|
1760
|
-
if (!key)
|
|
1761
|
-
throw new Error(`No key for output ${output.outpoint} (owner: ${owner})`);
|
|
1762
|
-
return key;
|
|
1763
|
-
});
|
|
1764
|
-
}
|
|
1765
|
-
async function executeSweep(params) {
|
|
1766
|
-
const { wallet, keys, funding, ordinals, amount, onProgress } = params;
|
|
1767
|
-
const ctx = createContext2(wallet, { services: getServices(), chain: "main" });
|
|
1768
|
-
const result = {
|
|
1769
|
-
ordinalTxids: [],
|
|
1770
|
-
bsv21Txids: [],
|
|
1771
|
-
errors: []
|
|
1772
|
-
};
|
|
1773
|
-
if (funding.length > 0) {
|
|
1774
|
-
onProgress(`Sweeping ${funding.length} BSV UTXOs...`);
|
|
1775
|
-
try {
|
|
1776
|
-
const inputs = await prepareSweepInputs(ctx, funding);
|
|
1777
|
-
const bsvResult = await sweepBsv.execute(ctx, { inputs, keys: buildKeys(funding, keys), amount });
|
|
1778
|
-
if (bsvResult.error)
|
|
1779
|
-
result.errors.push(`BSV: ${bsvResult.error}`);
|
|
1780
|
-
else if (bsvResult.txid)
|
|
1781
|
-
result.bsvTxid = bsvResult.txid;
|
|
1782
|
-
} catch (e) {
|
|
1783
|
-
result.errors.push(`BSV: ${e instanceof Error ? e.message : String(e)}`);
|
|
1784
|
-
}
|
|
1785
|
-
}
|
|
1786
|
-
if (ordinals.length > 0) {
|
|
1787
|
-
onProgress(`Sweeping ${ordinals.length} ordinals...`);
|
|
1788
|
-
try {
|
|
1789
|
-
const inputs = await prepareSweepInputs(ctx, ordinals);
|
|
1790
|
-
const ordResult = await sweepOrdinals.execute(ctx, { inputs, keys: buildKeys(ordinals, keys) });
|
|
1791
|
-
if (ordResult.error)
|
|
1792
|
-
result.errors.push(`Ordinals: ${ordResult.error}`);
|
|
1793
|
-
else if (ordResult.txid)
|
|
1794
|
-
result.ordinalTxids.push(ordResult.txid);
|
|
1795
|
-
} catch (e) {
|
|
1796
|
-
result.errors.push(`Ordinals: ${e instanceof Error ? e.message : String(e)}`);
|
|
1797
|
-
}
|
|
1798
|
-
}
|
|
1799
|
-
onProgress("Sweep complete");
|
|
1800
|
-
return result;
|
|
1801
|
-
}
|
|
1802
|
-
async function sweepBsv21Token(params) {
|
|
1803
|
-
const { wallet, keys, token, onProgress } = params;
|
|
1804
|
-
const ctx = createContext2(wallet, { services: getServices(), chain: "main" });
|
|
1805
|
-
onProgress(`Sweeping ${token.symbol ?? token.tokenId.slice(0, 8)}...`);
|
|
1806
|
-
try {
|
|
1807
|
-
const inputs = token.outputs.map((out) => ({
|
|
1808
|
-
outpoint: out.outpoint,
|
|
1809
|
-
tokenId: token.tokenId,
|
|
1810
|
-
amount: token.amounts.get(out.outpoint) ?? "0"
|
|
1811
|
-
}));
|
|
1812
|
-
const tokenKeys = buildKeys(token.outputs, keys);
|
|
1813
|
-
const result = await sweepBsv21.execute(ctx, { inputs, keys: tokenKeys });
|
|
1814
|
-
if (result.error)
|
|
1815
|
-
return { error: result.error };
|
|
1816
|
-
return { txid: result.txid };
|
|
1817
|
-
} catch (e) {
|
|
1818
|
-
return { error: e instanceof Error ? e.message : String(e) };
|
|
1819
|
-
}
|
|
1820
|
-
}
|
|
1821
|
-
|
|
1822
|
-
// src/lib/legacy-send.ts
|
|
1823
|
-
import { parseOutpoint } from "@1sat/utils";
|
|
1824
|
-
import { MAP_PREFIX } from "@1sat/types";
|
|
1825
|
-
import { OP, P2PKH, PrivateKey as PrivateKey2, Script, Transaction, Utils } from "@bsv/sdk";
|
|
1826
|
-
async function fetchSourceTx(txid) {
|
|
1827
|
-
const services = getServices();
|
|
1828
|
-
const beef = await services.getBeefForTxid(txid);
|
|
1829
|
-
const found = beef.findTxid(txid);
|
|
1830
|
-
if (!found?.tx)
|
|
1831
|
-
throw new Error(`Transaction ${txid} not found in BEEF`);
|
|
1832
|
-
return found.tx;
|
|
1833
|
-
}
|
|
1834
|
-
function buildKeyMap(keys) {
|
|
1835
|
-
const map = new Map;
|
|
1836
|
-
const payKey = PrivateKey2.fromWif(keys.payPk);
|
|
1837
|
-
const ordKey = PrivateKey2.fromWif(keys.ordPk);
|
|
1838
|
-
map.set(deriveAddress(keys.payPk), payKey);
|
|
1839
|
-
map.set(deriveAddress(keys.ordPk), ordKey);
|
|
1840
|
-
if (keys.identityPk) {
|
|
1841
|
-
map.set(deriveAddress(keys.identityPk), PrivateKey2.fromWif(keys.identityPk));
|
|
1842
|
-
}
|
|
1843
|
-
return map;
|
|
1844
|
-
}
|
|
1845
|
-
function keyForOutput(output, keyMap, fallback) {
|
|
1846
|
-
if (output.events) {
|
|
1847
|
-
for (const event of output.events) {
|
|
1848
|
-
if (event.startsWith("own:") || event.startsWith("p2pkh:")) {
|
|
1849
|
-
const addr = event.split(":")[1];
|
|
1850
|
-
const key = keyMap.get(addr);
|
|
1851
|
-
if (key)
|
|
1852
|
-
return key;
|
|
1853
|
-
}
|
|
1854
|
-
}
|
|
1855
|
-
}
|
|
1856
|
-
return fallback;
|
|
1857
|
-
}
|
|
1858
|
-
async function legacySendBsv(params) {
|
|
1859
|
-
const { funding, keys, destination, amount } = params;
|
|
1860
|
-
if (!funding.length)
|
|
1861
|
-
throw new Error("No funding UTXOs");
|
|
1862
|
-
if (!destination)
|
|
1863
|
-
throw new Error("No destination address");
|
|
1864
|
-
const keyMap = buildKeyMap(keys);
|
|
1865
|
-
const payKey = PrivateKey2.fromWif(keys.payPk);
|
|
1866
|
-
const sourceAddress = payKey.toPublicKey().toAddress();
|
|
1867
|
-
const p2pkh = new P2PKH;
|
|
1868
|
-
const tx = new Transaction;
|
|
1869
|
-
for (const utxo of funding) {
|
|
1870
|
-
const { txid, vout } = parseOutpoint(utxo.outpoint);
|
|
1871
|
-
const key = keyForOutput(utxo, keyMap, payKey);
|
|
1872
|
-
tx.addInput({
|
|
1873
|
-
sourceTXID: txid,
|
|
1874
|
-
sourceOutputIndex: vout,
|
|
1875
|
-
sourceTransaction: await fetchSourceTx(txid),
|
|
1876
|
-
unlockingScriptTemplate: p2pkh.unlock(key),
|
|
1877
|
-
sequence: 4294967295
|
|
1878
|
-
});
|
|
1879
|
-
}
|
|
1880
|
-
if (amount) {
|
|
1881
|
-
tx.addOutput({
|
|
1882
|
-
lockingScript: p2pkh.lock(destination),
|
|
1883
|
-
satoshis: amount
|
|
1884
|
-
});
|
|
1885
|
-
tx.addOutput({
|
|
1886
|
-
lockingScript: p2pkh.lock(sourceAddress),
|
|
1887
|
-
change: true
|
|
1888
|
-
});
|
|
1889
|
-
} else {
|
|
1890
|
-
tx.addOutput({
|
|
1891
|
-
lockingScript: p2pkh.lock(destination),
|
|
1892
|
-
change: true
|
|
1893
|
-
});
|
|
1894
|
-
}
|
|
1895
|
-
await tx.fee();
|
|
1896
|
-
await tx.sign();
|
|
1897
|
-
const rawTx = tx.toBinary();
|
|
1898
|
-
const result = await getServices().arcade.submitTransaction(rawTx);
|
|
1899
|
-
return {
|
|
1900
|
-
txid: result.txid,
|
|
1901
|
-
rawtx: Utils.toHex(rawTx)
|
|
1902
|
-
};
|
|
1903
|
-
}
|
|
1904
|
-
async function legacySendOrdinals(params) {
|
|
1905
|
-
const { ordinals, funding, keys, destination } = params;
|
|
1906
|
-
if (!ordinals.length)
|
|
1907
|
-
throw new Error("No ordinals to send");
|
|
1908
|
-
if (!funding.length)
|
|
1909
|
-
throw new Error("No funding UTXOs for fees");
|
|
1910
|
-
if (!destination)
|
|
1911
|
-
throw new Error("No destination address");
|
|
1912
|
-
const keyMap = buildKeyMap(keys);
|
|
1913
|
-
const payKey = PrivateKey2.fromWif(keys.payPk);
|
|
1914
|
-
const sourceAddress = payKey.toPublicKey().toAddress();
|
|
1915
|
-
const p2pkh = new P2PKH;
|
|
1916
|
-
const tx = new Transaction;
|
|
1917
|
-
for (const ord of ordinals) {
|
|
1918
|
-
const { txid, vout } = parseOutpoint(ord.outpoint);
|
|
1919
|
-
const key = keyForOutput(ord, keyMap, payKey);
|
|
1920
|
-
tx.addInput({
|
|
1921
|
-
sourceTXID: txid,
|
|
1922
|
-
sourceOutputIndex: vout,
|
|
1923
|
-
sourceTransaction: await fetchSourceTx(txid),
|
|
1924
|
-
unlockingScriptTemplate: p2pkh.unlock(key),
|
|
1925
|
-
sequence: 4294967295
|
|
1926
|
-
});
|
|
1927
|
-
}
|
|
1928
|
-
for (const _ord of ordinals) {
|
|
1929
|
-
tx.addOutput({
|
|
1930
|
-
lockingScript: p2pkh.lock(destination),
|
|
1931
|
-
satoshis: 1
|
|
1932
|
-
});
|
|
1933
|
-
}
|
|
1934
|
-
for (const utxo of funding) {
|
|
1935
|
-
const { txid, vout } = parseOutpoint(utxo.outpoint);
|
|
1936
|
-
const key = keyForOutput(utxo, keyMap, payKey);
|
|
1937
|
-
tx.addInput({
|
|
1938
|
-
sourceTXID: txid,
|
|
1939
|
-
sourceOutputIndex: vout,
|
|
1940
|
-
sourceTransaction: await fetchSourceTx(txid),
|
|
1941
|
-
unlockingScriptTemplate: p2pkh.unlock(key),
|
|
1942
|
-
sequence: 4294967295
|
|
1818
|
+
}),
|
|
1819
|
+
/* @__PURE__ */ jsxs5(Button, {
|
|
1820
|
+
onClick: handleScan,
|
|
1821
|
+
disabled: disabled || scanning,
|
|
1822
|
+
className: "w-full",
|
|
1823
|
+
children: [
|
|
1824
|
+
scanning ? /* @__PURE__ */ jsx10(Loader23, {
|
|
1825
|
+
className: "h-4 w-4 animate-spin mr-2"
|
|
1826
|
+
}) : /* @__PURE__ */ jsx10(Search, {
|
|
1827
|
+
className: "h-4 w-4 mr-2"
|
|
1828
|
+
}),
|
|
1829
|
+
scanning ? "Scanning..." : "Scan for Assets"
|
|
1830
|
+
]
|
|
1831
|
+
})
|
|
1832
|
+
]
|
|
1833
|
+
})
|
|
1834
|
+
]
|
|
1943
1835
|
});
|
|
1944
1836
|
}
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
}
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1837
|
+
if (mode === "choose") {
|
|
1838
|
+
return /* @__PURE__ */ jsxs5(Card, {
|
|
1839
|
+
children: [
|
|
1840
|
+
/* @__PURE__ */ jsx10(CardHeader, {
|
|
1841
|
+
children: /* @__PURE__ */ jsxs5(CardTitle, {
|
|
1842
|
+
className: "flex items-center gap-2",
|
|
1843
|
+
children: [
|
|
1844
|
+
/* @__PURE__ */ jsx10(KeyRound, {
|
|
1845
|
+
className: "h-5 w-5"
|
|
1846
|
+
}),
|
|
1847
|
+
"Legacy Keys"
|
|
1848
|
+
]
|
|
1849
|
+
})
|
|
1850
|
+
}),
|
|
1851
|
+
/* @__PURE__ */ jsxs5(CardContent, {
|
|
1852
|
+
className: "space-y-3",
|
|
1853
|
+
children: [
|
|
1854
|
+
/* @__PURE__ */ jsx10("input", {
|
|
1855
|
+
ref: fileInputRef,
|
|
1856
|
+
type: "file",
|
|
1857
|
+
accept: ".json,.bep,.txt",
|
|
1858
|
+
className: "hidden",
|
|
1859
|
+
onChange: handleFileUpload
|
|
1860
|
+
}),
|
|
1861
|
+
/* @__PURE__ */ jsxs5(Button, {
|
|
1862
|
+
variant: "outline",
|
|
1863
|
+
className: "w-full gap-2",
|
|
1864
|
+
onClick: () => fileInputRef.current?.click(),
|
|
1865
|
+
children: [
|
|
1866
|
+
/* @__PURE__ */ jsx10(Upload, {
|
|
1867
|
+
className: "h-4 w-4"
|
|
1868
|
+
}),
|
|
1869
|
+
"Import Backup File"
|
|
1870
|
+
]
|
|
1871
|
+
}),
|
|
1872
|
+
/* @__PURE__ */ jsx10(Button, {
|
|
1873
|
+
variant: "outline",
|
|
1874
|
+
className: "w-full gap-2",
|
|
1875
|
+
onClick: () => setMode("backup"),
|
|
1876
|
+
children: "Paste Backup Data"
|
|
1877
|
+
}),
|
|
1878
|
+
/* @__PURE__ */ jsx10(Button, {
|
|
1879
|
+
variant: "ghost",
|
|
1880
|
+
className: "w-full text-xs text-muted-foreground",
|
|
1881
|
+
onClick: () => setMode("wif"),
|
|
1882
|
+
children: "Enter WIF keys manually"
|
|
1883
|
+
})
|
|
1884
|
+
]
|
|
1885
|
+
})
|
|
1886
|
+
]
|
|
1976
1887
|
});
|
|
1977
1888
|
}
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1889
|
+
if (mode === "backup") {
|
|
1890
|
+
return /* @__PURE__ */ jsxs5(Card, {
|
|
1891
|
+
children: [
|
|
1892
|
+
/* @__PURE__ */ jsx10(CardHeader, {
|
|
1893
|
+
children: /* @__PURE__ */ jsxs5("div", {
|
|
1894
|
+
className: "flex items-center justify-between",
|
|
1895
|
+
children: [
|
|
1896
|
+
/* @__PURE__ */ jsxs5(CardTitle, {
|
|
1897
|
+
className: "flex items-center gap-2",
|
|
1898
|
+
children: [
|
|
1899
|
+
/* @__PURE__ */ jsx10(KeyRound, {
|
|
1900
|
+
className: "h-5 w-5"
|
|
1901
|
+
}),
|
|
1902
|
+
"Import Backup"
|
|
1903
|
+
]
|
|
1904
|
+
}),
|
|
1905
|
+
/* @__PURE__ */ jsx10(Button, {
|
|
1906
|
+
variant: "ghost",
|
|
1907
|
+
size: "sm",
|
|
1908
|
+
className: "h-6 w-6 p-0",
|
|
1909
|
+
onClick: handleReset,
|
|
1910
|
+
children: /* @__PURE__ */ jsx10(X2, {
|
|
1911
|
+
className: "h-3 w-3"
|
|
1912
|
+
})
|
|
1913
|
+
})
|
|
1914
|
+
]
|
|
1915
|
+
})
|
|
1916
|
+
}),
|
|
1917
|
+
/* @__PURE__ */ jsxs5(CardContent, {
|
|
1918
|
+
className: "space-y-3",
|
|
1919
|
+
children: [
|
|
1920
|
+
!needsPassword ? /* @__PURE__ */ jsxs5(Fragment2, {
|
|
1921
|
+
children: [
|
|
1922
|
+
/* @__PURE__ */ jsx10("textarea", {
|
|
1923
|
+
placeholder: "Paste backup JSON or encrypted data...",
|
|
1924
|
+
className: "w-full h-24 rounded-md border border-input bg-transparent px-3 py-2 text-sm font-mono resize-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
|
|
1925
|
+
onChange: (e) => processBackupText(e.target.value.trim())
|
|
1926
|
+
}),
|
|
1927
|
+
/* @__PURE__ */ jsx10("input", {
|
|
1928
|
+
ref: fileInputRef,
|
|
1929
|
+
type: "file",
|
|
1930
|
+
accept: ".json,.bep,.txt",
|
|
1931
|
+
className: "hidden",
|
|
1932
|
+
onChange: handleFileUpload
|
|
1933
|
+
}),
|
|
1934
|
+
/* @__PURE__ */ jsxs5(Button, {
|
|
1935
|
+
variant: "outline",
|
|
1936
|
+
size: "sm",
|
|
1937
|
+
className: "w-full gap-2",
|
|
1938
|
+
onClick: () => fileInputRef.current?.click(),
|
|
1939
|
+
children: [
|
|
1940
|
+
/* @__PURE__ */ jsx10(Upload, {
|
|
1941
|
+
className: "h-4 w-4"
|
|
1942
|
+
}),
|
|
1943
|
+
"Or upload a file"
|
|
1944
|
+
]
|
|
1945
|
+
})
|
|
1946
|
+
]
|
|
1947
|
+
}) : /* @__PURE__ */ jsxs5(Fragment2, {
|
|
1948
|
+
children: [
|
|
1949
|
+
/* @__PURE__ */ jsx10("p", {
|
|
1950
|
+
className: "text-sm text-muted-foreground",
|
|
1951
|
+
children: "This backup is encrypted. Enter the password to decrypt."
|
|
1952
|
+
}),
|
|
1953
|
+
/* @__PURE__ */ jsx10(Input, {
|
|
1954
|
+
type: "password",
|
|
1955
|
+
placeholder: "Backup password...",
|
|
1956
|
+
value: password,
|
|
1957
|
+
onChange: (e) => setPassword(e.target.value),
|
|
1958
|
+
onKeyDown: (e) => {
|
|
1959
|
+
if (e.key === "Enter")
|
|
1960
|
+
handleDecrypt();
|
|
1961
|
+
}
|
|
1962
|
+
}),
|
|
1963
|
+
/* @__PURE__ */ jsxs5(Button, {
|
|
1964
|
+
onClick: handleDecrypt,
|
|
1965
|
+
disabled: !password || decrypting,
|
|
1966
|
+
className: "w-full",
|
|
1967
|
+
children: [
|
|
1968
|
+
decrypting ? /* @__PURE__ */ jsx10(Loader23, {
|
|
1969
|
+
className: "h-4 w-4 animate-spin mr-2"
|
|
1970
|
+
}) : null,
|
|
1971
|
+
decrypting ? "Decrypting..." : "Decrypt"
|
|
1972
|
+
]
|
|
1973
|
+
})
|
|
1974
|
+
]
|
|
1975
|
+
}),
|
|
1976
|
+
error && /* @__PURE__ */ jsx10("p", {
|
|
1977
|
+
className: "text-sm text-destructive",
|
|
1978
|
+
children: error
|
|
1979
|
+
})
|
|
1980
|
+
]
|
|
1981
|
+
})
|
|
1982
|
+
]
|
|
1987
1983
|
});
|
|
1988
1984
|
}
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1985
|
+
return /* @__PURE__ */ jsxs5(Card, {
|
|
1986
|
+
children: [
|
|
1987
|
+
/* @__PURE__ */ jsx10(CardHeader, {
|
|
1988
|
+
children: /* @__PURE__ */ jsxs5("div", {
|
|
1989
|
+
className: "flex items-center justify-between",
|
|
1990
|
+
children: [
|
|
1991
|
+
/* @__PURE__ */ jsxs5(CardTitle, {
|
|
1992
|
+
className: "flex items-center gap-2",
|
|
1993
|
+
children: [
|
|
1994
|
+
/* @__PURE__ */ jsx10(KeyRound, {
|
|
1995
|
+
className: "h-5 w-5"
|
|
1996
|
+
}),
|
|
1997
|
+
"Manual WIF Entry"
|
|
1998
|
+
]
|
|
1999
|
+
}),
|
|
2000
|
+
/* @__PURE__ */ jsx10(Button, {
|
|
2001
|
+
variant: "ghost",
|
|
2002
|
+
size: "sm",
|
|
2003
|
+
className: "h-6 w-6 p-0",
|
|
2004
|
+
onClick: handleReset,
|
|
2005
|
+
children: /* @__PURE__ */ jsx10(X2, {
|
|
2006
|
+
className: "h-3 w-3"
|
|
2007
|
+
})
|
|
2008
|
+
})
|
|
2009
|
+
]
|
|
2010
|
+
})
|
|
2011
|
+
}),
|
|
2012
|
+
/* @__PURE__ */ jsxs5(CardContent, {
|
|
2013
|
+
className: "space-y-4",
|
|
2014
|
+
children: [
|
|
2015
|
+
/* @__PURE__ */ jsxs5("div", {
|
|
2016
|
+
className: "space-y-2",
|
|
2017
|
+
children: [
|
|
2018
|
+
/* @__PURE__ */ jsx10("label", {
|
|
2019
|
+
className: "text-sm font-medium",
|
|
2020
|
+
children: sameKey ? "Private Key (WIF)" : "Pay Key (WIF)"
|
|
2021
|
+
}),
|
|
2022
|
+
/* @__PURE__ */ jsx10(Input, {
|
|
2023
|
+
type: "password",
|
|
2024
|
+
placeholder: "Enter WIF private key...",
|
|
2025
|
+
value: payWif,
|
|
2026
|
+
onChange: (e) => setPayWif(e.target.value),
|
|
2027
|
+
disabled: disabled || scanning
|
|
2028
|
+
})
|
|
2029
|
+
]
|
|
2030
|
+
}),
|
|
2031
|
+
/* @__PURE__ */ jsxs5("label", {
|
|
2032
|
+
className: "flex items-center gap-2 text-sm",
|
|
2033
|
+
children: [
|
|
2034
|
+
/* @__PURE__ */ jsx10("input", {
|
|
2035
|
+
type: "checkbox",
|
|
2036
|
+
checked: sameKey,
|
|
2037
|
+
onChange: (e) => setSameKey(e.target.checked),
|
|
2038
|
+
disabled: disabled || scanning
|
|
2039
|
+
}),
|
|
2040
|
+
"Same key for pay and ordinals"
|
|
2041
|
+
]
|
|
2042
|
+
}),
|
|
2043
|
+
!sameKey && /* @__PURE__ */ jsxs5("div", {
|
|
2044
|
+
className: "space-y-2",
|
|
2045
|
+
children: [
|
|
2046
|
+
/* @__PURE__ */ jsx10("label", {
|
|
2047
|
+
className: "text-sm font-medium",
|
|
2048
|
+
children: "Ordinals Key (WIF)"
|
|
2049
|
+
}),
|
|
2050
|
+
/* @__PURE__ */ jsx10(Input, {
|
|
2051
|
+
type: "password",
|
|
2052
|
+
placeholder: "Enter ordinals WIF...",
|
|
2053
|
+
value: ordWif,
|
|
2054
|
+
onChange: (e) => setOrdWif(e.target.value),
|
|
2055
|
+
disabled: disabled || scanning
|
|
2056
|
+
})
|
|
2057
|
+
]
|
|
2058
|
+
}),
|
|
2059
|
+
/* @__PURE__ */ jsxs5(Button, {
|
|
2060
|
+
onClick: handleScan,
|
|
2061
|
+
disabled: disabled || scanning || !payWif.trim(),
|
|
2062
|
+
className: "w-full",
|
|
2063
|
+
children: [
|
|
2064
|
+
scanning ? /* @__PURE__ */ jsx10(Loader23, {
|
|
2065
|
+
className: "h-4 w-4 animate-spin mr-2"
|
|
2066
|
+
}) : /* @__PURE__ */ jsx10(Search, {
|
|
2067
|
+
className: "h-4 w-4 mr-2"
|
|
2068
|
+
}),
|
|
2069
|
+
scanning ? "Scanning..." : "Scan for Assets"
|
|
2070
|
+
]
|
|
2071
|
+
})
|
|
2072
|
+
]
|
|
2073
|
+
})
|
|
2074
|
+
]
|
|
1994
2075
|
});
|
|
1995
|
-
await tx.fee();
|
|
1996
|
-
await tx.sign();
|
|
1997
|
-
const rawTx = tx.toBinary();
|
|
1998
|
-
const result = await getServices().arcade.submitTransaction(rawTx);
|
|
1999
|
-
return {
|
|
2000
|
-
txid: result.txid,
|
|
2001
|
-
rawtx: Utils.toHex(rawTx)
|
|
2002
|
-
};
|
|
2003
2076
|
}
|
|
2004
2077
|
|
|
2005
2078
|
// src/components/SweepApp.tsx
|
|
2006
|
-
import { PrivateKey as PrivateKey3 } from "@bsv/sdk";
|
|
2007
2079
|
import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2008
|
-
function SweepApp({
|
|
2080
|
+
function SweepApp({
|
|
2081
|
+
legacyKeys: initialKeys,
|
|
2082
|
+
wallet: externalWallet,
|
|
2083
|
+
sweepOnly
|
|
2084
|
+
}) {
|
|
2009
2085
|
const [walletConnected, setWalletConnected] = useState5(!!externalWallet);
|
|
2010
2086
|
const [scanning, setScanning] = useState5(false);
|
|
2011
2087
|
const [scanProgress, setScanProgress] = useState5("");
|
|
@@ -2036,14 +2112,21 @@ function SweepApp({ legacyKeys: initialKeys, wallet: externalWallet, sweepOnly }
|
|
|
2036
2112
|
return map;
|
|
2037
2113
|
}, [legacyKeys]);
|
|
2038
2114
|
const addTx = useCallback2((label, txid, error) => {
|
|
2039
|
-
setTxHistory((prev) => [
|
|
2115
|
+
setTxHistory((prev) => [
|
|
2116
|
+
...prev,
|
|
2117
|
+
{ label, txid, timestamp: new Date, error }
|
|
2118
|
+
]);
|
|
2040
2119
|
}, []);
|
|
2041
2120
|
const tabs = useMemo(() => {
|
|
2042
2121
|
if (!assets)
|
|
2043
2122
|
return [];
|
|
2044
2123
|
const t = [];
|
|
2045
2124
|
if (assets.ordinals.length > 0)
|
|
2046
|
-
t.push({
|
|
2125
|
+
t.push({
|
|
2126
|
+
id: "ordinals",
|
|
2127
|
+
label: "Ordinals",
|
|
2128
|
+
count: assets.ordinals.length
|
|
2129
|
+
});
|
|
2047
2130
|
if (assets.opnsNames.length > 0)
|
|
2048
2131
|
t.push({ id: "opns", label: "OpNS", count: assets.opnsNames.length });
|
|
2049
2132
|
if (assets.bsv21Tokens.length > 0)
|
|
@@ -2089,7 +2172,13 @@ function SweepApp({ legacyKeys: initialKeys, wallet: externalWallet, sweepOnly }
|
|
|
2089
2172
|
const refreshAssets = useCallback2(async () => {
|
|
2090
2173
|
if (!legacyKeys)
|
|
2091
2174
|
return;
|
|
2092
|
-
const addresses = [
|
|
2175
|
+
const addresses = [
|
|
2176
|
+
...new Set([
|
|
2177
|
+
deriveAddress(legacyKeys.payPk),
|
|
2178
|
+
deriveAddress(legacyKeys.ordPk),
|
|
2179
|
+
...legacyKeys.identityPk ? [deriveAddress(legacyKeys.identityPk)] : []
|
|
2180
|
+
])
|
|
2181
|
+
];
|
|
2093
2182
|
const result = await scanAddresses(addresses);
|
|
2094
2183
|
setAssets(result);
|
|
2095
2184
|
setSelectedOrdinals(new Set);
|
|
@@ -2104,7 +2193,13 @@ function SweepApp({ legacyKeys: initialKeys, wallet: externalWallet, sweepOnly }
|
|
|
2104
2193
|
setSweepAmount(null);
|
|
2105
2194
|
setLegacyKeys(keys);
|
|
2106
2195
|
try {
|
|
2107
|
-
const addresses = [
|
|
2196
|
+
const addresses = [
|
|
2197
|
+
...new Set([
|
|
2198
|
+
deriveAddress(keys.payPk),
|
|
2199
|
+
deriveAddress(keys.ordPk),
|
|
2200
|
+
...keys.identityPk ? [deriveAddress(keys.identityPk)] : []
|
|
2201
|
+
])
|
|
2202
|
+
];
|
|
2108
2203
|
const result = await scanAddresses(addresses, (p) => setScanProgress(p.detail ?? p.phase));
|
|
2109
2204
|
setAssets(result);
|
|
2110
2205
|
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;
|
|
@@ -2167,17 +2262,36 @@ function SweepApp({ legacyKeys: initialKeys, wallet: externalWallet, sweepOnly }
|
|
|
2167
2262
|
if (!wallet || !legacyKeys || !assets)
|
|
2168
2263
|
return;
|
|
2169
2264
|
await runOperation("Sweep BSV", async () => {
|
|
2170
|
-
const result = await executeSweep({
|
|
2265
|
+
const result = await executeSweep({
|
|
2266
|
+
wallet,
|
|
2267
|
+
keys: keyMap,
|
|
2268
|
+
funding: getSelectedFunding(),
|
|
2269
|
+
ordinals: [],
|
|
2270
|
+
amount: sweepAmount ?? undefined,
|
|
2271
|
+
onProgress: setSweepProgress
|
|
2272
|
+
});
|
|
2171
2273
|
if (result.errors.length > 0)
|
|
2172
2274
|
throw new Error(result.errors[0]);
|
|
2173
2275
|
return result.bsvTxid ?? "";
|
|
2174
2276
|
});
|
|
2175
|
-
}, [
|
|
2277
|
+
}, [
|
|
2278
|
+
resolveWallet,
|
|
2279
|
+
legacyKeys,
|
|
2280
|
+
assets,
|
|
2281
|
+
sweepAmount,
|
|
2282
|
+
getSelectedFunding,
|
|
2283
|
+
runOperation
|
|
2284
|
+
]);
|
|
2176
2285
|
const handleSendBsv = useCallback2(async (destination) => {
|
|
2177
2286
|
if (!legacyKeys || !assets)
|
|
2178
2287
|
return;
|
|
2179
2288
|
await runOperation("Send BSV", async () => {
|
|
2180
|
-
const result = await legacySendBsv({
|
|
2289
|
+
const result = await legacySendBsv({
|
|
2290
|
+
funding: getSelectedFunding(),
|
|
2291
|
+
keys: legacyKeys,
|
|
2292
|
+
destination,
|
|
2293
|
+
amount: sweepAmount ?? undefined
|
|
2294
|
+
});
|
|
2181
2295
|
return result.txid;
|
|
2182
2296
|
});
|
|
2183
2297
|
}, [legacyKeys, assets, sweepAmount, getSelectedFunding, runOperation]);
|
|
@@ -2189,7 +2303,13 @@ function SweepApp({ legacyKeys: initialKeys, wallet: externalWallet, sweepOnly }
|
|
|
2189
2303
|
if (selected.length === 0)
|
|
2190
2304
|
return;
|
|
2191
2305
|
await runOperation(`Sweep ${selected.length} ordinal${selected.length !== 1 ? "s" : ""}`, async () => {
|
|
2192
|
-
const result = await executeSweep({
|
|
2306
|
+
const result = await executeSweep({
|
|
2307
|
+
wallet,
|
|
2308
|
+
keys: keyMap,
|
|
2309
|
+
funding: [],
|
|
2310
|
+
ordinals: selected,
|
|
2311
|
+
onProgress: setSweepProgress
|
|
2312
|
+
});
|
|
2193
2313
|
if (result.errors.length > 0)
|
|
2194
2314
|
throw new Error(result.errors[0]);
|
|
2195
2315
|
return result.ordinalTxids[0] ?? "";
|
|
@@ -2202,7 +2322,12 @@ function SweepApp({ legacyKeys: initialKeys, wallet: externalWallet, sweepOnly }
|
|
|
2202
2322
|
if (selected.length === 0)
|
|
2203
2323
|
return;
|
|
2204
2324
|
await runOperation(`Send ${selected.length} ordinal${selected.length !== 1 ? "s" : ""}`, async () => {
|
|
2205
|
-
const result = await legacySendOrdinals({
|
|
2325
|
+
const result = await legacySendOrdinals({
|
|
2326
|
+
ordinals: selected,
|
|
2327
|
+
funding: assets.funding,
|
|
2328
|
+
keys: legacyKeys,
|
|
2329
|
+
destination
|
|
2330
|
+
});
|
|
2206
2331
|
return result.txid;
|
|
2207
2332
|
});
|
|
2208
2333
|
}, [legacyKeys, assets, selectedOrdinals, runOperation]);
|
|
@@ -2213,7 +2338,11 @@ function SweepApp({ legacyKeys: initialKeys, wallet: externalWallet, sweepOnly }
|
|
|
2213
2338
|
if (selected.length === 0)
|
|
2214
2339
|
return;
|
|
2215
2340
|
await runOperation(`Burn ${selected.length} ordinal${selected.length !== 1 ? "s" : ""}`, async () => {
|
|
2216
|
-
const result = await legacyBurnOrdinals({
|
|
2341
|
+
const result = await legacyBurnOrdinals({
|
|
2342
|
+
ordinals: selected,
|
|
2343
|
+
funding: assets.funding,
|
|
2344
|
+
keys: legacyKeys
|
|
2345
|
+
});
|
|
2217
2346
|
return result.txid;
|
|
2218
2347
|
});
|
|
2219
2348
|
}, [legacyKeys, assets, selectedOrdinals, runOperation]);
|
|
@@ -2225,7 +2354,13 @@ function SweepApp({ legacyKeys: initialKeys, wallet: externalWallet, sweepOnly }
|
|
|
2225
2354
|
if (selected.length === 0)
|
|
2226
2355
|
return;
|
|
2227
2356
|
await runOperation(`Sweep ${selected.length} domain${selected.length !== 1 ? "s" : ""}`, async () => {
|
|
2228
|
-
const result = await executeSweep({
|
|
2357
|
+
const result = await executeSweep({
|
|
2358
|
+
wallet,
|
|
2359
|
+
keys: keyMap,
|
|
2360
|
+
funding: [],
|
|
2361
|
+
ordinals: selected,
|
|
2362
|
+
onProgress: setSweepProgress
|
|
2363
|
+
});
|
|
2229
2364
|
if (result.errors.length > 0)
|
|
2230
2365
|
throw new Error(result.errors[0]);
|
|
2231
2366
|
return result.ordinalTxids[0] ?? "";
|
|
@@ -2238,7 +2373,12 @@ function SweepApp({ legacyKeys: initialKeys, wallet: externalWallet, sweepOnly }
|
|
|
2238
2373
|
if (selected.length === 0)
|
|
2239
2374
|
return;
|
|
2240
2375
|
await runOperation(`Send ${selected.length} domain${selected.length !== 1 ? "s" : ""}`, async () => {
|
|
2241
|
-
const result = await legacySendOrdinals({
|
|
2376
|
+
const result = await legacySendOrdinals({
|
|
2377
|
+
ordinals: selected,
|
|
2378
|
+
funding: assets.funding,
|
|
2379
|
+
keys: legacyKeys,
|
|
2380
|
+
destination
|
|
2381
|
+
});
|
|
2242
2382
|
return result.txid;
|
|
2243
2383
|
});
|
|
2244
2384
|
}, [legacyKeys, assets, selectedOpns, runOperation]);
|
|
@@ -2249,7 +2389,11 @@ function SweepApp({ legacyKeys: initialKeys, wallet: externalWallet, sweepOnly }
|
|
|
2249
2389
|
if (selected.length === 0)
|
|
2250
2390
|
return;
|
|
2251
2391
|
await runOperation(`Burn ${selected.length} domain${selected.length !== 1 ? "s" : ""}`, async () => {
|
|
2252
|
-
const result = await legacyBurnOrdinals({
|
|
2392
|
+
const result = await legacyBurnOrdinals({
|
|
2393
|
+
ordinals: selected,
|
|
2394
|
+
funding: assets.funding,
|
|
2395
|
+
keys: legacyKeys
|
|
2396
|
+
});
|
|
2253
2397
|
return result.txid;
|
|
2254
2398
|
});
|
|
2255
2399
|
}, [legacyKeys, assets, selectedOpns, runOperation]);
|
|
@@ -2261,7 +2405,12 @@ function SweepApp({ legacyKeys: initialKeys, wallet: externalWallet, sweepOnly }
|
|
|
2261
2405
|
if (!token)
|
|
2262
2406
|
return;
|
|
2263
2407
|
await runOperation(`Sweep ${token.symbol ?? tokenId.slice(0, 8)}`, async () => {
|
|
2264
|
-
const result = await sweepBsv21Token({
|
|
2408
|
+
const result = await sweepBsv21Token({
|
|
2409
|
+
wallet,
|
|
2410
|
+
keys: keyMap,
|
|
2411
|
+
token,
|
|
2412
|
+
onProgress: setSweepProgress
|
|
2413
|
+
});
|
|
2265
2414
|
if (result.error)
|
|
2266
2415
|
throw new Error(result.error);
|
|
2267
2416
|
return result.txid ?? "";
|
|
@@ -2403,7 +2552,12 @@ function SweepApp({ legacyKeys: initialKeys, wallet: externalWallet, sweepOnly }
|
|
|
2403
2552
|
});
|
|
2404
2553
|
}
|
|
2405
2554
|
// src/components/sweep-progress.tsx
|
|
2406
|
-
import {
|
|
2555
|
+
import {
|
|
2556
|
+
AlertTriangle,
|
|
2557
|
+
CheckCircle2,
|
|
2558
|
+
ExternalLink as ExternalLink2,
|
|
2559
|
+
Loader2 as Loader24
|
|
2560
|
+
} from "lucide-react";
|
|
2407
2561
|
import { jsx as jsx12, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2408
2562
|
var EXPLORER_BASE2 = "https://bananablocks.com/tx/";
|
|
2409
2563
|
function TxLink({ label, txid }) {
|