@siphoyawe/mina-cli 1.0.0
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/index.d.ts +1 -0
- package/dist/index.js +3229 -0
- package/dist/index.js.map +1 -0
- package/package.json +41 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,3229 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.tsx
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import React11 from "react";
|
|
7
|
+
import { render } from "ink";
|
|
8
|
+
|
|
9
|
+
// src/commands/wizard.tsx
|
|
10
|
+
import { useState, useEffect, useCallback } from "react";
|
|
11
|
+
import { Box as Box7, Text as Text7, useApp, useInput } from "ink";
|
|
12
|
+
import {
|
|
13
|
+
getChains,
|
|
14
|
+
getBridgeableTokens,
|
|
15
|
+
HYPEREVM_CHAIN_ID
|
|
16
|
+
} from "@siphoyawe/mina-sdk";
|
|
17
|
+
|
|
18
|
+
// src/ui/theme.ts
|
|
19
|
+
var theme = {
|
|
20
|
+
// Primary colors
|
|
21
|
+
primary: "#7DD3FC",
|
|
22
|
+
// Sky blue - main accent
|
|
23
|
+
secondary: "#A1A1AA",
|
|
24
|
+
// Gray - supporting text
|
|
25
|
+
muted: "#71717A",
|
|
26
|
+
// Dark gray - disabled/placeholder
|
|
27
|
+
// Status colors
|
|
28
|
+
success: "#0ECC83",
|
|
29
|
+
// Green - completed/success
|
|
30
|
+
error: "#F87171",
|
|
31
|
+
// Red - error/failed
|
|
32
|
+
warning: "#FBBF24",
|
|
33
|
+
// Yellow - warning/pending
|
|
34
|
+
// UI elements
|
|
35
|
+
border: "#3F3F46",
|
|
36
|
+
// Border gray
|
|
37
|
+
background: "#18181B",
|
|
38
|
+
// Dark background
|
|
39
|
+
// Additional colors for variety
|
|
40
|
+
accent: "#E879F9",
|
|
41
|
+
// Purple accent
|
|
42
|
+
info: "#38BDF8"
|
|
43
|
+
// Light blue info
|
|
44
|
+
};
|
|
45
|
+
var borders = {
|
|
46
|
+
// Single line box
|
|
47
|
+
topLeft: "\u250C",
|
|
48
|
+
topRight: "\u2510",
|
|
49
|
+
bottomLeft: "\u2514",
|
|
50
|
+
bottomRight: "\u2518",
|
|
51
|
+
horizontal: "\u2500",
|
|
52
|
+
vertical: "\u2502",
|
|
53
|
+
// Junctions
|
|
54
|
+
leftT: "\u251C",
|
|
55
|
+
rightT: "\u2524",
|
|
56
|
+
topT: "\u252C",
|
|
57
|
+
bottomT: "\u2534",
|
|
58
|
+
cross: "\u253C",
|
|
59
|
+
// Double line (for emphasis)
|
|
60
|
+
doubleHorizontal: "\u2550",
|
|
61
|
+
doubleVertical: "\u2551",
|
|
62
|
+
doubleTopLeft: "\u2554",
|
|
63
|
+
doubleTopRight: "\u2557",
|
|
64
|
+
doubleBottomLeft: "\u255A",
|
|
65
|
+
doubleBottomRight: "\u255D"
|
|
66
|
+
};
|
|
67
|
+
var symbols = {
|
|
68
|
+
pending: "\u25CB",
|
|
69
|
+
active: "\u2192",
|
|
70
|
+
completed: "\u2713",
|
|
71
|
+
failed: "\u2717",
|
|
72
|
+
spinner: "\u25D0",
|
|
73
|
+
bullet: "\u2022",
|
|
74
|
+
arrow: "\u203A",
|
|
75
|
+
check: "\u2714",
|
|
76
|
+
cross: "\u2718"
|
|
77
|
+
};
|
|
78
|
+
var MINA_LOGO = `
|
|
79
|
+
__ __ ___ _ _ _
|
|
80
|
+
| \\/ |_ _| \\ | | / \\
|
|
81
|
+
| |\\/| || || \\| | / _ \\
|
|
82
|
+
| | | || || |\\ |/ ___ \\
|
|
83
|
+
|_| |_|___|_| \\_/_/ \\_\\
|
|
84
|
+
`;
|
|
85
|
+
var TAGLINE = "Cross-chain bridge to Hyperliquid";
|
|
86
|
+
|
|
87
|
+
// src/ui/Box.tsx
|
|
88
|
+
import { Box as InkBox, Text } from "ink";
|
|
89
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
90
|
+
function Box({
|
|
91
|
+
title,
|
|
92
|
+
borderColor = theme.border,
|
|
93
|
+
bordered = true,
|
|
94
|
+
padding = 1,
|
|
95
|
+
children,
|
|
96
|
+
...props
|
|
97
|
+
}) {
|
|
98
|
+
if (!bordered) {
|
|
99
|
+
return /* @__PURE__ */ jsx(InkBox, { padding, ...props, children });
|
|
100
|
+
}
|
|
101
|
+
return /* @__PURE__ */ jsxs(
|
|
102
|
+
InkBox,
|
|
103
|
+
{
|
|
104
|
+
flexDirection: "column",
|
|
105
|
+
borderStyle: "round",
|
|
106
|
+
borderColor,
|
|
107
|
+
paddingX: padding,
|
|
108
|
+
paddingY: padding > 0 ? 1 : 0,
|
|
109
|
+
...props,
|
|
110
|
+
children: [
|
|
111
|
+
title && /* @__PURE__ */ jsx(InkBox, { marginBottom: 1, children: /* @__PURE__ */ jsx(Text, { color: theme.primary, bold: true, children: title }) }),
|
|
112
|
+
children
|
|
113
|
+
]
|
|
114
|
+
}
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
function Divider({
|
|
118
|
+
width = 40,
|
|
119
|
+
color = theme.border,
|
|
120
|
+
char = borders.horizontal
|
|
121
|
+
}) {
|
|
122
|
+
return /* @__PURE__ */ jsx(Text, { color, children: char.repeat(width) });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// src/ui/Header.tsx
|
|
126
|
+
import { Box as Box2, Text as Text2 } from "ink";
|
|
127
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
128
|
+
function Header({
|
|
129
|
+
showTagline = true,
|
|
130
|
+
tagline = TAGLINE,
|
|
131
|
+
compact = false
|
|
132
|
+
}) {
|
|
133
|
+
if (compact) {
|
|
134
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginBottom: 1, children: [
|
|
135
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.primary, bold: true, children: "MINA" }),
|
|
136
|
+
showTagline && /* @__PURE__ */ jsx2(Text2, { color: theme.secondary, dimColor: true, children: tagline })
|
|
137
|
+
] });
|
|
138
|
+
}
|
|
139
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginBottom: 1, children: [
|
|
140
|
+
/* @__PURE__ */ jsx2(Text2, { color: theme.primary, bold: true, children: MINA_LOGO }),
|
|
141
|
+
showTagline && /* @__PURE__ */ jsx2(Box2, { justifyContent: "center", children: /* @__PURE__ */ jsx2(Text2, { color: theme.secondary, dimColor: true, children: tagline }) })
|
|
142
|
+
] });
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// src/ui/Select.tsx
|
|
146
|
+
import { Box as Box3, Text as Text3 } from "ink";
|
|
147
|
+
import InkSelectInput from "ink-select-input";
|
|
148
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
149
|
+
function Select({
|
|
150
|
+
items,
|
|
151
|
+
onSelect,
|
|
152
|
+
onHighlight,
|
|
153
|
+
initialIndex = 0,
|
|
154
|
+
disabled = false,
|
|
155
|
+
label,
|
|
156
|
+
limit
|
|
157
|
+
}) {
|
|
158
|
+
const inkItems = items.map((item) => ({
|
|
159
|
+
label: item.label,
|
|
160
|
+
value: item
|
|
161
|
+
}));
|
|
162
|
+
const handleSelect = (item) => {
|
|
163
|
+
if (!item.value.disabled) {
|
|
164
|
+
onSelect(item.value);
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
const handleHighlight = (item) => {
|
|
168
|
+
onHighlight?.(item.value);
|
|
169
|
+
};
|
|
170
|
+
const itemComponent = ({ isSelected, label: label2 }) => {
|
|
171
|
+
const item = items.find((i) => i.label === label2);
|
|
172
|
+
const isDisabled = item?.disabled;
|
|
173
|
+
return /* @__PURE__ */ jsxs3(Box3, { children: [
|
|
174
|
+
/* @__PURE__ */ jsxs3(Text3, { color: isSelected ? theme.primary : isDisabled ? theme.muted : theme.secondary, children: [
|
|
175
|
+
isSelected ? symbols.arrow : " ",
|
|
176
|
+
" ",
|
|
177
|
+
label2
|
|
178
|
+
] }),
|
|
179
|
+
item?.description && /* @__PURE__ */ jsxs3(Text3, { color: theme.muted, dimColor: true, children: [
|
|
180
|
+
" ",
|
|
181
|
+
"\u2014 ",
|
|
182
|
+
item.description
|
|
183
|
+
] })
|
|
184
|
+
] });
|
|
185
|
+
};
|
|
186
|
+
const indicatorComponent = ({ isSelected }) => /* @__PURE__ */ jsx3(Text3, { color: theme.primary, children: isSelected ? symbols.arrow : " " });
|
|
187
|
+
if (disabled) {
|
|
188
|
+
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
189
|
+
label && /* @__PURE__ */ jsx3(Text3, { color: theme.muted, dimColor: true, children: label }),
|
|
190
|
+
/* @__PURE__ */ jsx3(Text3, { color: theme.muted, dimColor: true, children: "(disabled)" })
|
|
191
|
+
] });
|
|
192
|
+
}
|
|
193
|
+
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", children: [
|
|
194
|
+
label && /* @__PURE__ */ jsx3(Box3, { marginBottom: 1, children: /* @__PURE__ */ jsx3(Text3, { color: theme.secondary, children: label }) }),
|
|
195
|
+
/* @__PURE__ */ jsx3(
|
|
196
|
+
InkSelectInput,
|
|
197
|
+
{
|
|
198
|
+
items: inkItems,
|
|
199
|
+
onSelect: handleSelect,
|
|
200
|
+
onHighlight: handleHighlight,
|
|
201
|
+
initialIndex,
|
|
202
|
+
itemComponent,
|
|
203
|
+
indicatorComponent,
|
|
204
|
+
limit
|
|
205
|
+
}
|
|
206
|
+
)
|
|
207
|
+
] });
|
|
208
|
+
}
|
|
209
|
+
function ChainSelect({
|
|
210
|
+
chains,
|
|
211
|
+
onSelect,
|
|
212
|
+
label = "Select Chain:"
|
|
213
|
+
}) {
|
|
214
|
+
return /* @__PURE__ */ jsx3(
|
|
215
|
+
Select,
|
|
216
|
+
{
|
|
217
|
+
items: chains,
|
|
218
|
+
onSelect: (item) => onSelect(item),
|
|
219
|
+
label
|
|
220
|
+
}
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
function TokenSelect({
|
|
224
|
+
tokens,
|
|
225
|
+
onSelect,
|
|
226
|
+
label = "Select Token:"
|
|
227
|
+
}) {
|
|
228
|
+
return /* @__PURE__ */ jsx3(
|
|
229
|
+
Select,
|
|
230
|
+
{
|
|
231
|
+
items: tokens,
|
|
232
|
+
onSelect: (item) => onSelect(item),
|
|
233
|
+
label
|
|
234
|
+
}
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// src/ui/ProgressSteps.tsx
|
|
239
|
+
import { Box as Box4, Text as Text4 } from "ink";
|
|
240
|
+
import InkSpinner from "ink-spinner";
|
|
241
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
242
|
+
function getStatusIndicator(status) {
|
|
243
|
+
switch (status) {
|
|
244
|
+
case "pending":
|
|
245
|
+
return /* @__PURE__ */ jsx4(Text4, { color: theme.muted, children: symbols.pending });
|
|
246
|
+
case "active":
|
|
247
|
+
return /* @__PURE__ */ jsx4(Text4, { color: theme.primary, children: /* @__PURE__ */ jsx4(InkSpinner, { type: "dots" }) });
|
|
248
|
+
case "completed":
|
|
249
|
+
return /* @__PURE__ */ jsx4(Text4, { color: theme.success, children: symbols.completed });
|
|
250
|
+
case "failed":
|
|
251
|
+
return /* @__PURE__ */ jsx4(Text4, { color: theme.error, children: symbols.failed });
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
function getStatusColor(status) {
|
|
255
|
+
switch (status) {
|
|
256
|
+
case "pending":
|
|
257
|
+
return theme.muted;
|
|
258
|
+
case "active":
|
|
259
|
+
return theme.primary;
|
|
260
|
+
case "completed":
|
|
261
|
+
return theme.success;
|
|
262
|
+
case "failed":
|
|
263
|
+
return theme.error;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function StepItem({
|
|
267
|
+
step,
|
|
268
|
+
index,
|
|
269
|
+
showNumber,
|
|
270
|
+
showTimestamp,
|
|
271
|
+
isLast
|
|
272
|
+
}) {
|
|
273
|
+
const color = getStatusColor(step.status);
|
|
274
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
275
|
+
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
276
|
+
/* @__PURE__ */ jsx4(Box4, { width: 3, children: getStatusIndicator(step.status) }),
|
|
277
|
+
showNumber && /* @__PURE__ */ jsx4(Box4, { width: 4, children: /* @__PURE__ */ jsxs4(Text4, { color, dimColor: step.status === "pending", children: [
|
|
278
|
+
index + 1,
|
|
279
|
+
"."
|
|
280
|
+
] }) }),
|
|
281
|
+
/* @__PURE__ */ jsx4(Box4, { flexGrow: 1, children: /* @__PURE__ */ jsx4(
|
|
282
|
+
Text4,
|
|
283
|
+
{
|
|
284
|
+
color,
|
|
285
|
+
bold: step.status === "active",
|
|
286
|
+
dimColor: step.status === "pending",
|
|
287
|
+
children: step.label
|
|
288
|
+
}
|
|
289
|
+
) }),
|
|
290
|
+
showTimestamp && step.timestamp && /* @__PURE__ */ jsx4(Box4, { marginLeft: 2, children: /* @__PURE__ */ jsx4(Text4, { color: theme.muted, dimColor: true, children: step.timestamp }) })
|
|
291
|
+
] }),
|
|
292
|
+
step.description && /* @__PURE__ */ jsx4(Box4, { marginLeft: showNumber ? 7 : 3, children: /* @__PURE__ */ jsx4(Text4, { color: theme.muted, dimColor: true, children: step.description }) }),
|
|
293
|
+
!isLast && step.status !== "pending" && /* @__PURE__ */ jsx4(Box4, { marginLeft: 1, children: /* @__PURE__ */ jsx4(Text4, { color: theme.border, children: "\u2502" }) })
|
|
294
|
+
] });
|
|
295
|
+
}
|
|
296
|
+
function ProgressSteps({
|
|
297
|
+
steps,
|
|
298
|
+
showNumbers = true,
|
|
299
|
+
showTimestamps = false,
|
|
300
|
+
title
|
|
301
|
+
}) {
|
|
302
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
303
|
+
title && /* @__PURE__ */ jsx4(Box4, { marginBottom: 1, children: /* @__PURE__ */ jsx4(Text4, { color: theme.secondary, bold: true, children: title }) }),
|
|
304
|
+
steps.map((step, index) => /* @__PURE__ */ jsx4(
|
|
305
|
+
StepItem,
|
|
306
|
+
{
|
|
307
|
+
step,
|
|
308
|
+
index,
|
|
309
|
+
showNumber: showNumbers,
|
|
310
|
+
showTimestamp: showTimestamps,
|
|
311
|
+
isLast: index === steps.length - 1
|
|
312
|
+
},
|
|
313
|
+
index
|
|
314
|
+
))
|
|
315
|
+
] });
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// src/ui/Spinner.tsx
|
|
319
|
+
import { Box as Box5, Text as Text5 } from "ink";
|
|
320
|
+
import InkSpinner2 from "ink-spinner";
|
|
321
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
322
|
+
function Spinner({
|
|
323
|
+
text,
|
|
324
|
+
type = "dots",
|
|
325
|
+
color = theme.primary,
|
|
326
|
+
textColor = theme.secondary,
|
|
327
|
+
rightAlign = false
|
|
328
|
+
}) {
|
|
329
|
+
const spinnerElement = /* @__PURE__ */ jsx5(Text5, { color, children: /* @__PURE__ */ jsx5(InkSpinner2, { type }) });
|
|
330
|
+
if (!text) {
|
|
331
|
+
return spinnerElement;
|
|
332
|
+
}
|
|
333
|
+
return /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
334
|
+
!rightAlign && spinnerElement,
|
|
335
|
+
/* @__PURE__ */ jsxs5(Text5, { color: textColor, children: [
|
|
336
|
+
rightAlign ? "" : " ",
|
|
337
|
+
text,
|
|
338
|
+
rightAlign ? " " : ""
|
|
339
|
+
] }),
|
|
340
|
+
rightAlign && spinnerElement
|
|
341
|
+
] });
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// src/ui/Table.tsx
|
|
345
|
+
import React from "react";
|
|
346
|
+
import { Box as Box6, Text as Text6 } from "ink";
|
|
347
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
348
|
+
function getCellValue(row, accessor) {
|
|
349
|
+
if (typeof accessor === "function") {
|
|
350
|
+
return String(accessor(row));
|
|
351
|
+
}
|
|
352
|
+
return String(row[accessor] ?? "");
|
|
353
|
+
}
|
|
354
|
+
function calculateColumnWidths(data, columns, maxWidth) {
|
|
355
|
+
const widths = columns.map((col) => {
|
|
356
|
+
const headerWidth = col.header.length;
|
|
357
|
+
const dataWidths = data.map((row) => getCellValue(row, col.accessor).length);
|
|
358
|
+
const maxContentWidth = Math.max(headerWidth, ...dataWidths);
|
|
359
|
+
return col.width ?? Math.min(maxContentWidth, 30);
|
|
360
|
+
});
|
|
361
|
+
if (maxWidth) {
|
|
362
|
+
const totalWidth = widths.reduce((a, b) => a + b, 0) + columns.length * 3 + 1;
|
|
363
|
+
if (totalWidth > maxWidth) {
|
|
364
|
+
const scale = maxWidth / totalWidth;
|
|
365
|
+
return widths.map((w) => Math.floor(w * scale));
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return widths;
|
|
369
|
+
}
|
|
370
|
+
function padText(text, width, align = "left") {
|
|
371
|
+
const truncated = text.length > width ? text.slice(0, width - 1) + "\u2026" : text;
|
|
372
|
+
const padding = width - truncated.length;
|
|
373
|
+
switch (align) {
|
|
374
|
+
case "right":
|
|
375
|
+
return " ".repeat(padding) + truncated;
|
|
376
|
+
case "center":
|
|
377
|
+
const leftPad = Math.floor(padding / 2);
|
|
378
|
+
const rightPad = padding - leftPad;
|
|
379
|
+
return " ".repeat(leftPad) + truncated + " ".repeat(rightPad);
|
|
380
|
+
default:
|
|
381
|
+
return truncated + " ".repeat(padding);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
function BorderLine({
|
|
385
|
+
widths,
|
|
386
|
+
position,
|
|
387
|
+
color
|
|
388
|
+
}) {
|
|
389
|
+
const chars = {
|
|
390
|
+
top: { left: borders.topLeft, right: borders.topRight, junction: borders.topT },
|
|
391
|
+
middle: { left: borders.leftT, right: borders.rightT, junction: borders.cross },
|
|
392
|
+
bottom: { left: borders.bottomLeft, right: borders.bottomRight, junction: borders.bottomT }
|
|
393
|
+
};
|
|
394
|
+
const { left, right, junction } = chars[position];
|
|
395
|
+
const segments = widths.map((w) => borders.horizontal.repeat(w + 2));
|
|
396
|
+
const line = left + segments.join(junction) + right;
|
|
397
|
+
return /* @__PURE__ */ jsx6(Text6, { color, children: line });
|
|
398
|
+
}
|
|
399
|
+
function TableRow({
|
|
400
|
+
row,
|
|
401
|
+
columns,
|
|
402
|
+
widths,
|
|
403
|
+
bordered,
|
|
404
|
+
borderColor,
|
|
405
|
+
isEven,
|
|
406
|
+
striped
|
|
407
|
+
}) {
|
|
408
|
+
return /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
409
|
+
bordered && /* @__PURE__ */ jsx6(Text6, { color: borderColor, children: borders.vertical }),
|
|
410
|
+
columns.map((col, i) => {
|
|
411
|
+
const value = getCellValue(row, col.accessor);
|
|
412
|
+
const colWidth = widths[i] ?? col.header.length;
|
|
413
|
+
const paddedValue = padText(value, colWidth, col.align);
|
|
414
|
+
let cellColor = theme.secondary;
|
|
415
|
+
if (col.cellColor) {
|
|
416
|
+
cellColor = typeof col.cellColor === "function" ? col.cellColor(value, row) : col.cellColor;
|
|
417
|
+
}
|
|
418
|
+
return /* @__PURE__ */ jsxs6(React.Fragment, { children: [
|
|
419
|
+
/* @__PURE__ */ jsx6(Text6, { children: " " }),
|
|
420
|
+
/* @__PURE__ */ jsx6(Text6, { color: cellColor, dimColor: striped && isEven, children: paddedValue }),
|
|
421
|
+
/* @__PURE__ */ jsx6(Text6, { children: " " }),
|
|
422
|
+
bordered && /* @__PURE__ */ jsx6(Text6, { color: borderColor, children: borders.vertical })
|
|
423
|
+
] }, i);
|
|
424
|
+
})
|
|
425
|
+
] });
|
|
426
|
+
}
|
|
427
|
+
function Table({
|
|
428
|
+
data,
|
|
429
|
+
columns,
|
|
430
|
+
bordered = true,
|
|
431
|
+
borderColor = theme.border,
|
|
432
|
+
headerBold = true,
|
|
433
|
+
striped = false,
|
|
434
|
+
maxWidth
|
|
435
|
+
}) {
|
|
436
|
+
const widths = calculateColumnWidths(data, columns, maxWidth);
|
|
437
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
438
|
+
bordered && /* @__PURE__ */ jsx6(BorderLine, { widths, position: "top", color: borderColor }),
|
|
439
|
+
/* @__PURE__ */ jsxs6(Box6, { children: [
|
|
440
|
+
bordered && /* @__PURE__ */ jsx6(Text6, { color: borderColor, children: borders.vertical }),
|
|
441
|
+
columns.map((col, i) => {
|
|
442
|
+
const colWidth = widths[i] ?? col.header.length;
|
|
443
|
+
return /* @__PURE__ */ jsxs6(React.Fragment, { children: [
|
|
444
|
+
/* @__PURE__ */ jsx6(Text6, { children: " " }),
|
|
445
|
+
/* @__PURE__ */ jsx6(Text6, { color: col.headerColor ?? theme.primary, bold: headerBold, children: padText(col.header, colWidth, col.align) }),
|
|
446
|
+
/* @__PURE__ */ jsx6(Text6, { children: " " }),
|
|
447
|
+
bordered && /* @__PURE__ */ jsx6(Text6, { color: borderColor, children: borders.vertical })
|
|
448
|
+
] }, i);
|
|
449
|
+
})
|
|
450
|
+
] }),
|
|
451
|
+
bordered && /* @__PURE__ */ jsx6(BorderLine, { widths, position: "middle", color: borderColor }),
|
|
452
|
+
data.map((row, rowIndex) => /* @__PURE__ */ jsx6(
|
|
453
|
+
TableRow,
|
|
454
|
+
{
|
|
455
|
+
row,
|
|
456
|
+
columns,
|
|
457
|
+
widths,
|
|
458
|
+
bordered,
|
|
459
|
+
borderColor,
|
|
460
|
+
isEven: rowIndex % 2 === 0,
|
|
461
|
+
striped
|
|
462
|
+
},
|
|
463
|
+
rowIndex
|
|
464
|
+
)),
|
|
465
|
+
bordered && /* @__PURE__ */ jsx6(BorderLine, { widths, position: "bottom", color: borderColor })
|
|
466
|
+
] });
|
|
467
|
+
}
|
|
468
|
+
function KeyValue({
|
|
469
|
+
items,
|
|
470
|
+
keyColor = theme.muted,
|
|
471
|
+
valueColor = theme.secondary,
|
|
472
|
+
separator = ":"
|
|
473
|
+
}) {
|
|
474
|
+
const maxKeyWidth = Math.max(...items.map((item) => item.key.length));
|
|
475
|
+
return /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", children: items.map((item, index) => /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
476
|
+
/* @__PURE__ */ jsx6(Text6, { color: keyColor, children: item.key.padEnd(maxKeyWidth) }),
|
|
477
|
+
/* @__PURE__ */ jsxs6(Text6, { color: theme.muted, children: [
|
|
478
|
+
" ",
|
|
479
|
+
separator,
|
|
480
|
+
" "
|
|
481
|
+
] }),
|
|
482
|
+
/* @__PURE__ */ jsx6(Text6, { color: valueColor, children: item.value })
|
|
483
|
+
] }, index)) });
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// src/commands/wizard.tsx
|
|
487
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
488
|
+
var initialState = {
|
|
489
|
+
step: "chain",
|
|
490
|
+
chain: null,
|
|
491
|
+
token: null,
|
|
492
|
+
amount: "",
|
|
493
|
+
quote: null,
|
|
494
|
+
error: null
|
|
495
|
+
};
|
|
496
|
+
function NavigationHints({ step }) {
|
|
497
|
+
const hints = {
|
|
498
|
+
chain: "up/down Select Enter Confirm q Quit",
|
|
499
|
+
token: "up/down Select Enter Confirm b Back q Quit",
|
|
500
|
+
amount: "Enter Confirm b Back q Quit",
|
|
501
|
+
confirm: "Enter Execute b Back q Quit",
|
|
502
|
+
execute: "Please wait..."
|
|
503
|
+
};
|
|
504
|
+
return /* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text7, { color: theme.muted, dimColor: true, children: hints[step] }) });
|
|
505
|
+
}
|
|
506
|
+
function StepIndicator({ currentStep }) {
|
|
507
|
+
const steps = ["chain", "token", "amount", "confirm", "execute"];
|
|
508
|
+
const stepLabels = {
|
|
509
|
+
chain: "Chain",
|
|
510
|
+
token: "Token",
|
|
511
|
+
amount: "Amount",
|
|
512
|
+
confirm: "Confirm",
|
|
513
|
+
execute: "Execute"
|
|
514
|
+
};
|
|
515
|
+
const currentIndex = steps.indexOf(currentStep);
|
|
516
|
+
return /* @__PURE__ */ jsx7(Box7, { marginBottom: 1, children: steps.map((step, index) => {
|
|
517
|
+
const isActive = index === currentIndex;
|
|
518
|
+
const isCompleted = index < currentIndex;
|
|
519
|
+
const separator = index < steps.length - 1 ? " > " : "";
|
|
520
|
+
return /* @__PURE__ */ jsxs7(Text7, { children: [
|
|
521
|
+
/* @__PURE__ */ jsxs7(
|
|
522
|
+
Text7,
|
|
523
|
+
{
|
|
524
|
+
color: isActive ? theme.primary : isCompleted ? theme.success : theme.muted,
|
|
525
|
+
bold: isActive,
|
|
526
|
+
dimColor: !isActive && !isCompleted,
|
|
527
|
+
children: [
|
|
528
|
+
isCompleted ? symbols.check : isActive ? symbols.arrow : symbols.pending,
|
|
529
|
+
" ",
|
|
530
|
+
stepLabels[step]
|
|
531
|
+
]
|
|
532
|
+
}
|
|
533
|
+
),
|
|
534
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.muted, dimColor: true, children: separator })
|
|
535
|
+
] }, step);
|
|
536
|
+
}) });
|
|
537
|
+
}
|
|
538
|
+
function ChainSelectionStep({
|
|
539
|
+
onSelect,
|
|
540
|
+
selectedChain
|
|
541
|
+
}) {
|
|
542
|
+
const [chains, setChains] = useState([]);
|
|
543
|
+
const [loading, setLoading] = useState(true);
|
|
544
|
+
const [error, setError] = useState(null);
|
|
545
|
+
useEffect(() => {
|
|
546
|
+
async function loadChains() {
|
|
547
|
+
try {
|
|
548
|
+
setLoading(true);
|
|
549
|
+
setError(null);
|
|
550
|
+
const response = await getChains();
|
|
551
|
+
const originChains = response.chains.filter((c) => c.id !== HYPEREVM_CHAIN_ID);
|
|
552
|
+
setChains(originChains);
|
|
553
|
+
} catch (err) {
|
|
554
|
+
setError(err instanceof Error ? err.message : "Failed to load chains");
|
|
555
|
+
} finally {
|
|
556
|
+
setLoading(false);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
loadChains();
|
|
560
|
+
}, []);
|
|
561
|
+
if (loading) {
|
|
562
|
+
return /* @__PURE__ */ jsx7(Spinner, { text: "Loading available chains..." });
|
|
563
|
+
}
|
|
564
|
+
if (error) {
|
|
565
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
566
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.error, children: [
|
|
567
|
+
symbols.failed,
|
|
568
|
+
" Error: ",
|
|
569
|
+
error
|
|
570
|
+
] }),
|
|
571
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.muted, children: "Press q to quit and try again" })
|
|
572
|
+
] });
|
|
573
|
+
}
|
|
574
|
+
const chainItems = chains.map((chain) => ({
|
|
575
|
+
label: chain.name,
|
|
576
|
+
value: chain.id,
|
|
577
|
+
chainId: chain.id,
|
|
578
|
+
type: "origin",
|
|
579
|
+
description: `Chain ID: ${chain.id}`
|
|
580
|
+
}));
|
|
581
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
582
|
+
/* @__PURE__ */ jsx7(Box7, { marginBottom: 1, children: /* @__PURE__ */ jsx7(Text7, { color: theme.secondary, bold: true, children: "Select source chain to bridge from:" }) }),
|
|
583
|
+
/* @__PURE__ */ jsx7(
|
|
584
|
+
ChainSelect,
|
|
585
|
+
{
|
|
586
|
+
chains: chainItems,
|
|
587
|
+
onSelect: (item) => {
|
|
588
|
+
const chain = chains.find((c) => c.id === item.chainId);
|
|
589
|
+
if (chain) onSelect(chain);
|
|
590
|
+
},
|
|
591
|
+
label: ""
|
|
592
|
+
}
|
|
593
|
+
),
|
|
594
|
+
/* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsxs7(Text7, { color: theme.muted, dimColor: true, children: [
|
|
595
|
+
"Destination: HyperEVM (Chain ID: ",
|
|
596
|
+
HYPEREVM_CHAIN_ID,
|
|
597
|
+
")"
|
|
598
|
+
] }) })
|
|
599
|
+
] });
|
|
600
|
+
}
|
|
601
|
+
function TokenSelectionStep({
|
|
602
|
+
chain,
|
|
603
|
+
onSelect,
|
|
604
|
+
selectedToken
|
|
605
|
+
}) {
|
|
606
|
+
const [tokens, setTokens] = useState([]);
|
|
607
|
+
const [loading, setLoading] = useState(true);
|
|
608
|
+
const [error, setError] = useState(null);
|
|
609
|
+
useEffect(() => {
|
|
610
|
+
async function loadTokens() {
|
|
611
|
+
try {
|
|
612
|
+
setLoading(true);
|
|
613
|
+
setError(null);
|
|
614
|
+
const response = await getBridgeableTokens(chain.id);
|
|
615
|
+
setTokens(response.tokens);
|
|
616
|
+
} catch (err) {
|
|
617
|
+
setError(err instanceof Error ? err.message : "Failed to load tokens");
|
|
618
|
+
} finally {
|
|
619
|
+
setLoading(false);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
loadTokens();
|
|
623
|
+
}, [chain.id]);
|
|
624
|
+
if (loading) {
|
|
625
|
+
return /* @__PURE__ */ jsx7(Spinner, { text: `Loading bridgeable tokens for ${chain.name}...` });
|
|
626
|
+
}
|
|
627
|
+
if (error) {
|
|
628
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
629
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.error, children: [
|
|
630
|
+
symbols.failed,
|
|
631
|
+
" Error: ",
|
|
632
|
+
error
|
|
633
|
+
] }),
|
|
634
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.muted, children: "Press b to go back or q to quit" })
|
|
635
|
+
] });
|
|
636
|
+
}
|
|
637
|
+
if (tokens.length === 0) {
|
|
638
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
639
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.warning, children: [
|
|
640
|
+
symbols.pending,
|
|
641
|
+
" No bridgeable tokens found for ",
|
|
642
|
+
chain.name
|
|
643
|
+
] }),
|
|
644
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.muted, children: "Press b to go back and select another chain" })
|
|
645
|
+
] });
|
|
646
|
+
}
|
|
647
|
+
const tokenItems = tokens.map((token) => ({
|
|
648
|
+
label: token.symbol,
|
|
649
|
+
value: token.address,
|
|
650
|
+
symbol: token.symbol,
|
|
651
|
+
description: token.name
|
|
652
|
+
}));
|
|
653
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
654
|
+
/* @__PURE__ */ jsx7(Box7, { marginBottom: 1, children: /* @__PURE__ */ jsxs7(Text7, { color: theme.secondary, bold: true, children: [
|
|
655
|
+
"Select token to bridge from ",
|
|
656
|
+
chain.name,
|
|
657
|
+
":"
|
|
658
|
+
] }) }),
|
|
659
|
+
/* @__PURE__ */ jsx7(
|
|
660
|
+
TokenSelect,
|
|
661
|
+
{
|
|
662
|
+
tokens: tokenItems,
|
|
663
|
+
onSelect: (item) => {
|
|
664
|
+
const token = tokens.find((t) => t.address === item.value);
|
|
665
|
+
if (token) onSelect(token);
|
|
666
|
+
},
|
|
667
|
+
label: ""
|
|
668
|
+
}
|
|
669
|
+
)
|
|
670
|
+
] });
|
|
671
|
+
}
|
|
672
|
+
function AmountInputStep({
|
|
673
|
+
token,
|
|
674
|
+
chain,
|
|
675
|
+
amount,
|
|
676
|
+
onAmountChange,
|
|
677
|
+
onConfirm,
|
|
678
|
+
error
|
|
679
|
+
}) {
|
|
680
|
+
const [cursorVisible, setCursorVisible] = useState(true);
|
|
681
|
+
useEffect(() => {
|
|
682
|
+
const interval = setInterval(() => {
|
|
683
|
+
setCursorVisible((v) => !v);
|
|
684
|
+
}, 530);
|
|
685
|
+
return () => clearInterval(interval);
|
|
686
|
+
}, []);
|
|
687
|
+
useInput((input, key) => {
|
|
688
|
+
if (key.return && amount.length > 0) {
|
|
689
|
+
onConfirm();
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
if (key.backspace || key.delete) {
|
|
693
|
+
onAmountChange(amount.slice(0, -1));
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
if (/^[0-9.]$/.test(input)) {
|
|
697
|
+
if (input === "." && amount.includes(".")) return;
|
|
698
|
+
if (input === "0" && amount === "0") return;
|
|
699
|
+
onAmountChange(amount + input);
|
|
700
|
+
}
|
|
701
|
+
});
|
|
702
|
+
const cursor = cursorVisible ? "|" : " ";
|
|
703
|
+
const displayAmount = amount || "0";
|
|
704
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
705
|
+
/* @__PURE__ */ jsx7(Box7, { marginBottom: 1, children: /* @__PURE__ */ jsxs7(Text7, { color: theme.secondary, bold: true, children: [
|
|
706
|
+
"Enter amount of ",
|
|
707
|
+
token.symbol,
|
|
708
|
+
" to bridge:"
|
|
709
|
+
] }) }),
|
|
710
|
+
/* @__PURE__ */ jsx7(Box, { bordered: true, padding: 1, children: /* @__PURE__ */ jsxs7(Box7, { children: [
|
|
711
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.primary, bold: true, children: displayAmount }),
|
|
712
|
+
/* @__PURE__ */ jsx7(Text7, { color: theme.primary, children: cursor }),
|
|
713
|
+
/* @__PURE__ */ jsxs7(Text7, { color: theme.muted, children: [
|
|
714
|
+
" ",
|
|
715
|
+
token.symbol
|
|
716
|
+
] })
|
|
717
|
+
] }) }),
|
|
718
|
+
error && /* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsxs7(Text7, { color: theme.error, children: [
|
|
719
|
+
symbols.failed,
|
|
720
|
+
" ",
|
|
721
|
+
error
|
|
722
|
+
] }) }),
|
|
723
|
+
/* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsxs7(Text7, { color: theme.muted, dimColor: true, children: [
|
|
724
|
+
"From: ",
|
|
725
|
+
chain.name,
|
|
726
|
+
" (",
|
|
727
|
+
token.symbol,
|
|
728
|
+
")"
|
|
729
|
+
] }) }),
|
|
730
|
+
/* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsx7(Text7, { color: theme.muted, dimColor: true, children: "To: HyperEVM (USDC)" }) })
|
|
731
|
+
] });
|
|
732
|
+
}
|
|
733
|
+
function ConfirmationStep({
|
|
734
|
+
chain,
|
|
735
|
+
token,
|
|
736
|
+
amount,
|
|
737
|
+
onConfirm
|
|
738
|
+
}) {
|
|
739
|
+
useInput((_input, key) => {
|
|
740
|
+
if (key.return) {
|
|
741
|
+
onConfirm();
|
|
742
|
+
}
|
|
743
|
+
});
|
|
744
|
+
const estimatedFees = "$1.50 - $3.00";
|
|
745
|
+
const estimatedTime = "2-5 minutes";
|
|
746
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
747
|
+
/* @__PURE__ */ jsx7(Box7, { marginBottom: 1, children: /* @__PURE__ */ jsx7(Text7, { color: theme.secondary, bold: true, children: "Review your bridge transaction:" }) }),
|
|
748
|
+
/* @__PURE__ */ jsxs7(Box, { bordered: true, title: "Transaction Summary", padding: 1, children: [
|
|
749
|
+
/* @__PURE__ */ jsx7(
|
|
750
|
+
KeyValue,
|
|
751
|
+
{
|
|
752
|
+
items: [
|
|
753
|
+
{ key: "From Chain", value: chain.name },
|
|
754
|
+
{ key: "Token", value: token.symbol },
|
|
755
|
+
{ key: "Amount", value: `${amount} ${token.symbol}` },
|
|
756
|
+
{ key: "To Chain", value: "HyperEVM" },
|
|
757
|
+
{ key: "Receive", value: "USDC" }
|
|
758
|
+
]
|
|
759
|
+
}
|
|
760
|
+
),
|
|
761
|
+
/* @__PURE__ */ jsx7(Box7, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsx7(Divider, { width: 40 }) }),
|
|
762
|
+
/* @__PURE__ */ jsx7(
|
|
763
|
+
KeyValue,
|
|
764
|
+
{
|
|
765
|
+
items: [
|
|
766
|
+
{ key: "Est. Fees", value: estimatedFees },
|
|
767
|
+
{ key: "Est. Time", value: estimatedTime }
|
|
768
|
+
],
|
|
769
|
+
keyColor: theme.muted,
|
|
770
|
+
valueColor: theme.warning
|
|
771
|
+
}
|
|
772
|
+
)
|
|
773
|
+
] }),
|
|
774
|
+
/* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsxs7(Text7, { color: theme.success, bold: true, children: [
|
|
775
|
+
symbols.arrow,
|
|
776
|
+
" Press Enter to execute the bridge"
|
|
777
|
+
] }) }),
|
|
778
|
+
/* @__PURE__ */ jsx7(Box7, { children: /* @__PURE__ */ jsx7(Text7, { color: theme.muted, dimColor: true, children: "Press b to go back and edit" }) })
|
|
779
|
+
] });
|
|
780
|
+
}
|
|
781
|
+
function ExecutionStep({
|
|
782
|
+
chain,
|
|
783
|
+
token,
|
|
784
|
+
amount
|
|
785
|
+
}) {
|
|
786
|
+
const [currentStep, setCurrentStep] = useState(0);
|
|
787
|
+
useEffect(() => {
|
|
788
|
+
const timer = setInterval(() => {
|
|
789
|
+
setCurrentStep((prev) => {
|
|
790
|
+
if (prev >= 4) {
|
|
791
|
+
clearInterval(timer);
|
|
792
|
+
return prev;
|
|
793
|
+
}
|
|
794
|
+
return prev + 1;
|
|
795
|
+
});
|
|
796
|
+
}, 2e3);
|
|
797
|
+
return () => clearInterval(timer);
|
|
798
|
+
}, []);
|
|
799
|
+
const steps = [
|
|
800
|
+
{
|
|
801
|
+
label: "Connecting wallet",
|
|
802
|
+
status: currentStep > 0 ? "completed" : currentStep === 0 ? "active" : "pending"
|
|
803
|
+
},
|
|
804
|
+
{
|
|
805
|
+
label: "Fetching quote",
|
|
806
|
+
status: currentStep > 1 ? "completed" : currentStep === 1 ? "active" : "pending"
|
|
807
|
+
},
|
|
808
|
+
{
|
|
809
|
+
label: "Approving token",
|
|
810
|
+
status: currentStep > 2 ? "completed" : currentStep === 2 ? "active" : "pending"
|
|
811
|
+
},
|
|
812
|
+
{
|
|
813
|
+
label: "Executing bridge",
|
|
814
|
+
status: currentStep > 3 ? "completed" : currentStep === 3 ? "active" : "pending"
|
|
815
|
+
},
|
|
816
|
+
{
|
|
817
|
+
label: "Confirming on destination",
|
|
818
|
+
status: currentStep > 4 ? "completed" : currentStep === 4 ? "active" : "pending"
|
|
819
|
+
}
|
|
820
|
+
];
|
|
821
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", children: [
|
|
822
|
+
/* @__PURE__ */ jsx7(Box7, { marginBottom: 1, children: /* @__PURE__ */ jsxs7(Text7, { color: theme.secondary, bold: true, children: [
|
|
823
|
+
"Bridging ",
|
|
824
|
+
amount,
|
|
825
|
+
" ",
|
|
826
|
+
token.symbol,
|
|
827
|
+
" from ",
|
|
828
|
+
chain.name,
|
|
829
|
+
" to HyperEVM"
|
|
830
|
+
] }) }),
|
|
831
|
+
/* @__PURE__ */ jsx7(ProgressSteps, { steps, title: "Bridge Progress", showNumbers: true }),
|
|
832
|
+
currentStep >= 4 && /* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsxs7(Text7, { color: theme.success, bold: true, children: [
|
|
833
|
+
symbols.check,
|
|
834
|
+
" Bridge transaction submitted! Waiting for confirmation..."
|
|
835
|
+
] }) }),
|
|
836
|
+
/* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text7, { color: theme.muted, dimColor: true, children: "(Demo mode - actual execution will be implemented in Story 12.5)" }) })
|
|
837
|
+
] });
|
|
838
|
+
}
|
|
839
|
+
function Wizard() {
|
|
840
|
+
const { exit } = useApp();
|
|
841
|
+
const [state, setState] = useState(initialState);
|
|
842
|
+
useInput((input, key) => {
|
|
843
|
+
if (input === "q" && state.step !== "execute") {
|
|
844
|
+
exit();
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
if (input === "b" && state.step !== "chain" && state.step !== "execute" && state.step !== "amount") {
|
|
848
|
+
goBack();
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
if (input === "b" && state.step === "amount") {
|
|
852
|
+
goBack();
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
});
|
|
856
|
+
const goBack = useCallback(() => {
|
|
857
|
+
setState((prev) => {
|
|
858
|
+
switch (prev.step) {
|
|
859
|
+
case "token":
|
|
860
|
+
return { ...prev, step: "chain", token: null, error: null };
|
|
861
|
+
case "amount":
|
|
862
|
+
return { ...prev, step: "token", amount: "", error: null };
|
|
863
|
+
case "confirm":
|
|
864
|
+
return { ...prev, step: "amount", error: null };
|
|
865
|
+
default:
|
|
866
|
+
return prev;
|
|
867
|
+
}
|
|
868
|
+
});
|
|
869
|
+
}, []);
|
|
870
|
+
const handleChainSelect = useCallback((chain) => {
|
|
871
|
+
setState((prev) => ({ ...prev, chain, step: "token", error: null }));
|
|
872
|
+
}, []);
|
|
873
|
+
const handleTokenSelect = useCallback((token) => {
|
|
874
|
+
setState((prev) => ({ ...prev, token, step: "amount", error: null }));
|
|
875
|
+
}, []);
|
|
876
|
+
const handleAmountChange = useCallback((amount) => {
|
|
877
|
+
setState((prev) => ({ ...prev, amount, error: null }));
|
|
878
|
+
}, []);
|
|
879
|
+
const handleAmountConfirm = useCallback(() => {
|
|
880
|
+
const numAmount = parseFloat(state.amount);
|
|
881
|
+
if (isNaN(numAmount) || numAmount <= 0) {
|
|
882
|
+
setState((prev) => ({ ...prev, error: "Please enter a valid positive amount" }));
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
885
|
+
setState((prev) => ({ ...prev, step: "confirm", error: null }));
|
|
886
|
+
}, [state.amount]);
|
|
887
|
+
const handleConfirm = useCallback(() => {
|
|
888
|
+
setState((prev) => ({ ...prev, step: "execute", error: null }));
|
|
889
|
+
}, []);
|
|
890
|
+
const renderStepContent = () => {
|
|
891
|
+
switch (state.step) {
|
|
892
|
+
case "chain":
|
|
893
|
+
return /* @__PURE__ */ jsx7(
|
|
894
|
+
ChainSelectionStep,
|
|
895
|
+
{
|
|
896
|
+
onSelect: handleChainSelect,
|
|
897
|
+
selectedChain: state.chain
|
|
898
|
+
}
|
|
899
|
+
);
|
|
900
|
+
case "token":
|
|
901
|
+
if (!state.chain) return null;
|
|
902
|
+
return /* @__PURE__ */ jsx7(
|
|
903
|
+
TokenSelectionStep,
|
|
904
|
+
{
|
|
905
|
+
chain: state.chain,
|
|
906
|
+
onSelect: handleTokenSelect,
|
|
907
|
+
selectedToken: state.token
|
|
908
|
+
}
|
|
909
|
+
);
|
|
910
|
+
case "amount":
|
|
911
|
+
if (!state.chain || !state.token) return null;
|
|
912
|
+
return /* @__PURE__ */ jsx7(
|
|
913
|
+
AmountInputStep,
|
|
914
|
+
{
|
|
915
|
+
chain: state.chain,
|
|
916
|
+
token: state.token,
|
|
917
|
+
amount: state.amount,
|
|
918
|
+
onAmountChange: handleAmountChange,
|
|
919
|
+
onConfirm: handleAmountConfirm,
|
|
920
|
+
error: state.error
|
|
921
|
+
}
|
|
922
|
+
);
|
|
923
|
+
case "confirm":
|
|
924
|
+
if (!state.chain || !state.token) return null;
|
|
925
|
+
return /* @__PURE__ */ jsx7(
|
|
926
|
+
ConfirmationStep,
|
|
927
|
+
{
|
|
928
|
+
chain: state.chain,
|
|
929
|
+
token: state.token,
|
|
930
|
+
amount: state.amount,
|
|
931
|
+
onConfirm: handleConfirm
|
|
932
|
+
}
|
|
933
|
+
);
|
|
934
|
+
case "execute":
|
|
935
|
+
if (!state.chain || !state.token) return null;
|
|
936
|
+
return /* @__PURE__ */ jsx7(
|
|
937
|
+
ExecutionStep,
|
|
938
|
+
{
|
|
939
|
+
chain: state.chain,
|
|
940
|
+
token: state.token,
|
|
941
|
+
amount: state.amount
|
|
942
|
+
}
|
|
943
|
+
);
|
|
944
|
+
default:
|
|
945
|
+
return null;
|
|
946
|
+
}
|
|
947
|
+
};
|
|
948
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", padding: 1, children: [
|
|
949
|
+
/* @__PURE__ */ jsx7(Header, { showTagline: true, tagline: "Cross-chain bridge to Hyperliquid" }),
|
|
950
|
+
/* @__PURE__ */ jsx7(StepIndicator, { currentStep: state.step }),
|
|
951
|
+
/* @__PURE__ */ jsx7(Box7, { flexDirection: "column", marginY: 1, children: renderStepContent() }),
|
|
952
|
+
/* @__PURE__ */ jsx7(NavigationHints, { step: state.step })
|
|
953
|
+
] });
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
// src/commands/quote.tsx
|
|
957
|
+
import { useState as useState2, useEffect as useEffect2 } from "react";
|
|
958
|
+
import { Box as Box8, Text as Text8, useApp as useApp2 } from "ink";
|
|
959
|
+
import {
|
|
960
|
+
Mina,
|
|
961
|
+
getChains as getChains2,
|
|
962
|
+
getTokens,
|
|
963
|
+
HYPEREVM_CHAIN_ID as HYPEREVM_CHAIN_ID2,
|
|
964
|
+
HYPEREVM_USDC_ADDRESS
|
|
965
|
+
} from "@siphoyawe/mina-sdk";
|
|
966
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
967
|
+
function parseAmount(amount, decimals) {
|
|
968
|
+
const [whole, fraction = ""] = amount.split(".");
|
|
969
|
+
const paddedFraction = fraction.padEnd(decimals, "0").slice(0, decimals);
|
|
970
|
+
return (whole || "0") + paddedFraction;
|
|
971
|
+
}
|
|
972
|
+
function formatAmount(amount, decimals) {
|
|
973
|
+
const padded = amount.padStart(decimals + 1, "0");
|
|
974
|
+
const integerPart = padded.slice(0, -decimals) || "0";
|
|
975
|
+
const decimalPart = padded.slice(-decimals);
|
|
976
|
+
const trimmedDecimal = decimalPart.replace(/0+$/, "");
|
|
977
|
+
return trimmedDecimal ? `${integerPart}.${trimmedDecimal}` : integerPart;
|
|
978
|
+
}
|
|
979
|
+
function formatTime(seconds) {
|
|
980
|
+
if (seconds < 60) {
|
|
981
|
+
return `~${seconds} seconds`;
|
|
982
|
+
}
|
|
983
|
+
const minutes = Math.ceil(seconds / 60);
|
|
984
|
+
return `~${minutes} minute${minutes > 1 ? "s" : ""}`;
|
|
985
|
+
}
|
|
986
|
+
function formatUsd(amount) {
|
|
987
|
+
return `$${amount.toFixed(2)}`;
|
|
988
|
+
}
|
|
989
|
+
function getRouteDescription(quote) {
|
|
990
|
+
const stepTools = quote.steps.map((step) => step.tool);
|
|
991
|
+
const uniqueTools = [...new Set(stepTools)];
|
|
992
|
+
return uniqueTools.join(" -> ");
|
|
993
|
+
}
|
|
994
|
+
function QuoteBox({
|
|
995
|
+
quote,
|
|
996
|
+
fromChain,
|
|
997
|
+
toChain,
|
|
998
|
+
sourceToken,
|
|
999
|
+
destToken
|
|
1000
|
+
}) {
|
|
1001
|
+
const inputAmount = formatAmount(quote.fromAmount, sourceToken.decimals);
|
|
1002
|
+
const outputAmount = formatAmount(quote.toAmount, destToken.decimals);
|
|
1003
|
+
const routeDescription = getRouteDescription(quote);
|
|
1004
|
+
const estimatedTime = formatTime(quote.estimatedTime);
|
|
1005
|
+
const totalFees = formatUsd(quote.fees.totalUsd);
|
|
1006
|
+
const gasFees = formatUsd(quote.fees.gasUsd);
|
|
1007
|
+
const bridgeFees = formatUsd(quote.fees.bridgeFeeUsd);
|
|
1008
|
+
const protocolFees = formatUsd(quote.fees.protocolFeeUsd);
|
|
1009
|
+
return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", children: /* @__PURE__ */ jsxs8(Box, { bordered: true, title: "Bridge Quote", padding: 1, children: [
|
|
1010
|
+
/* @__PURE__ */ jsx8(
|
|
1011
|
+
KeyValue,
|
|
1012
|
+
{
|
|
1013
|
+
items: [
|
|
1014
|
+
{ key: "From", value: `${inputAmount} ${sourceToken.symbol} (${fromChain.name})` },
|
|
1015
|
+
{ key: "To", value: `~${outputAmount} ${destToken.symbol} (${toChain})` }
|
|
1016
|
+
],
|
|
1017
|
+
keyColor: theme.muted,
|
|
1018
|
+
valueColor: theme.primary
|
|
1019
|
+
}
|
|
1020
|
+
),
|
|
1021
|
+
/* @__PURE__ */ jsx8(Box8, { marginY: 1, children: /* @__PURE__ */ jsx8(Divider, { width: 45 }) }),
|
|
1022
|
+
/* @__PURE__ */ jsx8(
|
|
1023
|
+
KeyValue,
|
|
1024
|
+
{
|
|
1025
|
+
items: [
|
|
1026
|
+
{ key: "Route", value: routeDescription },
|
|
1027
|
+
{ key: "Time", value: estimatedTime },
|
|
1028
|
+
{ key: "Fees", value: totalFees }
|
|
1029
|
+
],
|
|
1030
|
+
keyColor: theme.muted,
|
|
1031
|
+
valueColor: theme.secondary
|
|
1032
|
+
}
|
|
1033
|
+
),
|
|
1034
|
+
/* @__PURE__ */ jsx8(Box8, { marginY: 1, children: /* @__PURE__ */ jsx8(Divider, { width: 45 }) }),
|
|
1035
|
+
/* @__PURE__ */ jsx8(Box8, { marginBottom: 1, children: /* @__PURE__ */ jsx8(Text8, { color: theme.muted, bold: true, children: "Fee Breakdown:" }) }),
|
|
1036
|
+
/* @__PURE__ */ jsx8(
|
|
1037
|
+
KeyValue,
|
|
1038
|
+
{
|
|
1039
|
+
items: [
|
|
1040
|
+
{ key: " Gas", value: gasFees },
|
|
1041
|
+
{ key: " Bridge", value: bridgeFees },
|
|
1042
|
+
{ key: " Protocol", value: protocolFees },
|
|
1043
|
+
{ key: " Total", value: totalFees }
|
|
1044
|
+
],
|
|
1045
|
+
keyColor: theme.muted,
|
|
1046
|
+
valueColor: theme.warning
|
|
1047
|
+
}
|
|
1048
|
+
),
|
|
1049
|
+
quote.highImpact && /* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsxs8(Text8, { color: theme.warning, children: [
|
|
1050
|
+
symbols.pending,
|
|
1051
|
+
" High price impact: ",
|
|
1052
|
+
(quote.priceImpact * 100).toFixed(2),
|
|
1053
|
+
"%"
|
|
1054
|
+
] }) }),
|
|
1055
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsxs8(Text8, { color: theme.muted, children: [
|
|
1056
|
+
"Min. received: ",
|
|
1057
|
+
quote.minimumReceivedFormatted,
|
|
1058
|
+
" ",
|
|
1059
|
+
destToken.symbol
|
|
1060
|
+
] }) })
|
|
1061
|
+
] }) });
|
|
1062
|
+
}
|
|
1063
|
+
function JsonOutput({ quote, fromChain, sourceToken }) {
|
|
1064
|
+
const jsonData = {
|
|
1065
|
+
quote: {
|
|
1066
|
+
id: quote.id,
|
|
1067
|
+
from: {
|
|
1068
|
+
chain: fromChain.name,
|
|
1069
|
+
chainId: fromChain.id,
|
|
1070
|
+
token: sourceToken.symbol,
|
|
1071
|
+
amount: formatAmount(quote.fromAmount, sourceToken.decimals),
|
|
1072
|
+
amountRaw: quote.fromAmount
|
|
1073
|
+
},
|
|
1074
|
+
to: {
|
|
1075
|
+
chain: "Hyperliquid",
|
|
1076
|
+
chainId: HYPEREVM_CHAIN_ID2,
|
|
1077
|
+
token: quote.toToken.symbol,
|
|
1078
|
+
amount: formatAmount(quote.toAmount, quote.toToken.decimals),
|
|
1079
|
+
amountRaw: quote.toAmount
|
|
1080
|
+
},
|
|
1081
|
+
route: {
|
|
1082
|
+
steps: quote.steps.map((step) => ({
|
|
1083
|
+
type: step.type,
|
|
1084
|
+
tool: step.tool,
|
|
1085
|
+
fromChain: step.fromChainId,
|
|
1086
|
+
toChain: step.toChainId,
|
|
1087
|
+
estimatedTime: step.estimatedTime
|
|
1088
|
+
})),
|
|
1089
|
+
totalSteps: quote.steps.length
|
|
1090
|
+
},
|
|
1091
|
+
fees: {
|
|
1092
|
+
total: quote.fees.totalUsd,
|
|
1093
|
+
gas: quote.fees.gasUsd,
|
|
1094
|
+
bridge: quote.fees.bridgeFeeUsd,
|
|
1095
|
+
protocol: quote.fees.protocolFeeUsd
|
|
1096
|
+
},
|
|
1097
|
+
estimatedTime: quote.estimatedTime,
|
|
1098
|
+
priceImpact: quote.priceImpact,
|
|
1099
|
+
highImpact: quote.highImpact,
|
|
1100
|
+
slippageTolerance: quote.slippageTolerance,
|
|
1101
|
+
minimumReceived: quote.minimumReceivedFormatted,
|
|
1102
|
+
expiresAt: quote.expiresAt
|
|
1103
|
+
}
|
|
1104
|
+
};
|
|
1105
|
+
return /* @__PURE__ */ jsx8(Text8, { children: JSON.stringify(jsonData, null, 2) });
|
|
1106
|
+
}
|
|
1107
|
+
function ErrorDisplay({ message }) {
|
|
1108
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", padding: 1, children: [
|
|
1109
|
+
/* @__PURE__ */ jsx8(Box8, { children: /* @__PURE__ */ jsxs8(Text8, { color: theme.error, bold: true, children: [
|
|
1110
|
+
symbols.failed,
|
|
1111
|
+
" Error: ",
|
|
1112
|
+
message
|
|
1113
|
+
] }) }),
|
|
1114
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text8, { color: theme.muted, children: "Usage: npx mina quote --from <chain> --token <symbol> --amount <number>" }) }),
|
|
1115
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsx8(Text8, { color: theme.muted, children: "Example: npx mina quote --from arbitrum --token USDC --amount 100" }) })
|
|
1116
|
+
] });
|
|
1117
|
+
}
|
|
1118
|
+
function QuoteDisplay({
|
|
1119
|
+
fromChain,
|
|
1120
|
+
toChain,
|
|
1121
|
+
token,
|
|
1122
|
+
amount,
|
|
1123
|
+
jsonOutput
|
|
1124
|
+
}) {
|
|
1125
|
+
const { exit } = useApp2();
|
|
1126
|
+
const [loading, setLoading] = useState2(true);
|
|
1127
|
+
const [error, setError] = useState2(null);
|
|
1128
|
+
const [quote, setQuote] = useState2(null);
|
|
1129
|
+
const [sourceChain, setSourceChain] = useState2(null);
|
|
1130
|
+
const [sourceToken, setSourceToken] = useState2(null);
|
|
1131
|
+
const [destToken, setDestToken] = useState2(null);
|
|
1132
|
+
useEffect2(() => {
|
|
1133
|
+
async function fetchQuote() {
|
|
1134
|
+
try {
|
|
1135
|
+
setLoading(true);
|
|
1136
|
+
setError(null);
|
|
1137
|
+
const numAmount = parseFloat(amount);
|
|
1138
|
+
if (isNaN(numAmount) || numAmount <= 0) {
|
|
1139
|
+
throw new Error("Invalid amount. Please provide a positive number.");
|
|
1140
|
+
}
|
|
1141
|
+
const chainsResponse = await getChains2();
|
|
1142
|
+
const chain = chainsResponse.chains.find(
|
|
1143
|
+
(c) => c.name.toLowerCase() === fromChain.toLowerCase() || c.key.toLowerCase() === fromChain.toLowerCase()
|
|
1144
|
+
);
|
|
1145
|
+
if (!chain) {
|
|
1146
|
+
const availableChains = chainsResponse.chains.map((c) => c.name).join(", ");
|
|
1147
|
+
throw new Error(
|
|
1148
|
+
`Unknown chain: "${fromChain}". Available chains: ${availableChains}`
|
|
1149
|
+
);
|
|
1150
|
+
}
|
|
1151
|
+
setSourceChain(chain);
|
|
1152
|
+
const tokensResponse = await getTokens(chain.id);
|
|
1153
|
+
const foundToken = tokensResponse.tokens.find(
|
|
1154
|
+
(t) => t.symbol.toLowerCase() === token.toLowerCase()
|
|
1155
|
+
);
|
|
1156
|
+
if (!foundToken) {
|
|
1157
|
+
const availableTokens = tokensResponse.tokens.slice(0, 10).map((t) => t.symbol).join(", ");
|
|
1158
|
+
throw new Error(
|
|
1159
|
+
`Token "${token}" not found on ${chain.name}. Available tokens include: ${availableTokens}...`
|
|
1160
|
+
);
|
|
1161
|
+
}
|
|
1162
|
+
setSourceToken(foundToken);
|
|
1163
|
+
const destinationToken = {
|
|
1164
|
+
address: HYPEREVM_USDC_ADDRESS,
|
|
1165
|
+
symbol: "USDC",
|
|
1166
|
+
name: "USD Coin",
|
|
1167
|
+
decimals: 6,
|
|
1168
|
+
logoUrl: "",
|
|
1169
|
+
chainId: HYPEREVM_CHAIN_ID2
|
|
1170
|
+
};
|
|
1171
|
+
setDestToken(destinationToken);
|
|
1172
|
+
const mina = new Mina({ integrator: "mina-cli" });
|
|
1173
|
+
const quoteResult = await mina.getQuote({
|
|
1174
|
+
fromChainId: chain.id,
|
|
1175
|
+
toChainId: HYPEREVM_CHAIN_ID2,
|
|
1176
|
+
fromToken: foundToken.address,
|
|
1177
|
+
toToken: HYPEREVM_USDC_ADDRESS,
|
|
1178
|
+
fromAmount: parseAmount(amount, foundToken.decimals),
|
|
1179
|
+
fromAddress: "0x0000000000000000000000000000000000000000"
|
|
1180
|
+
// Placeholder for quote
|
|
1181
|
+
});
|
|
1182
|
+
setQuote(quoteResult);
|
|
1183
|
+
} catch (err) {
|
|
1184
|
+
const message = err instanceof Error ? err.message : "Failed to fetch quote";
|
|
1185
|
+
setError(message);
|
|
1186
|
+
} finally {
|
|
1187
|
+
setLoading(false);
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
fetchQuote();
|
|
1191
|
+
}, [fromChain, token, amount]);
|
|
1192
|
+
useEffect2(() => {
|
|
1193
|
+
if (!loading && jsonOutput) {
|
|
1194
|
+
const timer = setTimeout(() => exit(), 100);
|
|
1195
|
+
return () => clearTimeout(timer);
|
|
1196
|
+
}
|
|
1197
|
+
}, [loading, jsonOutput, exit]);
|
|
1198
|
+
if (loading) {
|
|
1199
|
+
return /* @__PURE__ */ jsx8(Box8, { padding: 1, children: /* @__PURE__ */ jsx8(Spinner, { text: `Fetching quote for ${amount} ${token} from ${fromChain}...` }) });
|
|
1200
|
+
}
|
|
1201
|
+
if (error) {
|
|
1202
|
+
return /* @__PURE__ */ jsx8(ErrorDisplay, { message: error });
|
|
1203
|
+
}
|
|
1204
|
+
if (!quote || !sourceChain || !sourceToken || !destToken) {
|
|
1205
|
+
return /* @__PURE__ */ jsx8(ErrorDisplay, { message: "Failed to fetch quote data" });
|
|
1206
|
+
}
|
|
1207
|
+
if (jsonOutput) {
|
|
1208
|
+
return /* @__PURE__ */ jsx8(JsonOutput, { quote, fromChain: sourceChain, sourceToken });
|
|
1209
|
+
}
|
|
1210
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", padding: 1, children: [
|
|
1211
|
+
/* @__PURE__ */ jsx8(
|
|
1212
|
+
QuoteBox,
|
|
1213
|
+
{
|
|
1214
|
+
quote,
|
|
1215
|
+
fromChain: sourceChain,
|
|
1216
|
+
toChain: toChain === "hyperliquid" ? "Hyperliquid" : toChain,
|
|
1217
|
+
sourceToken,
|
|
1218
|
+
destToken
|
|
1219
|
+
}
|
|
1220
|
+
),
|
|
1221
|
+
/* @__PURE__ */ jsx8(Box8, { marginTop: 1, children: /* @__PURE__ */ jsxs8(Text8, { color: theme.muted, children: [
|
|
1222
|
+
"Quote valid until: ",
|
|
1223
|
+
new Date(quote.expiresAt).toLocaleTimeString()
|
|
1224
|
+
] }) })
|
|
1225
|
+
] });
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
// src/commands/chains.tsx
|
|
1229
|
+
import { useState as useState3, useEffect as useEffect3 } from "react";
|
|
1230
|
+
import { Box as Box9, Text as Text9, useApp as useApp3 } from "ink";
|
|
1231
|
+
import {
|
|
1232
|
+
getChains as getChains3,
|
|
1233
|
+
HYPEREVM_CHAIN_ID as HYPEREVM_CHAIN_ID3
|
|
1234
|
+
} from "@siphoyawe/mina-sdk";
|
|
1235
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1236
|
+
function ChainsCommand({ json = false }) {
|
|
1237
|
+
const { exit } = useApp3();
|
|
1238
|
+
const [chains, setChains] = useState3([]);
|
|
1239
|
+
const [loading, setLoading] = useState3(true);
|
|
1240
|
+
const [error, setError] = useState3(null);
|
|
1241
|
+
useEffect3(() => {
|
|
1242
|
+
async function loadChains() {
|
|
1243
|
+
try {
|
|
1244
|
+
setLoading(true);
|
|
1245
|
+
setError(null);
|
|
1246
|
+
const response = await getChains3();
|
|
1247
|
+
const chainRows = response.chains.map((chain) => ({
|
|
1248
|
+
name: chain.name,
|
|
1249
|
+
id: chain.id,
|
|
1250
|
+
type: "Origin"
|
|
1251
|
+
}));
|
|
1252
|
+
const hasHyperEvm = chainRows.some((c) => c.id === HYPEREVM_CHAIN_ID3);
|
|
1253
|
+
if (!hasHyperEvm) {
|
|
1254
|
+
chainRows.push({
|
|
1255
|
+
name: "Hyperliquid",
|
|
1256
|
+
id: HYPEREVM_CHAIN_ID3,
|
|
1257
|
+
type: "Dest"
|
|
1258
|
+
});
|
|
1259
|
+
} else {
|
|
1260
|
+
const hyperEvmChain = chainRows.find((c) => c.id === HYPEREVM_CHAIN_ID3);
|
|
1261
|
+
if (hyperEvmChain) {
|
|
1262
|
+
hyperEvmChain.type = "Dest";
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
chainRows.sort((a, b) => {
|
|
1266
|
+
if (a.type !== b.type) {
|
|
1267
|
+
return a.type === "Origin" ? -1 : 1;
|
|
1268
|
+
}
|
|
1269
|
+
return a.name.localeCompare(b.name);
|
|
1270
|
+
});
|
|
1271
|
+
setChains(chainRows);
|
|
1272
|
+
} catch (err) {
|
|
1273
|
+
setError(err instanceof Error ? err.message : "Failed to load chains");
|
|
1274
|
+
} finally {
|
|
1275
|
+
setLoading(false);
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
loadChains();
|
|
1279
|
+
}, []);
|
|
1280
|
+
useEffect3(() => {
|
|
1281
|
+
if (!loading && json) {
|
|
1282
|
+
setTimeout(() => exit(), 100);
|
|
1283
|
+
}
|
|
1284
|
+
}, [loading, json, exit]);
|
|
1285
|
+
if (json) {
|
|
1286
|
+
if (loading) return null;
|
|
1287
|
+
if (error) {
|
|
1288
|
+
console.error(JSON.stringify({ error }, null, 2));
|
|
1289
|
+
return null;
|
|
1290
|
+
}
|
|
1291
|
+
console.log(JSON.stringify(chains, null, 2));
|
|
1292
|
+
return null;
|
|
1293
|
+
}
|
|
1294
|
+
if (loading) {
|
|
1295
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", padding: 1, children: [
|
|
1296
|
+
/* @__PURE__ */ jsx9(Header, { compact: true, showTagline: false }),
|
|
1297
|
+
/* @__PURE__ */ jsx9(Spinner, { text: "Loading supported chains..." })
|
|
1298
|
+
] });
|
|
1299
|
+
}
|
|
1300
|
+
if (error) {
|
|
1301
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", padding: 1, children: [
|
|
1302
|
+
/* @__PURE__ */ jsx9(Header, { compact: true, showTagline: false }),
|
|
1303
|
+
/* @__PURE__ */ jsx9(Box9, { children: /* @__PURE__ */ jsxs9(Text9, { color: theme.error, children: [
|
|
1304
|
+
symbols.failed,
|
|
1305
|
+
" Error: ",
|
|
1306
|
+
error
|
|
1307
|
+
] }) })
|
|
1308
|
+
] });
|
|
1309
|
+
}
|
|
1310
|
+
const columns = [
|
|
1311
|
+
{
|
|
1312
|
+
header: "Chain",
|
|
1313
|
+
accessor: "name",
|
|
1314
|
+
headerColor: theme.primary,
|
|
1315
|
+
cellColor: theme.secondary
|
|
1316
|
+
},
|
|
1317
|
+
{
|
|
1318
|
+
header: "ID",
|
|
1319
|
+
accessor: (row) => String(row.id),
|
|
1320
|
+
align: "right",
|
|
1321
|
+
headerColor: theme.primary,
|
|
1322
|
+
cellColor: theme.muted
|
|
1323
|
+
},
|
|
1324
|
+
{
|
|
1325
|
+
header: "Type",
|
|
1326
|
+
accessor: "type",
|
|
1327
|
+
headerColor: theme.primary,
|
|
1328
|
+
cellColor: (value) => value === "Origin" ? theme.success : theme.accent
|
|
1329
|
+
}
|
|
1330
|
+
];
|
|
1331
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", padding: 1, children: [
|
|
1332
|
+
/* @__PURE__ */ jsx9(Header, { compact: true, showTagline: false }),
|
|
1333
|
+
/* @__PURE__ */ jsx9(Box9, { marginBottom: 1, children: /* @__PURE__ */ jsxs9(Text9, { color: theme.secondary, children: [
|
|
1334
|
+
symbols.arrow,
|
|
1335
|
+
" Supported Chains (",
|
|
1336
|
+
chains.length,
|
|
1337
|
+
")"
|
|
1338
|
+
] }) }),
|
|
1339
|
+
/* @__PURE__ */ jsx9(
|
|
1340
|
+
Table,
|
|
1341
|
+
{
|
|
1342
|
+
data: chains,
|
|
1343
|
+
columns,
|
|
1344
|
+
bordered: true,
|
|
1345
|
+
borderColor: theme.border
|
|
1346
|
+
}
|
|
1347
|
+
),
|
|
1348
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text9, { color: theme.muted, dimColor: true, children: "Origin = Source chains for bridging | Dest = Destination chain" }) })
|
|
1349
|
+
] });
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
// src/commands/tokens.tsx
|
|
1353
|
+
import { useState as useState4, useEffect as useEffect4 } from "react";
|
|
1354
|
+
import { Box as Box10, Text as Text10, useApp as useApp4 } from "ink";
|
|
1355
|
+
import {
|
|
1356
|
+
getChains as getChains4,
|
|
1357
|
+
getBridgeableTokens as getBridgeableTokens2
|
|
1358
|
+
} from "@siphoyawe/mina-sdk";
|
|
1359
|
+
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1360
|
+
function truncateAddress(address) {
|
|
1361
|
+
if (address.length <= 14) return address;
|
|
1362
|
+
return `${address.slice(0, 10)}...${address.slice(-4)}`;
|
|
1363
|
+
}
|
|
1364
|
+
async function resolveChainId(chainInput) {
|
|
1365
|
+
const numericId = parseInt(chainInput, 10);
|
|
1366
|
+
if (!isNaN(numericId)) {
|
|
1367
|
+
const response2 = await getChains4();
|
|
1368
|
+
const chain2 = response2.chains.find((c) => c.id === numericId);
|
|
1369
|
+
if (chain2) {
|
|
1370
|
+
return { chainId: chain2.id, chainName: chain2.name };
|
|
1371
|
+
}
|
|
1372
|
+
return { chainId: numericId, chainName: chainInput };
|
|
1373
|
+
}
|
|
1374
|
+
const response = await getChains4();
|
|
1375
|
+
const chainLower = chainInput.toLowerCase();
|
|
1376
|
+
const chain = response.chains.find(
|
|
1377
|
+
(c) => c.name.toLowerCase() === chainLower || c.key?.toLowerCase() === chainLower
|
|
1378
|
+
);
|
|
1379
|
+
if (chain) {
|
|
1380
|
+
return { chainId: chain.id, chainName: chain.name };
|
|
1381
|
+
}
|
|
1382
|
+
return null;
|
|
1383
|
+
}
|
|
1384
|
+
function TokensCommand({
|
|
1385
|
+
chain,
|
|
1386
|
+
json = false
|
|
1387
|
+
}) {
|
|
1388
|
+
const { exit } = useApp4();
|
|
1389
|
+
const [tokens, setTokens] = useState4([]);
|
|
1390
|
+
const [loading, setLoading] = useState4(true);
|
|
1391
|
+
const [error, setError] = useState4(null);
|
|
1392
|
+
const [chainName, setChainName] = useState4("");
|
|
1393
|
+
const [availableChains, setAvailableChains] = useState4([]);
|
|
1394
|
+
useEffect4(() => {
|
|
1395
|
+
async function loadTokens() {
|
|
1396
|
+
try {
|
|
1397
|
+
setLoading(true);
|
|
1398
|
+
setError(null);
|
|
1399
|
+
if (!chain) {
|
|
1400
|
+
const chainsResponse = await getChains4();
|
|
1401
|
+
setAvailableChains(chainsResponse.chains);
|
|
1402
|
+
setError("Please specify a chain with --chain <name|id>");
|
|
1403
|
+
setLoading(false);
|
|
1404
|
+
return;
|
|
1405
|
+
}
|
|
1406
|
+
const resolved = await resolveChainId(chain);
|
|
1407
|
+
if (!resolved) {
|
|
1408
|
+
const chainsResponse = await getChains4();
|
|
1409
|
+
setAvailableChains(chainsResponse.chains);
|
|
1410
|
+
setError(`Unknown chain: "${chain}"`);
|
|
1411
|
+
setLoading(false);
|
|
1412
|
+
return;
|
|
1413
|
+
}
|
|
1414
|
+
setChainName(resolved.chainName);
|
|
1415
|
+
const response = await getBridgeableTokens2(resolved.chainId);
|
|
1416
|
+
const tokenRows = response.tokens.map((token) => ({
|
|
1417
|
+
symbol: token.symbol,
|
|
1418
|
+
address: token.address,
|
|
1419
|
+
displayAddress: truncateAddress(token.address),
|
|
1420
|
+
decimals: token.decimals,
|
|
1421
|
+
name: token.name
|
|
1422
|
+
}));
|
|
1423
|
+
tokenRows.sort((a, b) => a.symbol.localeCompare(b.symbol));
|
|
1424
|
+
setTokens(tokenRows);
|
|
1425
|
+
} catch (err) {
|
|
1426
|
+
setError(err instanceof Error ? err.message : "Failed to load tokens");
|
|
1427
|
+
} finally {
|
|
1428
|
+
setLoading(false);
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
loadTokens();
|
|
1432
|
+
}, [chain]);
|
|
1433
|
+
useEffect4(() => {
|
|
1434
|
+
if (!loading && json) {
|
|
1435
|
+
setTimeout(() => exit(), 100);
|
|
1436
|
+
}
|
|
1437
|
+
}, [loading, json, exit]);
|
|
1438
|
+
if (json) {
|
|
1439
|
+
if (loading) return null;
|
|
1440
|
+
if (error && !availableChains.length) {
|
|
1441
|
+
console.error(JSON.stringify({ error }, null, 2));
|
|
1442
|
+
return null;
|
|
1443
|
+
}
|
|
1444
|
+
if (error && availableChains.length) {
|
|
1445
|
+
console.error(JSON.stringify({
|
|
1446
|
+
error,
|
|
1447
|
+
availableChains: availableChains.map((c) => ({ name: c.name, id: c.id }))
|
|
1448
|
+
}, null, 2));
|
|
1449
|
+
return null;
|
|
1450
|
+
}
|
|
1451
|
+
console.log(JSON.stringify(tokens.map((t) => ({
|
|
1452
|
+
symbol: t.symbol,
|
|
1453
|
+
address: t.address,
|
|
1454
|
+
decimals: t.decimals,
|
|
1455
|
+
name: t.name
|
|
1456
|
+
})), null, 2));
|
|
1457
|
+
return null;
|
|
1458
|
+
}
|
|
1459
|
+
if (loading) {
|
|
1460
|
+
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", padding: 1, children: [
|
|
1461
|
+
/* @__PURE__ */ jsx10(Header, { compact: true, showTagline: false }),
|
|
1462
|
+
/* @__PURE__ */ jsx10(Spinner, { text: chain ? `Loading tokens for ${chain}...` : "Loading..." })
|
|
1463
|
+
] });
|
|
1464
|
+
}
|
|
1465
|
+
if (error) {
|
|
1466
|
+
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", padding: 1, children: [
|
|
1467
|
+
/* @__PURE__ */ jsx10(Header, { compact: true, showTagline: false }),
|
|
1468
|
+
/* @__PURE__ */ jsx10(Box10, { marginBottom: 1, children: /* @__PURE__ */ jsxs10(Text10, { color: theme.error, children: [
|
|
1469
|
+
symbols.failed,
|
|
1470
|
+
" ",
|
|
1471
|
+
error
|
|
1472
|
+
] }) }),
|
|
1473
|
+
availableChains.length > 0 && /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
|
|
1474
|
+
/* @__PURE__ */ jsx10(Box10, { marginBottom: 1, children: /* @__PURE__ */ jsx10(Text10, { color: theme.secondary, children: "Available chains:" }) }),
|
|
1475
|
+
availableChains.slice(0, 10).map((c, i) => /* @__PURE__ */ jsxs10(Box10, { children: [
|
|
1476
|
+
/* @__PURE__ */ jsxs10(Text10, { color: theme.muted, children: [
|
|
1477
|
+
" ",
|
|
1478
|
+
symbols.arrow,
|
|
1479
|
+
" "
|
|
1480
|
+
] }),
|
|
1481
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.primary, children: c.name }),
|
|
1482
|
+
/* @__PURE__ */ jsxs10(Text10, { color: theme.muted, children: [
|
|
1483
|
+
" (ID: ",
|
|
1484
|
+
c.id,
|
|
1485
|
+
")"
|
|
1486
|
+
] })
|
|
1487
|
+
] }, i)),
|
|
1488
|
+
availableChains.length > 10 && /* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsxs10(Text10, { color: theme.muted, dimColor: true, children: [
|
|
1489
|
+
"...and ",
|
|
1490
|
+
availableChains.length - 10,
|
|
1491
|
+
' more. Use "mina chains" to see all.'
|
|
1492
|
+
] }) }),
|
|
1493
|
+
/* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text10, { color: theme.muted, dimColor: true, children: "Example: mina tokens --chain arbitrum" }) })
|
|
1494
|
+
] })
|
|
1495
|
+
] });
|
|
1496
|
+
}
|
|
1497
|
+
if (tokens.length === 0) {
|
|
1498
|
+
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", padding: 1, children: [
|
|
1499
|
+
/* @__PURE__ */ jsx10(Header, { compact: true, showTagline: false }),
|
|
1500
|
+
/* @__PURE__ */ jsx10(Box10, { children: /* @__PURE__ */ jsxs10(Text10, { color: theme.warning, children: [
|
|
1501
|
+
symbols.pending,
|
|
1502
|
+
" No bridgeable tokens found for ",
|
|
1503
|
+
chainName
|
|
1504
|
+
] }) })
|
|
1505
|
+
] });
|
|
1506
|
+
}
|
|
1507
|
+
const columns = [
|
|
1508
|
+
{
|
|
1509
|
+
header: "Symbol",
|
|
1510
|
+
accessor: "symbol",
|
|
1511
|
+
headerColor: theme.primary,
|
|
1512
|
+
cellColor: theme.success
|
|
1513
|
+
},
|
|
1514
|
+
{
|
|
1515
|
+
header: "Address",
|
|
1516
|
+
accessor: "displayAddress",
|
|
1517
|
+
headerColor: theme.primary,
|
|
1518
|
+
cellColor: theme.muted,
|
|
1519
|
+
width: 20
|
|
1520
|
+
},
|
|
1521
|
+
{
|
|
1522
|
+
header: "Decimals",
|
|
1523
|
+
accessor: (row) => String(row.decimals),
|
|
1524
|
+
align: "right",
|
|
1525
|
+
headerColor: theme.primary,
|
|
1526
|
+
cellColor: theme.secondary
|
|
1527
|
+
}
|
|
1528
|
+
];
|
|
1529
|
+
return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", padding: 1, children: [
|
|
1530
|
+
/* @__PURE__ */ jsx10(Header, { compact: true, showTagline: false }),
|
|
1531
|
+
/* @__PURE__ */ jsxs10(Box10, { marginBottom: 1, children: [
|
|
1532
|
+
/* @__PURE__ */ jsxs10(Text10, { color: theme.secondary, children: [
|
|
1533
|
+
symbols.arrow,
|
|
1534
|
+
" Bridgeable Tokens on",
|
|
1535
|
+
" "
|
|
1536
|
+
] }),
|
|
1537
|
+
/* @__PURE__ */ jsx10(Text10, { color: theme.primary, bold: true, children: chainName }),
|
|
1538
|
+
/* @__PURE__ */ jsxs10(Text10, { color: theme.muted, children: [
|
|
1539
|
+
" (",
|
|
1540
|
+
tokens.length,
|
|
1541
|
+
")"
|
|
1542
|
+
] })
|
|
1543
|
+
] }),
|
|
1544
|
+
/* @__PURE__ */ jsx10(
|
|
1545
|
+
Table,
|
|
1546
|
+
{
|
|
1547
|
+
data: tokens,
|
|
1548
|
+
columns,
|
|
1549
|
+
bordered: true,
|
|
1550
|
+
borderColor: theme.border
|
|
1551
|
+
}
|
|
1552
|
+
),
|
|
1553
|
+
/* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text10, { color: theme.muted, dimColor: true, children: "Tokens that can be bridged to Hyperliquid via Mina" }) })
|
|
1554
|
+
] });
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1557
|
+
// src/commands/status.tsx
|
|
1558
|
+
import { useState as useState5, useEffect as useEffect5, useCallback as useCallback2, useMemo } from "react";
|
|
1559
|
+
import { Box as Box11, Text as Text11, useApp as useApp5 } from "ink";
|
|
1560
|
+
import {
|
|
1561
|
+
Mina as Mina2
|
|
1562
|
+
} from "@siphoyawe/mina-sdk";
|
|
1563
|
+
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1564
|
+
function formatDuration(ms) {
|
|
1565
|
+
const seconds = Math.floor(ms / 1e3);
|
|
1566
|
+
if (seconds < 60) return `${seconds}s`;
|
|
1567
|
+
const minutes = Math.floor(seconds / 60);
|
|
1568
|
+
const remainingSeconds = seconds % 60;
|
|
1569
|
+
return `${minutes}m ${remainingSeconds}s`;
|
|
1570
|
+
}
|
|
1571
|
+
function truncateHash(hash) {
|
|
1572
|
+
if (hash.length <= 16) return hash;
|
|
1573
|
+
return `${hash.slice(0, 8)}...${hash.slice(-6)}`;
|
|
1574
|
+
}
|
|
1575
|
+
function getStatusColor2(status) {
|
|
1576
|
+
switch (status) {
|
|
1577
|
+
case "completed":
|
|
1578
|
+
return theme.success;
|
|
1579
|
+
case "failed":
|
|
1580
|
+
return theme.error;
|
|
1581
|
+
case "pending":
|
|
1582
|
+
return theme.muted;
|
|
1583
|
+
case "bridging":
|
|
1584
|
+
case "depositing":
|
|
1585
|
+
case "executing":
|
|
1586
|
+
return theme.primary;
|
|
1587
|
+
default:
|
|
1588
|
+
return theme.secondary;
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
function getStepIndicator(status) {
|
|
1592
|
+
switch (status) {
|
|
1593
|
+
case "pending":
|
|
1594
|
+
return /* @__PURE__ */ jsx11(Text11, { color: theme.muted, children: symbols.pending });
|
|
1595
|
+
case "executing":
|
|
1596
|
+
return /* @__PURE__ */ jsx11(Text11, { color: theme.primary, children: /* @__PURE__ */ jsx11(Spinner, {}) });
|
|
1597
|
+
case "completed":
|
|
1598
|
+
return /* @__PURE__ */ jsx11(Text11, { color: theme.success, children: symbols.completed });
|
|
1599
|
+
case "failed":
|
|
1600
|
+
return /* @__PURE__ */ jsx11(Text11, { color: theme.error, children: symbols.failed });
|
|
1601
|
+
default:
|
|
1602
|
+
return /* @__PURE__ */ jsx11(Text11, { color: theme.muted, children: symbols.pending });
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
function calculateProgress(steps) {
|
|
1606
|
+
if (steps.length === 0) return 0;
|
|
1607
|
+
const completedSteps = steps.filter((s) => s.status === "completed").length;
|
|
1608
|
+
const executingSteps = steps.filter((s) => s.status === "executing").length;
|
|
1609
|
+
return Math.round((completedSteps + executingSteps * 0.5) / steps.length * 100);
|
|
1610
|
+
}
|
|
1611
|
+
function getCurrentStepInfo(steps) {
|
|
1612
|
+
const total = steps.length;
|
|
1613
|
+
const current = steps.findIndex((s) => s.status === "executing" || s.status === "pending");
|
|
1614
|
+
if (current === -1) {
|
|
1615
|
+
return { current: total, total };
|
|
1616
|
+
}
|
|
1617
|
+
return { current: current + 1, total };
|
|
1618
|
+
}
|
|
1619
|
+
function formatStepType(stepType) {
|
|
1620
|
+
if (!stepType) return "Unknown";
|
|
1621
|
+
const mapping = {
|
|
1622
|
+
approval: "Approval",
|
|
1623
|
+
swap: "Swap",
|
|
1624
|
+
bridge: "Bridging",
|
|
1625
|
+
deposit: "Deposit"
|
|
1626
|
+
};
|
|
1627
|
+
return mapping[stepType] || stepType.charAt(0).toUpperCase() + stepType.slice(1);
|
|
1628
|
+
}
|
|
1629
|
+
function StatusDisplay({
|
|
1630
|
+
status,
|
|
1631
|
+
txHash,
|
|
1632
|
+
elapsedTime,
|
|
1633
|
+
estimatedRemaining
|
|
1634
|
+
}) {
|
|
1635
|
+
const stepInfo = getCurrentStepInfo(status.steps);
|
|
1636
|
+
const progress = calculateProgress(status.steps);
|
|
1637
|
+
const statusLabel = status.status.charAt(0).toUpperCase() + status.status.slice(1);
|
|
1638
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
|
|
1639
|
+
/* @__PURE__ */ jsx11(Box, { bordered: true, title: "Bridge Status", padding: 1, children: /* @__PURE__ */ jsx11(
|
|
1640
|
+
KeyValue,
|
|
1641
|
+
{
|
|
1642
|
+
items: [
|
|
1643
|
+
{ key: "TX", value: truncateHash(txHash) },
|
|
1644
|
+
{ key: "Status", value: `${statusLabel} (Step ${stepInfo.current}/${stepInfo.total})` }
|
|
1645
|
+
],
|
|
1646
|
+
valueColor: getStatusColor2(status.status)
|
|
1647
|
+
}
|
|
1648
|
+
) }),
|
|
1649
|
+
/* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx11(Box, { bordered: true, padding: 1, children: /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
|
|
1650
|
+
status.steps.map((step, index) => /* @__PURE__ */ jsxs11(Box11, { children: [
|
|
1651
|
+
/* @__PURE__ */ jsx11(Box11, { width: 3, children: getStepIndicator(step.status) }),
|
|
1652
|
+
/* @__PURE__ */ jsx11(Box11, { flexGrow: 1, children: /* @__PURE__ */ jsxs11(
|
|
1653
|
+
Text11,
|
|
1654
|
+
{
|
|
1655
|
+
color: getStatusColor2(step.status),
|
|
1656
|
+
bold: step.status === "executing",
|
|
1657
|
+
dimColor: step.status === "pending",
|
|
1658
|
+
children: [
|
|
1659
|
+
formatStepType(step.stepType),
|
|
1660
|
+
step.status === "executing" && progress > 0 && ` (${progress}%)`
|
|
1661
|
+
]
|
|
1662
|
+
}
|
|
1663
|
+
) }),
|
|
1664
|
+
step.txHash && /* @__PURE__ */ jsx11(Box11, { marginLeft: 2, children: /* @__PURE__ */ jsx11(Text11, { color: theme.muted, dimColor: true, children: truncateHash(step.txHash) }) })
|
|
1665
|
+
] }, step.stepId)),
|
|
1666
|
+
status.steps.length === 0 && /* @__PURE__ */ jsx11(Text11, { color: theme.muted, children: "No steps found" })
|
|
1667
|
+
] }) }) }),
|
|
1668
|
+
/* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx11(Box, { bordered: true, padding: 1, children: /* @__PURE__ */ jsx11(
|
|
1669
|
+
KeyValue,
|
|
1670
|
+
{
|
|
1671
|
+
items: [
|
|
1672
|
+
{ key: "Elapsed", value: formatDuration(elapsedTime) },
|
|
1673
|
+
{
|
|
1674
|
+
key: "Remaining",
|
|
1675
|
+
value: estimatedRemaining !== null ? `~${formatDuration(estimatedRemaining)}` : "Calculating..."
|
|
1676
|
+
}
|
|
1677
|
+
],
|
|
1678
|
+
keyColor: theme.muted,
|
|
1679
|
+
valueColor: theme.secondary
|
|
1680
|
+
}
|
|
1681
|
+
) }) }),
|
|
1682
|
+
status.status === "failed" && /* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx11(Box, { bordered: true, borderColor: theme.error, padding: 1, children: /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
|
|
1683
|
+
/* @__PURE__ */ jsxs11(Text11, { color: theme.error, bold: true, children: [
|
|
1684
|
+
symbols.failed,
|
|
1685
|
+
" Transaction Failed"
|
|
1686
|
+
] }),
|
|
1687
|
+
status.steps.find((s) => s.status === "failed")?.error && /* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx11(Text11, { color: theme.muted, children: status.steps.find((s) => s.status === "failed")?.error }) })
|
|
1688
|
+
] }) }) }),
|
|
1689
|
+
status.status === "completed" && /* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx11(Box, { bordered: true, borderColor: theme.success, padding: 1, children: /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
|
|
1690
|
+
/* @__PURE__ */ jsxs11(Text11, { color: theme.success, bold: true, children: [
|
|
1691
|
+
symbols.completed,
|
|
1692
|
+
" Bridge Completed Successfully"
|
|
1693
|
+
] }),
|
|
1694
|
+
status.bridgeTxHash && /* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx11(
|
|
1695
|
+
KeyValue,
|
|
1696
|
+
{
|
|
1697
|
+
items: [
|
|
1698
|
+
{ key: "Bridge TX", value: truncateHash(status.bridgeTxHash) },
|
|
1699
|
+
...status.depositTxHash ? [{ key: "Deposit TX", value: truncateHash(status.depositTxHash) }] : []
|
|
1700
|
+
],
|
|
1701
|
+
keyColor: theme.muted,
|
|
1702
|
+
valueColor: theme.success
|
|
1703
|
+
}
|
|
1704
|
+
) })
|
|
1705
|
+
] }) }) })
|
|
1706
|
+
] });
|
|
1707
|
+
}
|
|
1708
|
+
function LoadingState({ txHash }) {
|
|
1709
|
+
return /* @__PURE__ */ jsx11(Box11, { flexDirection: "column", children: /* @__PURE__ */ jsx11(Box, { bordered: true, title: "Bridge Status", padding: 1, children: /* @__PURE__ */ jsx11(Box11, { children: /* @__PURE__ */ jsx11(Spinner, { text: `Looking up ${truncateHash(txHash)}...` }) }) }) });
|
|
1710
|
+
}
|
|
1711
|
+
function NotFoundState({ txHash }) {
|
|
1712
|
+
return /* @__PURE__ */ jsx11(Box11, { flexDirection: "column", children: /* @__PURE__ */ jsx11(Box, { bordered: true, borderColor: theme.warning, title: "Bridge Status", padding: 1, children: /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
|
|
1713
|
+
/* @__PURE__ */ jsxs11(Text11, { color: theme.warning, children: [
|
|
1714
|
+
symbols.pending,
|
|
1715
|
+
" Transaction not found"
|
|
1716
|
+
] }),
|
|
1717
|
+
/* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsxs11(Text11, { color: theme.muted, children: [
|
|
1718
|
+
"TX: ",
|
|
1719
|
+
truncateHash(txHash)
|
|
1720
|
+
] }) }),
|
|
1721
|
+
/* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx11(Text11, { color: theme.muted, dimColor: true, children: "The transaction may not have been initiated via this CLI, or it may have expired from the local cache." }) })
|
|
1722
|
+
] }) }) });
|
|
1723
|
+
}
|
|
1724
|
+
function ErrorState({ error }) {
|
|
1725
|
+
return /* @__PURE__ */ jsx11(Box11, { flexDirection: "column", children: /* @__PURE__ */ jsx11(Box, { bordered: true, borderColor: theme.error, title: "Error", padding: 1, children: /* @__PURE__ */ jsxs11(Text11, { color: theme.error, children: [
|
|
1726
|
+
symbols.failed,
|
|
1727
|
+
" ",
|
|
1728
|
+
error
|
|
1729
|
+
] }) }) });
|
|
1730
|
+
}
|
|
1731
|
+
function Status({ txHash, watch = false }) {
|
|
1732
|
+
const { exit } = useApp5();
|
|
1733
|
+
const [status, setStatus] = useState5(null);
|
|
1734
|
+
const [loading, setLoading] = useState5(true);
|
|
1735
|
+
const [error, setError] = useState5(null);
|
|
1736
|
+
const [notFound, setNotFound] = useState5(false);
|
|
1737
|
+
const [startTime] = useState5(Date.now());
|
|
1738
|
+
const [elapsedTime, setElapsedTime] = useState5(0);
|
|
1739
|
+
const mina = useMemo(() => new Mina2({ integrator: "mina-cli" }), []);
|
|
1740
|
+
const fetchStatus = useCallback2(async () => {
|
|
1741
|
+
try {
|
|
1742
|
+
const result = await mina.getStatus(txHash);
|
|
1743
|
+
if (result === null) {
|
|
1744
|
+
setNotFound(true);
|
|
1745
|
+
setLoading(false);
|
|
1746
|
+
if (!watch) {
|
|
1747
|
+
setTimeout(() => exit(), 100);
|
|
1748
|
+
}
|
|
1749
|
+
return;
|
|
1750
|
+
}
|
|
1751
|
+
setStatus(result);
|
|
1752
|
+
setNotFound(false);
|
|
1753
|
+
setLoading(false);
|
|
1754
|
+
if (!watch && (result.status === "completed" || result.status === "failed")) {
|
|
1755
|
+
setTimeout(() => exit(), 100);
|
|
1756
|
+
}
|
|
1757
|
+
} catch (err) {
|
|
1758
|
+
setError(err instanceof Error ? err.message : "Failed to fetch status");
|
|
1759
|
+
setLoading(false);
|
|
1760
|
+
if (!watch) {
|
|
1761
|
+
setTimeout(() => exit(), 100);
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
}, [txHash, watch, exit, mina]);
|
|
1765
|
+
useEffect5(() => {
|
|
1766
|
+
fetchStatus();
|
|
1767
|
+
}, [fetchStatus]);
|
|
1768
|
+
useEffect5(() => {
|
|
1769
|
+
if (!watch) return;
|
|
1770
|
+
const interval = setInterval(() => {
|
|
1771
|
+
fetchStatus();
|
|
1772
|
+
}, 5e3);
|
|
1773
|
+
return () => clearInterval(interval);
|
|
1774
|
+
}, [watch, fetchStatus]);
|
|
1775
|
+
useEffect5(() => {
|
|
1776
|
+
const interval = setInterval(() => {
|
|
1777
|
+
setElapsedTime(Date.now() - (status?.createdAt || startTime));
|
|
1778
|
+
}, 1e3);
|
|
1779
|
+
return () => clearInterval(interval);
|
|
1780
|
+
}, [status?.createdAt, startTime]);
|
|
1781
|
+
const estimatedRemaining = status ? (() => {
|
|
1782
|
+
const progress = calculateProgress(status.steps);
|
|
1783
|
+
if (progress === 0) return null;
|
|
1784
|
+
if (progress >= 100) return 0;
|
|
1785
|
+
const elapsed = elapsedTime;
|
|
1786
|
+
const totalEstimated = elapsed / progress * 100;
|
|
1787
|
+
return Math.max(0, totalEstimated - elapsed);
|
|
1788
|
+
})() : null;
|
|
1789
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", padding: 1, children: [
|
|
1790
|
+
/* @__PURE__ */ jsx11(Header, { showTagline: false }),
|
|
1791
|
+
watch && /* @__PURE__ */ jsx11(Box11, { marginBottom: 1, children: /* @__PURE__ */ jsxs11(Text11, { color: theme.primary, children: [
|
|
1792
|
+
/* @__PURE__ */ jsx11(Spinner, {}),
|
|
1793
|
+
" Watching for updates (polling every 5s)"
|
|
1794
|
+
] }) }),
|
|
1795
|
+
/* @__PURE__ */ jsxs11(Box11, { marginTop: 1, children: [
|
|
1796
|
+
loading && /* @__PURE__ */ jsx11(LoadingState, { txHash }),
|
|
1797
|
+
error && /* @__PURE__ */ jsx11(ErrorState, { error }),
|
|
1798
|
+
notFound && !loading && /* @__PURE__ */ jsx11(NotFoundState, { txHash }),
|
|
1799
|
+
status && !loading && !error && /* @__PURE__ */ jsx11(
|
|
1800
|
+
StatusDisplay,
|
|
1801
|
+
{
|
|
1802
|
+
status,
|
|
1803
|
+
txHash,
|
|
1804
|
+
elapsedTime,
|
|
1805
|
+
estimatedRemaining
|
|
1806
|
+
}
|
|
1807
|
+
)
|
|
1808
|
+
] }),
|
|
1809
|
+
/* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsx11(Text11, { color: theme.muted, dimColor: true, children: watch ? "Ctrl+C to stop watching" : "Press any key to exit" }) })
|
|
1810
|
+
] });
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
// src/commands/bridge.tsx
|
|
1814
|
+
import { useState as useState6, useEffect as useEffect6, useCallback as useCallback3 } from "react";
|
|
1815
|
+
import { Box as Box12, Text as Text12, useApp as useApp6, useInput as useInput2 } from "ink";
|
|
1816
|
+
import {
|
|
1817
|
+
Mina as Mina3,
|
|
1818
|
+
getChains as getChains5,
|
|
1819
|
+
getBridgeableTokens as getBridgeableTokens3,
|
|
1820
|
+
getQuote,
|
|
1821
|
+
HYPEREVM_CHAIN_ID as HYPEREVM_CHAIN_ID4,
|
|
1822
|
+
HYPEREVM_USDC_ADDRESS as HYPEREVM_USDC_ADDRESS2,
|
|
1823
|
+
normalizeError,
|
|
1824
|
+
isRecoverableError,
|
|
1825
|
+
RECOVERY_ACTIONS
|
|
1826
|
+
} from "@siphoyawe/mina-sdk";
|
|
1827
|
+
|
|
1828
|
+
// src/lib/wallet.ts
|
|
1829
|
+
import fs from "fs";
|
|
1830
|
+
import readline from "readline";
|
|
1831
|
+
async function loadPrivateKey(path3) {
|
|
1832
|
+
if (path3) {
|
|
1833
|
+
if (!fs.existsSync(path3)) {
|
|
1834
|
+
throw new Error(`Key file not found: ${path3}`);
|
|
1835
|
+
}
|
|
1836
|
+
const content = fs.readFileSync(path3, "utf-8");
|
|
1837
|
+
try {
|
|
1838
|
+
const json = JSON.parse(content);
|
|
1839
|
+
const key = json.privateKey || json.private_key || json.key;
|
|
1840
|
+
if (key) {
|
|
1841
|
+
return normalizePrivateKey(key);
|
|
1842
|
+
}
|
|
1843
|
+
return normalizePrivateKey(content.trim());
|
|
1844
|
+
} catch {
|
|
1845
|
+
return normalizePrivateKey(content.trim());
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
return promptForPrivateKey();
|
|
1849
|
+
}
|
|
1850
|
+
function promptForPrivateKey() {
|
|
1851
|
+
return new Promise((resolve, reject) => {
|
|
1852
|
+
const rl = readline.createInterface({
|
|
1853
|
+
input: process.stdin,
|
|
1854
|
+
output: process.stdout
|
|
1855
|
+
});
|
|
1856
|
+
process.stdout.write("Enter private key (input will be visible): ");
|
|
1857
|
+
rl.on("line", (answer) => {
|
|
1858
|
+
rl.close();
|
|
1859
|
+
try {
|
|
1860
|
+
resolve(normalizePrivateKey(answer.trim()));
|
|
1861
|
+
} catch (err) {
|
|
1862
|
+
reject(err);
|
|
1863
|
+
}
|
|
1864
|
+
});
|
|
1865
|
+
rl.on("close", () => {
|
|
1866
|
+
});
|
|
1867
|
+
});
|
|
1868
|
+
}
|
|
1869
|
+
function normalizePrivateKey(key) {
|
|
1870
|
+
const trimmed = key.trim();
|
|
1871
|
+
const keyWithoutPrefix = trimmed.startsWith("0x") ? trimmed.slice(2) : trimmed;
|
|
1872
|
+
if (!/^[0-9a-fA-F]{64}$/.test(keyWithoutPrefix)) {
|
|
1873
|
+
throw new Error("Invalid private key format. Expected 64 hex characters.");
|
|
1874
|
+
}
|
|
1875
|
+
return trimmed.startsWith("0x") ? trimmed : `0x${trimmed}`;
|
|
1876
|
+
}
|
|
1877
|
+
async function getAddressFromPrivateKey(privateKey) {
|
|
1878
|
+
const { privateKeyToAccount } = await import("viem/accounts");
|
|
1879
|
+
const account = privateKeyToAccount(privateKey);
|
|
1880
|
+
return account.address;
|
|
1881
|
+
}
|
|
1882
|
+
async function createSigner(privateKey, chainId, rpcUrl) {
|
|
1883
|
+
const { privateKeyToAccount } = await import("viem/accounts");
|
|
1884
|
+
const { createWalletClient, http } = await import("viem");
|
|
1885
|
+
const { arbitrum, mainnet, optimism, polygon, base, avalanche, bsc } = await import("viem/chains");
|
|
1886
|
+
const chainMap = {
|
|
1887
|
+
1: mainnet,
|
|
1888
|
+
42161: arbitrum,
|
|
1889
|
+
10: optimism,
|
|
1890
|
+
137: polygon,
|
|
1891
|
+
8453: base,
|
|
1892
|
+
43114: avalanche,
|
|
1893
|
+
56: bsc,
|
|
1894
|
+
// HyperEVM
|
|
1895
|
+
999: {
|
|
1896
|
+
id: 999,
|
|
1897
|
+
name: "HyperEVM",
|
|
1898
|
+
nativeCurrency: { name: "ETH", symbol: "ETH", decimals: 18 },
|
|
1899
|
+
rpcUrls: {
|
|
1900
|
+
default: { http: ["https://rpc.hyperliquid.xyz/evm"] }
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
};
|
|
1904
|
+
const chain = chainMap[chainId];
|
|
1905
|
+
if (!chain) {
|
|
1906
|
+
throw new Error(`Unsupported chain ID: ${chainId}. Supported: ${Object.keys(chainMap).join(", ")}`);
|
|
1907
|
+
}
|
|
1908
|
+
const account = privateKeyToAccount(privateKey);
|
|
1909
|
+
const transport = rpcUrl ? http(rpcUrl) : http();
|
|
1910
|
+
const walletClient = createWalletClient({
|
|
1911
|
+
account,
|
|
1912
|
+
chain,
|
|
1913
|
+
transport
|
|
1914
|
+
});
|
|
1915
|
+
return {
|
|
1916
|
+
sendTransaction: async (request) => {
|
|
1917
|
+
const txHash = await walletClient.sendTransaction({
|
|
1918
|
+
to: request.to,
|
|
1919
|
+
data: request.data,
|
|
1920
|
+
value: BigInt(request.value || "0"),
|
|
1921
|
+
gas: request.gasLimit ? BigInt(request.gasLimit) : void 0,
|
|
1922
|
+
gasPrice: request.gasPrice ? BigInt(request.gasPrice) : void 0,
|
|
1923
|
+
chain
|
|
1924
|
+
});
|
|
1925
|
+
return txHash;
|
|
1926
|
+
},
|
|
1927
|
+
getAddress: async () => {
|
|
1928
|
+
return account.address;
|
|
1929
|
+
},
|
|
1930
|
+
getChainId: async () => {
|
|
1931
|
+
return chainId;
|
|
1932
|
+
}
|
|
1933
|
+
};
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
// src/commands/bridge.tsx
|
|
1937
|
+
import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1938
|
+
function ErrorDisplay2({
|
|
1939
|
+
error,
|
|
1940
|
+
recoveryAction
|
|
1941
|
+
}) {
|
|
1942
|
+
const normalizedError = normalizeError(error);
|
|
1943
|
+
const isRecoverable = isRecoverableError(normalizedError);
|
|
1944
|
+
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", marginTop: 1, children: [
|
|
1945
|
+
/* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsxs12(Text12, { color: theme.error, bold: true, children: [
|
|
1946
|
+
symbols.failed,
|
|
1947
|
+
" Error: ",
|
|
1948
|
+
normalizedError.message
|
|
1949
|
+
] }) }),
|
|
1950
|
+
recoveryAction && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsxs12(Text12, { color: theme.warning, children: [
|
|
1951
|
+
symbols.arrow,
|
|
1952
|
+
" Recovery suggestion: ",
|
|
1953
|
+
recoveryAction
|
|
1954
|
+
] }) }),
|
|
1955
|
+
isRecoverable && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text12, { color: theme.muted, dimColor: true, children: "This error may be temporary. Try again in a few moments." }) })
|
|
1956
|
+
] });
|
|
1957
|
+
}
|
|
1958
|
+
function QuoteDisplay2({
|
|
1959
|
+
quote,
|
|
1960
|
+
chain,
|
|
1961
|
+
token,
|
|
1962
|
+
amount
|
|
1963
|
+
}) {
|
|
1964
|
+
const estimatedMinutes = Math.ceil(quote.estimatedTime / 60);
|
|
1965
|
+
const outputFormatted = (Number(quote.toAmount) / Math.pow(10, quote.toToken.decimals)).toFixed(2);
|
|
1966
|
+
return /* @__PURE__ */ jsxs12(Box, { bordered: true, title: "Bridge Quote", padding: 1, children: [
|
|
1967
|
+
/* @__PURE__ */ jsx12(
|
|
1968
|
+
KeyValue,
|
|
1969
|
+
{
|
|
1970
|
+
items: [
|
|
1971
|
+
{ key: "From Chain", value: chain.name },
|
|
1972
|
+
{ key: "Token", value: `${amount} ${token.symbol}` },
|
|
1973
|
+
{ key: "To Chain", value: "HyperEVM" },
|
|
1974
|
+
{ key: "You Receive", value: `~${outputFormatted} ${quote.toToken.symbol}` }
|
|
1975
|
+
]
|
|
1976
|
+
}
|
|
1977
|
+
),
|
|
1978
|
+
/* @__PURE__ */ jsx12(Box12, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsx12(Divider, { width: 40 }) }),
|
|
1979
|
+
/* @__PURE__ */ jsx12(
|
|
1980
|
+
KeyValue,
|
|
1981
|
+
{
|
|
1982
|
+
items: [
|
|
1983
|
+
{ key: "Gas Fee", value: `$${quote.fees.gasUsd.toFixed(2)}` },
|
|
1984
|
+
{ key: "Bridge Fee", value: `$${quote.fees.bridgeFeeUsd.toFixed(2)}` },
|
|
1985
|
+
{ key: "Total Fees", value: `$${quote.fees.totalUsd.toFixed(2)}` },
|
|
1986
|
+
{ key: "Est. Time", value: `~${estimatedMinutes} min` }
|
|
1987
|
+
],
|
|
1988
|
+
keyColor: theme.muted,
|
|
1989
|
+
valueColor: theme.warning
|
|
1990
|
+
}
|
|
1991
|
+
),
|
|
1992
|
+
quote.highImpact && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsxs12(Text12, { color: theme.warning, children: [
|
|
1993
|
+
symbols.pending,
|
|
1994
|
+
" Warning: High price impact (",
|
|
1995
|
+
(quote.priceImpact * 100).toFixed(2),
|
|
1996
|
+
"%)"
|
|
1997
|
+
] }) }),
|
|
1998
|
+
quote.includesAutoDeposit && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsxs12(Text12, { color: theme.success, dimColor: true, children: [
|
|
1999
|
+
symbols.check,
|
|
2000
|
+
" Auto-deposit to Hyperliquid L1 included"
|
|
2001
|
+
] }) })
|
|
2002
|
+
] });
|
|
2003
|
+
}
|
|
2004
|
+
function BridgeProgressDisplay({
|
|
2005
|
+
stepState,
|
|
2006
|
+
error,
|
|
2007
|
+
currentStepDescription
|
|
2008
|
+
}) {
|
|
2009
|
+
const steps = [
|
|
2010
|
+
{
|
|
2011
|
+
label: "Loading wallet & chain data",
|
|
2012
|
+
status: stepState.loading
|
|
2013
|
+
},
|
|
2014
|
+
{
|
|
2015
|
+
label: "Fetching bridge quote",
|
|
2016
|
+
status: stepState.quote
|
|
2017
|
+
},
|
|
2018
|
+
{
|
|
2019
|
+
label: "Approving token spend",
|
|
2020
|
+
status: stepState.approval,
|
|
2021
|
+
description: stepState.approval === "active" ? "Waiting for approval transaction..." : void 0
|
|
2022
|
+
},
|
|
2023
|
+
{
|
|
2024
|
+
label: "Executing bridge transaction",
|
|
2025
|
+
status: stepState.bridge,
|
|
2026
|
+
description: stepState.bridge === "active" ? "Waiting for bridge transaction..." : void 0
|
|
2027
|
+
},
|
|
2028
|
+
{
|
|
2029
|
+
label: "Confirming on destination",
|
|
2030
|
+
status: stepState.confirm,
|
|
2031
|
+
description: stepState.confirm === "active" ? currentStepDescription : error
|
|
2032
|
+
}
|
|
2033
|
+
];
|
|
2034
|
+
if (error) {
|
|
2035
|
+
const activeIndex = steps.findIndex((s) => s.status === "active" || s.status === "failed");
|
|
2036
|
+
if (activeIndex >= 0) {
|
|
2037
|
+
const step = steps[activeIndex];
|
|
2038
|
+
if (step) {
|
|
2039
|
+
step.status = "failed";
|
|
2040
|
+
step.description = error;
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
return /* @__PURE__ */ jsx12(ProgressSteps, { steps, title: "Bridge Progress", showNumbers: true });
|
|
2045
|
+
}
|
|
2046
|
+
function SuccessDisplay({
|
|
2047
|
+
result,
|
|
2048
|
+
chain,
|
|
2049
|
+
token,
|
|
2050
|
+
amount
|
|
2051
|
+
}) {
|
|
2052
|
+
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", marginTop: 1, children: [
|
|
2053
|
+
/* @__PURE__ */ jsx12(Box12, { children: /* @__PURE__ */ jsxs12(Text12, { color: theme.success, bold: true, children: [
|
|
2054
|
+
symbols.check,
|
|
2055
|
+
" Bridge completed successfully!"
|
|
2056
|
+
] }) }),
|
|
2057
|
+
/* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx12(Box, { bordered: true, padding: 1, children: /* @__PURE__ */ jsx12(
|
|
2058
|
+
KeyValue,
|
|
2059
|
+
{
|
|
2060
|
+
items: [
|
|
2061
|
+
{ key: "Status", value: "Success" },
|
|
2062
|
+
{ key: "From", value: `${amount} ${token.symbol} on ${chain.name}` },
|
|
2063
|
+
{ key: "To", value: "USDC on HyperEVM" },
|
|
2064
|
+
...result.txHash ? [{ key: "Bridge TX", value: result.txHash.slice(0, 20) + "..." }] : [],
|
|
2065
|
+
...result.depositTxHash ? [{ key: "Deposit TX", value: result.depositTxHash.slice(0, 20) + "..." }] : []
|
|
2066
|
+
],
|
|
2067
|
+
valueColor: theme.success
|
|
2068
|
+
}
|
|
2069
|
+
) }) }),
|
|
2070
|
+
/* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text12, { color: theme.muted, dimColor: true, children: "Your funds are now available on HyperEVM. Check your Hyperliquid balance." }) })
|
|
2071
|
+
] });
|
|
2072
|
+
}
|
|
2073
|
+
function Bridge({ options }) {
|
|
2074
|
+
const { exit } = useApp6();
|
|
2075
|
+
const [state, setState] = useState6("loading");
|
|
2076
|
+
const [chain, setChain] = useState6(null);
|
|
2077
|
+
const [token, setToken] = useState6(null);
|
|
2078
|
+
const [quote, setQuote] = useState6(null);
|
|
2079
|
+
const [walletAddress, setWalletAddress] = useState6(null);
|
|
2080
|
+
const [privateKey, setPrivateKey] = useState6(null);
|
|
2081
|
+
const [error, setError] = useState6(null);
|
|
2082
|
+
const [recoveryAction, setRecoveryAction] = useState6(null);
|
|
2083
|
+
const [result, setResult] = useState6(null);
|
|
2084
|
+
const [currentStepDescription, setCurrentStepDescription] = useState6("");
|
|
2085
|
+
const [stepState, setStepState] = useState6({
|
|
2086
|
+
loading: "active",
|
|
2087
|
+
quote: "pending",
|
|
2088
|
+
approval: "pending",
|
|
2089
|
+
bridge: "pending",
|
|
2090
|
+
confirm: "pending"
|
|
2091
|
+
});
|
|
2092
|
+
useInput2((input, key) => {
|
|
2093
|
+
if (state === "confirming") {
|
|
2094
|
+
if (key.return || input === "y" || input === "Y") {
|
|
2095
|
+
executeBridge();
|
|
2096
|
+
} else if (input === "n" || input === "N" || input === "q") {
|
|
2097
|
+
exit();
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
if (state === "completed" || state === "failed") {
|
|
2101
|
+
if (key.return || input === "q") {
|
|
2102
|
+
exit();
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
});
|
|
2106
|
+
useEffect6(() => {
|
|
2107
|
+
loadInitialData();
|
|
2108
|
+
}, []);
|
|
2109
|
+
const loadInitialData = async () => {
|
|
2110
|
+
try {
|
|
2111
|
+
setStepState((prev) => ({ ...prev, loading: "active" }));
|
|
2112
|
+
const key = await loadPrivateKey(options.key);
|
|
2113
|
+
setPrivateKey(key);
|
|
2114
|
+
const address = await getAddressFromPrivateKey(key);
|
|
2115
|
+
setWalletAddress(address);
|
|
2116
|
+
const chainsResponse = await getChains5();
|
|
2117
|
+
const foundChain = chainsResponse.chains.find(
|
|
2118
|
+
(c) => c.key.toLowerCase() === options.from.toLowerCase() || c.name.toLowerCase() === options.from.toLowerCase() || c.id.toString() === options.from
|
|
2119
|
+
);
|
|
2120
|
+
if (!foundChain) {
|
|
2121
|
+
throw new Error(`Chain not found: ${options.from}. Available chains: ${chainsResponse.chains.map((c) => c.key).join(", ")}`);
|
|
2122
|
+
}
|
|
2123
|
+
setChain(foundChain);
|
|
2124
|
+
const tokensResponse = await getBridgeableTokens3(foundChain.id);
|
|
2125
|
+
const foundToken = tokensResponse.tokens.find(
|
|
2126
|
+
(t) => t.symbol.toLowerCase() === options.token.toLowerCase() || t.address.toLowerCase() === options.token.toLowerCase()
|
|
2127
|
+
);
|
|
2128
|
+
if (!foundToken) {
|
|
2129
|
+
throw new Error(`Token not found: ${options.token}. Available tokens: ${tokensResponse.tokens.map((t) => t.symbol).join(", ")}`);
|
|
2130
|
+
}
|
|
2131
|
+
setToken(foundToken);
|
|
2132
|
+
setStepState((prev) => ({ ...prev, loading: "completed", quote: "active" }));
|
|
2133
|
+
const amountInSmallestUnit = (parseFloat(options.amount) * Math.pow(10, foundToken.decimals)).toString();
|
|
2134
|
+
const quoteResult = await getQuote({
|
|
2135
|
+
fromChainId: foundChain.id,
|
|
2136
|
+
toChainId: HYPEREVM_CHAIN_ID4,
|
|
2137
|
+
fromToken: foundToken.address,
|
|
2138
|
+
toToken: HYPEREVM_USDC_ADDRESS2,
|
|
2139
|
+
fromAmount: amountInSmallestUnit,
|
|
2140
|
+
fromAddress: address
|
|
2141
|
+
});
|
|
2142
|
+
setQuote(quoteResult);
|
|
2143
|
+
setStepState((prev) => ({ ...prev, quote: "completed" }));
|
|
2144
|
+
setState("loaded");
|
|
2145
|
+
if (options.yes) {
|
|
2146
|
+
setState("confirming");
|
|
2147
|
+
setTimeout(() => executeBridge(), 500);
|
|
2148
|
+
} else {
|
|
2149
|
+
setState("confirming");
|
|
2150
|
+
}
|
|
2151
|
+
} catch (err) {
|
|
2152
|
+
const normalizedError = normalizeError(err instanceof Error ? err : new Error(String(err)));
|
|
2153
|
+
setError(normalizedError);
|
|
2154
|
+
const action = RECOVERY_ACTIONS[normalizedError.code];
|
|
2155
|
+
if (action) {
|
|
2156
|
+
setRecoveryAction(action);
|
|
2157
|
+
}
|
|
2158
|
+
setStepState((prev) => ({
|
|
2159
|
+
...prev,
|
|
2160
|
+
loading: prev.loading === "active" ? "failed" : prev.loading,
|
|
2161
|
+
quote: prev.quote === "active" ? "failed" : prev.quote
|
|
2162
|
+
}));
|
|
2163
|
+
setState("failed");
|
|
2164
|
+
}
|
|
2165
|
+
};
|
|
2166
|
+
const executeBridge = useCallback3(async () => {
|
|
2167
|
+
if (!quote || !chain || !token || !privateKey || !walletAddress) return;
|
|
2168
|
+
try {
|
|
2169
|
+
setState("executing");
|
|
2170
|
+
setStepState((prev) => ({ ...prev, approval: "active" }));
|
|
2171
|
+
const signer = await createSigner(privateKey, chain.id);
|
|
2172
|
+
const mina = new Mina3({
|
|
2173
|
+
integrator: "mina-cli",
|
|
2174
|
+
autoDeposit: options.autoDeposit !== false
|
|
2175
|
+
});
|
|
2176
|
+
const executeResult = await mina.execute({
|
|
2177
|
+
quote,
|
|
2178
|
+
signer,
|
|
2179
|
+
onStepChange: (stepStatus) => {
|
|
2180
|
+
setCurrentStepDescription(`${stepStatus.step}: ${stepStatus.status}`);
|
|
2181
|
+
if (stepStatus.step === "approval") {
|
|
2182
|
+
if (stepStatus.status === "completed") {
|
|
2183
|
+
setStepState((prev) => ({ ...prev, approval: "completed", bridge: "active" }));
|
|
2184
|
+
} else if (stepStatus.status === "failed") {
|
|
2185
|
+
setStepState((prev) => ({ ...prev, approval: "failed" }));
|
|
2186
|
+
}
|
|
2187
|
+
} else if (stepStatus.step === "bridge" || stepStatus.step === "swap") {
|
|
2188
|
+
if (stepStatus.status === "completed") {
|
|
2189
|
+
setStepState((prev) => ({ ...prev, bridge: "completed", confirm: "active" }));
|
|
2190
|
+
} else if (stepStatus.status === "failed") {
|
|
2191
|
+
setStepState((prev) => ({ ...prev, bridge: "failed" }));
|
|
2192
|
+
}
|
|
2193
|
+
} else if (stepStatus.step === "deposit") {
|
|
2194
|
+
if (stepStatus.status === "completed") {
|
|
2195
|
+
setStepState((prev) => ({ ...prev, confirm: "completed" }));
|
|
2196
|
+
} else if (stepStatus.status === "failed") {
|
|
2197
|
+
setStepState((prev) => ({ ...prev, confirm: "failed" }));
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
},
|
|
2201
|
+
onStatusChange: (status) => {
|
|
2202
|
+
setCurrentStepDescription(status.substatus || `Step ${status.currentStep}/${status.totalSteps}`);
|
|
2203
|
+
}
|
|
2204
|
+
});
|
|
2205
|
+
setResult(executeResult);
|
|
2206
|
+
if (executeResult.status === "completed") {
|
|
2207
|
+
setStepState((prev) => ({
|
|
2208
|
+
...prev,
|
|
2209
|
+
approval: "completed",
|
|
2210
|
+
bridge: "completed",
|
|
2211
|
+
confirm: "completed"
|
|
2212
|
+
}));
|
|
2213
|
+
setState("completed");
|
|
2214
|
+
} else if (executeResult.status === "failed") {
|
|
2215
|
+
setError(executeResult.error || new Error("Bridge execution failed"));
|
|
2216
|
+
setState("failed");
|
|
2217
|
+
}
|
|
2218
|
+
} catch (err) {
|
|
2219
|
+
const normalizedError = normalizeError(err instanceof Error ? err : new Error(String(err)));
|
|
2220
|
+
setError(normalizedError);
|
|
2221
|
+
const action = RECOVERY_ACTIONS[normalizedError.code];
|
|
2222
|
+
if (action) {
|
|
2223
|
+
setRecoveryAction(action);
|
|
2224
|
+
}
|
|
2225
|
+
setState("failed");
|
|
2226
|
+
}
|
|
2227
|
+
}, [quote, chain, token, privateKey, walletAddress, options.autoDeposit]);
|
|
2228
|
+
return /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", padding: 1, children: [
|
|
2229
|
+
/* @__PURE__ */ jsx12(Header, { compact: true, showTagline: false }),
|
|
2230
|
+
/* @__PURE__ */ jsx12(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsxs12(Text12, { color: theme.primary, bold: true, children: [
|
|
2231
|
+
"Bridge ",
|
|
2232
|
+
options.amount,
|
|
2233
|
+
" ",
|
|
2234
|
+
options.token,
|
|
2235
|
+
" from ",
|
|
2236
|
+
options.from
|
|
2237
|
+
] }) }),
|
|
2238
|
+
walletAddress && /* @__PURE__ */ jsx12(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsxs12(Text12, { color: theme.muted, dimColor: true, children: [
|
|
2239
|
+
"Wallet: ",
|
|
2240
|
+
walletAddress.slice(0, 8),
|
|
2241
|
+
"...",
|
|
2242
|
+
walletAddress.slice(-6)
|
|
2243
|
+
] }) }),
|
|
2244
|
+
state === "loading" && /* @__PURE__ */ jsx12(Box12, { flexDirection: "column", children: /* @__PURE__ */ jsx12(BridgeProgressDisplay, { stepState }) }),
|
|
2245
|
+
state === "confirming" && quote && chain && token && /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
|
|
2246
|
+
/* @__PURE__ */ jsx12(
|
|
2247
|
+
QuoteDisplay2,
|
|
2248
|
+
{
|
|
2249
|
+
quote,
|
|
2250
|
+
chain,
|
|
2251
|
+
token,
|
|
2252
|
+
amount: options.amount
|
|
2253
|
+
}
|
|
2254
|
+
),
|
|
2255
|
+
!options.yes && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsxs12(Text12, { color: theme.warning, bold: true, children: [
|
|
2256
|
+
symbols.arrow,
|
|
2257
|
+
" Proceed with bridge? (y/n)"
|
|
2258
|
+
] }) }),
|
|
2259
|
+
options.yes && /* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx12(Spinner, { text: "Auto-confirming..." }) })
|
|
2260
|
+
] }),
|
|
2261
|
+
state === "executing" && /* @__PURE__ */ jsx12(Box12, { flexDirection: "column", children: /* @__PURE__ */ jsx12(
|
|
2262
|
+
BridgeProgressDisplay,
|
|
2263
|
+
{
|
|
2264
|
+
stepState,
|
|
2265
|
+
currentStepDescription
|
|
2266
|
+
}
|
|
2267
|
+
) }),
|
|
2268
|
+
state === "completed" && result && chain && token && /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
|
|
2269
|
+
/* @__PURE__ */ jsx12(BridgeProgressDisplay, { stepState }),
|
|
2270
|
+
/* @__PURE__ */ jsx12(
|
|
2271
|
+
SuccessDisplay,
|
|
2272
|
+
{
|
|
2273
|
+
result,
|
|
2274
|
+
chain,
|
|
2275
|
+
token,
|
|
2276
|
+
amount: options.amount
|
|
2277
|
+
}
|
|
2278
|
+
),
|
|
2279
|
+
/* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text12, { color: theme.muted, dimColor: true, children: "Press Enter or q to exit" }) })
|
|
2280
|
+
] }),
|
|
2281
|
+
state === "failed" && error && /* @__PURE__ */ jsxs12(Box12, { flexDirection: "column", children: [
|
|
2282
|
+
/* @__PURE__ */ jsx12(BridgeProgressDisplay, { stepState, error: error.message }),
|
|
2283
|
+
/* @__PURE__ */ jsx12(ErrorDisplay2, { error, recoveryAction: recoveryAction || void 0 }),
|
|
2284
|
+
/* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text12, { color: theme.muted, dimColor: true, children: "Press Enter or q to exit" }) })
|
|
2285
|
+
] })
|
|
2286
|
+
] });
|
|
2287
|
+
}
|
|
2288
|
+
async function bridgeCommand(options) {
|
|
2289
|
+
const { render: render2 } = await import("ink");
|
|
2290
|
+
const React12 = await import("react");
|
|
2291
|
+
render2(React12.createElement(Bridge, { options }));
|
|
2292
|
+
}
|
|
2293
|
+
|
|
2294
|
+
// src/commands/history.tsx
|
|
2295
|
+
import { useState as useState7, useEffect as useEffect7 } from "react";
|
|
2296
|
+
import { Box as Box13, Text as Text13, useApp as useApp7 } from "ink";
|
|
2297
|
+
|
|
2298
|
+
// src/lib/history.ts
|
|
2299
|
+
import os from "os";
|
|
2300
|
+
import fs2 from "fs";
|
|
2301
|
+
import path from "path";
|
|
2302
|
+
var HISTORY_PATH = path.join(os.homedir(), ".mina", "history.json");
|
|
2303
|
+
function ensureDir() {
|
|
2304
|
+
const dir = path.dirname(HISTORY_PATH);
|
|
2305
|
+
if (!fs2.existsSync(dir)) {
|
|
2306
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
function getHistory(limit = 10, address) {
|
|
2310
|
+
ensureDir();
|
|
2311
|
+
if (!fs2.existsSync(HISTORY_PATH)) {
|
|
2312
|
+
return [];
|
|
2313
|
+
}
|
|
2314
|
+
try {
|
|
2315
|
+
const data = JSON.parse(fs2.readFileSync(HISTORY_PATH, "utf-8"));
|
|
2316
|
+
let entries = data.entries || [];
|
|
2317
|
+
if (address) {
|
|
2318
|
+
entries = entries.filter(
|
|
2319
|
+
(e) => e.walletAddress?.toLowerCase() === address.toLowerCase()
|
|
2320
|
+
);
|
|
2321
|
+
}
|
|
2322
|
+
return entries.slice(-limit).reverse();
|
|
2323
|
+
} catch {
|
|
2324
|
+
return [];
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
|
|
2328
|
+
// src/commands/history.tsx
|
|
2329
|
+
import { Fragment, jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2330
|
+
function formatDate(timestamp) {
|
|
2331
|
+
return new Date(timestamp).toLocaleString("en-US", {
|
|
2332
|
+
month: "2-digit",
|
|
2333
|
+
day: "2-digit",
|
|
2334
|
+
hour: "2-digit",
|
|
2335
|
+
minute: "2-digit",
|
|
2336
|
+
hour12: false
|
|
2337
|
+
});
|
|
2338
|
+
}
|
|
2339
|
+
function truncateHash2(hash) {
|
|
2340
|
+
if (hash.length <= 16) return hash;
|
|
2341
|
+
return `${hash.slice(0, 8)}...${hash.slice(-6)}`;
|
|
2342
|
+
}
|
|
2343
|
+
function getStatusDisplay(status) {
|
|
2344
|
+
switch (status) {
|
|
2345
|
+
case "completed":
|
|
2346
|
+
return { icon: symbols.completed, color: theme.success, label: "Complete" };
|
|
2347
|
+
case "pending":
|
|
2348
|
+
return { icon: symbols.pending, color: theme.warning, label: "Pending" };
|
|
2349
|
+
case "failed":
|
|
2350
|
+
return { icon: symbols.failed, color: theme.error, label: "Failed" };
|
|
2351
|
+
default:
|
|
2352
|
+
return { icon: symbols.pending, color: theme.muted, label: "Unknown" };
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
function transformToRows(entries) {
|
|
2356
|
+
return entries.map((entry) => {
|
|
2357
|
+
const { icon, color, label } = getStatusDisplay(entry.status);
|
|
2358
|
+
return {
|
|
2359
|
+
date: formatDate(entry.timestamp),
|
|
2360
|
+
route: `${entry.fromChain} ${symbols.arrow} ${entry.toChain}`,
|
|
2361
|
+
amount: `${entry.amount} ${entry.token}`,
|
|
2362
|
+
status: `${icon} ${label}`,
|
|
2363
|
+
txHash: entry.txHash,
|
|
2364
|
+
statusColor: color
|
|
2365
|
+
};
|
|
2366
|
+
});
|
|
2367
|
+
}
|
|
2368
|
+
function EmptyState({ address }) {
|
|
2369
|
+
return /* @__PURE__ */ jsx13(Box13, { flexDirection: "column", children: /* @__PURE__ */ jsx13(
|
|
2370
|
+
Box13,
|
|
2371
|
+
{
|
|
2372
|
+
borderStyle: "round",
|
|
2373
|
+
borderColor: theme.border,
|
|
2374
|
+
paddingX: 2,
|
|
2375
|
+
paddingY: 1,
|
|
2376
|
+
children: /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
|
|
2377
|
+
/* @__PURE__ */ jsxs13(Text13, { color: theme.muted, children: [
|
|
2378
|
+
symbols.pending,
|
|
2379
|
+
" No bridge history found"
|
|
2380
|
+
] }),
|
|
2381
|
+
/* @__PURE__ */ jsx13(Box13, { marginTop: 1, children: /* @__PURE__ */ jsx13(Text13, { color: theme.muted, dimColor: true, children: address ? `No transactions found for address ${truncateHash2(address)}` : "Start by running a bridge transaction with: mina bridge --from <chain> --token USDC --amount 100" }) })
|
|
2382
|
+
] })
|
|
2383
|
+
}
|
|
2384
|
+
) });
|
|
2385
|
+
}
|
|
2386
|
+
function HistoryCommand({
|
|
2387
|
+
limit,
|
|
2388
|
+
address,
|
|
2389
|
+
json = false
|
|
2390
|
+
}) {
|
|
2391
|
+
const { exit } = useApp7();
|
|
2392
|
+
const [entries, setEntries] = useState7([]);
|
|
2393
|
+
const [loading, setLoading] = useState7(true);
|
|
2394
|
+
useEffect7(() => {
|
|
2395
|
+
const history = getHistory(limit, address);
|
|
2396
|
+
setEntries(history);
|
|
2397
|
+
setLoading(false);
|
|
2398
|
+
}, [limit, address]);
|
|
2399
|
+
useEffect7(() => {
|
|
2400
|
+
if (!loading && json) {
|
|
2401
|
+
setTimeout(() => exit(), 100);
|
|
2402
|
+
}
|
|
2403
|
+
}, [loading, json, exit]);
|
|
2404
|
+
if (json) {
|
|
2405
|
+
if (loading) return null;
|
|
2406
|
+
console.log(JSON.stringify(entries, null, 2));
|
|
2407
|
+
return null;
|
|
2408
|
+
}
|
|
2409
|
+
if (loading) {
|
|
2410
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", padding: 1, children: [
|
|
2411
|
+
/* @__PURE__ */ jsx13(Header, { compact: true, showTagline: false }),
|
|
2412
|
+
/* @__PURE__ */ jsx13(Text13, { color: theme.muted, children: "Loading history..." })
|
|
2413
|
+
] });
|
|
2414
|
+
}
|
|
2415
|
+
const rows = transformToRows(entries);
|
|
2416
|
+
const columns = [
|
|
2417
|
+
{
|
|
2418
|
+
header: "Date",
|
|
2419
|
+
accessor: "date",
|
|
2420
|
+
headerColor: theme.primary,
|
|
2421
|
+
cellColor: theme.secondary
|
|
2422
|
+
},
|
|
2423
|
+
{
|
|
2424
|
+
header: "Route",
|
|
2425
|
+
accessor: "route",
|
|
2426
|
+
headerColor: theme.primary,
|
|
2427
|
+
cellColor: theme.info
|
|
2428
|
+
},
|
|
2429
|
+
{
|
|
2430
|
+
header: "Amount",
|
|
2431
|
+
accessor: "amount",
|
|
2432
|
+
align: "right",
|
|
2433
|
+
headerColor: theme.primary,
|
|
2434
|
+
cellColor: theme.secondary
|
|
2435
|
+
},
|
|
2436
|
+
{
|
|
2437
|
+
header: "Status",
|
|
2438
|
+
accessor: "status",
|
|
2439
|
+
headerColor: theme.primary,
|
|
2440
|
+
cellColor: (_, row) => row.statusColor
|
|
2441
|
+
}
|
|
2442
|
+
];
|
|
2443
|
+
return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", padding: 1, children: [
|
|
2444
|
+
/* @__PURE__ */ jsx13(Header, { compact: true, showTagline: false }),
|
|
2445
|
+
/* @__PURE__ */ jsx13(Box13, { marginBottom: 1, children: /* @__PURE__ */ jsxs13(Text13, { color: theme.secondary, children: [
|
|
2446
|
+
symbols.arrow,
|
|
2447
|
+
" Bridge History",
|
|
2448
|
+
address && /* @__PURE__ */ jsxs13(Text13, { color: theme.muted, children: [
|
|
2449
|
+
" (",
|
|
2450
|
+
truncateHash2(address),
|
|
2451
|
+
")"
|
|
2452
|
+
] }),
|
|
2453
|
+
entries.length > 0 && /* @__PURE__ */ jsxs13(Text13, { color: theme.muted, children: [
|
|
2454
|
+
" - ",
|
|
2455
|
+
entries.length,
|
|
2456
|
+
" transaction",
|
|
2457
|
+
entries.length !== 1 ? "s" : ""
|
|
2458
|
+
] })
|
|
2459
|
+
] }) }),
|
|
2460
|
+
entries.length === 0 ? /* @__PURE__ */ jsx13(EmptyState, { address }) : /* @__PURE__ */ jsxs13(Fragment, { children: [
|
|
2461
|
+
/* @__PURE__ */ jsx13(
|
|
2462
|
+
Table,
|
|
2463
|
+
{
|
|
2464
|
+
data: rows,
|
|
2465
|
+
columns,
|
|
2466
|
+
bordered: true,
|
|
2467
|
+
borderColor: theme.border
|
|
2468
|
+
}
|
|
2469
|
+
),
|
|
2470
|
+
/* @__PURE__ */ jsxs13(Box13, { marginTop: 1, flexDirection: "column", children: [
|
|
2471
|
+
/* @__PURE__ */ jsxs13(Text13, { color: theme.muted, dimColor: true, children: [
|
|
2472
|
+
"Use ",
|
|
2473
|
+
'"',
|
|
2474
|
+
"mina status ",
|
|
2475
|
+
"<",
|
|
2476
|
+
"hash",
|
|
2477
|
+
'>"',
|
|
2478
|
+
" for full transaction details"
|
|
2479
|
+
] }),
|
|
2480
|
+
entries.length >= limit && /* @__PURE__ */ jsxs13(Text13, { color: theme.muted, dimColor: true, children: [
|
|
2481
|
+
"Showing last ",
|
|
2482
|
+
limit,
|
|
2483
|
+
" entries. Use --limit to see more."
|
|
2484
|
+
] })
|
|
2485
|
+
] })
|
|
2486
|
+
] })
|
|
2487
|
+
] });
|
|
2488
|
+
}
|
|
2489
|
+
|
|
2490
|
+
// src/commands/balance.tsx
|
|
2491
|
+
import React9, { useState as useState8, useEffect as useEffect8 } from "react";
|
|
2492
|
+
import { Box as Box14, Text as Text14, useApp as useApp8 } from "ink";
|
|
2493
|
+
import {
|
|
2494
|
+
Mina as Mina4,
|
|
2495
|
+
getChains as getChains6
|
|
2496
|
+
} from "@siphoyawe/mina-sdk";
|
|
2497
|
+
import { Fragment as Fragment2, jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
2498
|
+
function truncateAddress2(address) {
|
|
2499
|
+
if (address.length <= 14) return address;
|
|
2500
|
+
return `${address.slice(0, 6)}...${address.slice(-4)}`;
|
|
2501
|
+
}
|
|
2502
|
+
function formatNumber(value, decimals = 2) {
|
|
2503
|
+
if (value === 0) return "0";
|
|
2504
|
+
if (value < 0.01 && value > 0) {
|
|
2505
|
+
return value.toFixed(6);
|
|
2506
|
+
}
|
|
2507
|
+
return value.toLocaleString("en-US", {
|
|
2508
|
+
minimumFractionDigits: decimals,
|
|
2509
|
+
maximumFractionDigits: decimals
|
|
2510
|
+
});
|
|
2511
|
+
}
|
|
2512
|
+
function formatUsd2(value) {
|
|
2513
|
+
if (value === void 0 || value === 0) return "-";
|
|
2514
|
+
return `$${formatNumber(value, 2)}`;
|
|
2515
|
+
}
|
|
2516
|
+
var MAJOR_CHAIN_IDS = [1, 42161, 137, 10, 8453];
|
|
2517
|
+
var CHAIN_NAMES = {
|
|
2518
|
+
1: "Ethereum",
|
|
2519
|
+
42161: "Arbitrum",
|
|
2520
|
+
137: "Polygon",
|
|
2521
|
+
10: "Optimism",
|
|
2522
|
+
8453: "Base",
|
|
2523
|
+
56: "BNB Chain",
|
|
2524
|
+
43114: "Avalanche",
|
|
2525
|
+
250: "Fantom",
|
|
2526
|
+
100: "Gnosis",
|
|
2527
|
+
59144: "Linea",
|
|
2528
|
+
534352: "Scroll",
|
|
2529
|
+
324: "zkSync"
|
|
2530
|
+
};
|
|
2531
|
+
async function resolveChainId2(chainInput) {
|
|
2532
|
+
const numericId = parseInt(chainInput, 10);
|
|
2533
|
+
if (!isNaN(numericId)) {
|
|
2534
|
+
const response2 = await getChains6();
|
|
2535
|
+
const chain2 = response2.chains.find((c) => c.id === numericId);
|
|
2536
|
+
if (chain2) {
|
|
2537
|
+
return { chainId: chain2.id, chainName: chain2.name };
|
|
2538
|
+
}
|
|
2539
|
+
return { chainId: numericId, chainName: CHAIN_NAMES[numericId] || chainInput };
|
|
2540
|
+
}
|
|
2541
|
+
const response = await getChains6();
|
|
2542
|
+
const chainLower = chainInput.toLowerCase();
|
|
2543
|
+
const chain = response.chains.find(
|
|
2544
|
+
(c) => c.name.toLowerCase() === chainLower || c.key?.toLowerCase() === chainLower
|
|
2545
|
+
);
|
|
2546
|
+
if (chain) {
|
|
2547
|
+
return { chainId: chain.id, chainName: chain.name };
|
|
2548
|
+
}
|
|
2549
|
+
return null;
|
|
2550
|
+
}
|
|
2551
|
+
function BalanceCommand({
|
|
2552
|
+
address,
|
|
2553
|
+
chain,
|
|
2554
|
+
showAll = false,
|
|
2555
|
+
json = false
|
|
2556
|
+
}) {
|
|
2557
|
+
const { exit } = useApp8();
|
|
2558
|
+
const [chainBalances, setChainBalances] = useState8([]);
|
|
2559
|
+
const [loading, setLoading] = useState8(true);
|
|
2560
|
+
const [error, setError] = useState8(null);
|
|
2561
|
+
const [totalUsd, setTotalUsd] = useState8(0);
|
|
2562
|
+
useEffect8(() => {
|
|
2563
|
+
async function loadBalances() {
|
|
2564
|
+
try {
|
|
2565
|
+
setLoading(true);
|
|
2566
|
+
setError(null);
|
|
2567
|
+
const mina = new Mina4({ integrator: "mina-cli" });
|
|
2568
|
+
let chainIds = [];
|
|
2569
|
+
let chainNameMap = {};
|
|
2570
|
+
if (chain) {
|
|
2571
|
+
const resolved = await resolveChainId2(chain);
|
|
2572
|
+
if (!resolved) {
|
|
2573
|
+
setError(`Unknown chain: "${chain}"`);
|
|
2574
|
+
setLoading(false);
|
|
2575
|
+
return;
|
|
2576
|
+
}
|
|
2577
|
+
chainIds = [resolved.chainId];
|
|
2578
|
+
chainNameMap[resolved.chainId] = resolved.chainName;
|
|
2579
|
+
} else {
|
|
2580
|
+
chainIds = MAJOR_CHAIN_IDS;
|
|
2581
|
+
chainIds.forEach((id) => {
|
|
2582
|
+
chainNameMap[id] = CHAIN_NAMES[id] || `Chain ${id}`;
|
|
2583
|
+
});
|
|
2584
|
+
}
|
|
2585
|
+
const results = await Promise.all(
|
|
2586
|
+
chainIds.map(async (chainId) => {
|
|
2587
|
+
try {
|
|
2588
|
+
const balances = await mina.getChainBalances(address, chainId);
|
|
2589
|
+
return { chainId, balances };
|
|
2590
|
+
} catch (err) {
|
|
2591
|
+
console.warn(`Failed to fetch balances for chain ${chainId}:`, err);
|
|
2592
|
+
return { chainId, balances: [] };
|
|
2593
|
+
}
|
|
2594
|
+
})
|
|
2595
|
+
);
|
|
2596
|
+
const processedChains = [];
|
|
2597
|
+
let grandTotal = 0;
|
|
2598
|
+
for (const result of results) {
|
|
2599
|
+
const { chainId, balances } = result;
|
|
2600
|
+
let filteredBalances = balances.filter((b) => {
|
|
2601
|
+
return showAll || b.hasBalance;
|
|
2602
|
+
});
|
|
2603
|
+
const chainTotal = balances.reduce((sum, b) => {
|
|
2604
|
+
return sum + (b.balanceUsd || 0);
|
|
2605
|
+
}, 0);
|
|
2606
|
+
grandTotal += chainTotal;
|
|
2607
|
+
const balanceRows = filteredBalances.map((b) => ({
|
|
2608
|
+
symbol: b.token.symbol,
|
|
2609
|
+
balance: b.balance,
|
|
2610
|
+
balanceFormatted: b.formatted,
|
|
2611
|
+
usdValue: b.balanceUsd,
|
|
2612
|
+
hasBalance: b.hasBalance
|
|
2613
|
+
}));
|
|
2614
|
+
balanceRows.sort((a, b) => {
|
|
2615
|
+
if (a.hasBalance !== b.hasBalance) {
|
|
2616
|
+
return a.hasBalance ? -1 : 1;
|
|
2617
|
+
}
|
|
2618
|
+
return (b.usdValue || 0) - (a.usdValue || 0);
|
|
2619
|
+
});
|
|
2620
|
+
if (balanceRows.length > 0 || showAll) {
|
|
2621
|
+
processedChains.push({
|
|
2622
|
+
chainId,
|
|
2623
|
+
chainName: chainNameMap[chainId] || `Chain ${chainId}`,
|
|
2624
|
+
balances: balanceRows,
|
|
2625
|
+
totalUsd: chainTotal
|
|
2626
|
+
});
|
|
2627
|
+
}
|
|
2628
|
+
}
|
|
2629
|
+
processedChains.sort((a, b) => b.totalUsd - a.totalUsd);
|
|
2630
|
+
setChainBalances(processedChains);
|
|
2631
|
+
setTotalUsd(grandTotal);
|
|
2632
|
+
} catch (err) {
|
|
2633
|
+
setError(err instanceof Error ? err.message : "Failed to load balances");
|
|
2634
|
+
} finally {
|
|
2635
|
+
setLoading(false);
|
|
2636
|
+
}
|
|
2637
|
+
}
|
|
2638
|
+
loadBalances();
|
|
2639
|
+
}, [address, chain, showAll]);
|
|
2640
|
+
useEffect8(() => {
|
|
2641
|
+
if (!loading && json) {
|
|
2642
|
+
setTimeout(() => exit(), 100);
|
|
2643
|
+
}
|
|
2644
|
+
}, [loading, json, exit]);
|
|
2645
|
+
if (json) {
|
|
2646
|
+
if (loading) return null;
|
|
2647
|
+
if (error) {
|
|
2648
|
+
console.error(JSON.stringify({ error }, null, 2));
|
|
2649
|
+
return null;
|
|
2650
|
+
}
|
|
2651
|
+
console.log(JSON.stringify({
|
|
2652
|
+
address,
|
|
2653
|
+
chains: chainBalances.map((cb) => ({
|
|
2654
|
+
chainId: cb.chainId,
|
|
2655
|
+
chainName: cb.chainName,
|
|
2656
|
+
balances: cb.balances,
|
|
2657
|
+
totalUsd: cb.totalUsd
|
|
2658
|
+
})),
|
|
2659
|
+
totalUsd
|
|
2660
|
+
}, null, 2));
|
|
2661
|
+
return null;
|
|
2662
|
+
}
|
|
2663
|
+
if (loading) {
|
|
2664
|
+
return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", padding: 1, children: [
|
|
2665
|
+
/* @__PURE__ */ jsx14(Header, { compact: true, showTagline: false }),
|
|
2666
|
+
/* @__PURE__ */ jsx14(Spinner, { text: chain ? `Loading balances on ${chain}...` : "Loading balances across chains..." })
|
|
2667
|
+
] });
|
|
2668
|
+
}
|
|
2669
|
+
if (error) {
|
|
2670
|
+
return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", padding: 1, children: [
|
|
2671
|
+
/* @__PURE__ */ jsx14(Header, { compact: true, showTagline: false }),
|
|
2672
|
+
/* @__PURE__ */ jsx14(Box14, { children: /* @__PURE__ */ jsxs14(Text14, { color: theme.error, children: [
|
|
2673
|
+
symbols.failed,
|
|
2674
|
+
" Error: ",
|
|
2675
|
+
error
|
|
2676
|
+
] }) })
|
|
2677
|
+
] });
|
|
2678
|
+
}
|
|
2679
|
+
const maxSymbolWidth = Math.max(
|
|
2680
|
+
8,
|
|
2681
|
+
...chainBalances.flatMap((cb) => cb.balances.map((b) => b.symbol.length))
|
|
2682
|
+
);
|
|
2683
|
+
const maxBalanceWidth = Math.max(
|
|
2684
|
+
12,
|
|
2685
|
+
...chainBalances.flatMap((cb) => cb.balances.map((b) => b.balanceFormatted.length))
|
|
2686
|
+
);
|
|
2687
|
+
const boxWidth = Math.max(40, maxSymbolWidth + maxBalanceWidth + 20);
|
|
2688
|
+
const hasAnyBalances = chainBalances.some((cb) => cb.balances.length > 0);
|
|
2689
|
+
return /* @__PURE__ */ jsxs14(Box14, { flexDirection: "column", padding: 1, children: [
|
|
2690
|
+
/* @__PURE__ */ jsx14(Header, { compact: true, showTagline: false }),
|
|
2691
|
+
/* @__PURE__ */ jsx14(Box14, { children: /* @__PURE__ */ jsxs14(Text14, { color: theme.border, children: [
|
|
2692
|
+
borders.topLeft,
|
|
2693
|
+
borders.horizontal.repeat(boxWidth - 2),
|
|
2694
|
+
borders.topRight
|
|
2695
|
+
] }) }),
|
|
2696
|
+
/* @__PURE__ */ jsxs14(Box14, { children: [
|
|
2697
|
+
/* @__PURE__ */ jsx14(Text14, { color: theme.border, children: borders.vertical }),
|
|
2698
|
+
/* @__PURE__ */ jsx14(Box14, { width: boxWidth - 2, justifyContent: "center", children: /* @__PURE__ */ jsxs14(Text14, { color: theme.primary, bold: true, children: [
|
|
2699
|
+
"Balances: ",
|
|
2700
|
+
truncateAddress2(address)
|
|
2701
|
+
] }) }),
|
|
2702
|
+
/* @__PURE__ */ jsx14(Text14, { color: theme.border, children: borders.vertical })
|
|
2703
|
+
] }),
|
|
2704
|
+
/* @__PURE__ */ jsx14(Box14, { children: /* @__PURE__ */ jsxs14(Text14, { color: theme.border, children: [
|
|
2705
|
+
borders.leftT,
|
|
2706
|
+
borders.horizontal.repeat(boxWidth - 2),
|
|
2707
|
+
borders.rightT
|
|
2708
|
+
] }) }),
|
|
2709
|
+
!hasAnyBalances ? /* @__PURE__ */ jsx14(Fragment2, { children: /* @__PURE__ */ jsxs14(Box14, { children: [
|
|
2710
|
+
/* @__PURE__ */ jsx14(Text14, { color: theme.border, children: borders.vertical }),
|
|
2711
|
+
/* @__PURE__ */ jsx14(Box14, { width: boxWidth - 2, justifyContent: "center", children: /* @__PURE__ */ jsx14(Text14, { color: theme.muted, children: "No balances found" }) }),
|
|
2712
|
+
/* @__PURE__ */ jsx14(Text14, { color: theme.border, children: borders.vertical })
|
|
2713
|
+
] }) }) : chainBalances.map((chainData, chainIndex) => /* @__PURE__ */ jsxs14(React9.Fragment, { children: [
|
|
2714
|
+
/* @__PURE__ */ jsxs14(Box14, { children: [
|
|
2715
|
+
/* @__PURE__ */ jsx14(Text14, { color: theme.border, children: borders.vertical }),
|
|
2716
|
+
/* @__PURE__ */ jsx14(Text14, { children: " " }),
|
|
2717
|
+
/* @__PURE__ */ jsx14(Text14, { color: theme.accent, bold: true, children: chainData.chainName }),
|
|
2718
|
+
/* @__PURE__ */ jsx14(Box14, { flexGrow: 1 }),
|
|
2719
|
+
/* @__PURE__ */ jsx14(Text14, { color: theme.border, children: borders.vertical })
|
|
2720
|
+
] }),
|
|
2721
|
+
chainData.balances.length === 0 ? /* @__PURE__ */ jsxs14(Box14, { children: [
|
|
2722
|
+
/* @__PURE__ */ jsx14(Text14, { color: theme.border, children: borders.vertical }),
|
|
2723
|
+
/* @__PURE__ */ jsx14(Text14, { children: " " }),
|
|
2724
|
+
/* @__PURE__ */ jsx14(Text14, { color: theme.muted, dimColor: true, children: "No balances" }),
|
|
2725
|
+
/* @__PURE__ */ jsx14(Box14, { flexGrow: 1 }),
|
|
2726
|
+
/* @__PURE__ */ jsx14(Text14, { color: theme.border, children: borders.vertical })
|
|
2727
|
+
] }) : chainData.balances.map((balance, i) => /* @__PURE__ */ jsxs14(Box14, { children: [
|
|
2728
|
+
/* @__PURE__ */ jsx14(Text14, { color: theme.border, children: borders.vertical }),
|
|
2729
|
+
/* @__PURE__ */ jsx14(Text14, { children: " " }),
|
|
2730
|
+
/* @__PURE__ */ jsx14(
|
|
2731
|
+
Text14,
|
|
2732
|
+
{
|
|
2733
|
+
color: balance.hasBalance ? theme.success : theme.muted,
|
|
2734
|
+
dimColor: !balance.hasBalance,
|
|
2735
|
+
children: balance.symbol.padEnd(maxSymbolWidth)
|
|
2736
|
+
}
|
|
2737
|
+
),
|
|
2738
|
+
/* @__PURE__ */ jsx14(Text14, { children: " " }),
|
|
2739
|
+
/* @__PURE__ */ jsx14(
|
|
2740
|
+
Text14,
|
|
2741
|
+
{
|
|
2742
|
+
color: balance.hasBalance ? theme.secondary : theme.muted,
|
|
2743
|
+
dimColor: !balance.hasBalance,
|
|
2744
|
+
children: balance.balanceFormatted.padStart(maxBalanceWidth)
|
|
2745
|
+
}
|
|
2746
|
+
),
|
|
2747
|
+
/* @__PURE__ */ jsx14(Text14, { children: " " }),
|
|
2748
|
+
/* @__PURE__ */ jsx14(
|
|
2749
|
+
Text14,
|
|
2750
|
+
{
|
|
2751
|
+
color: balance.hasBalance ? theme.muted : theme.muted,
|
|
2752
|
+
dimColor: !balance.hasBalance,
|
|
2753
|
+
children: formatUsd2(balance.usdValue).padStart(12)
|
|
2754
|
+
}
|
|
2755
|
+
),
|
|
2756
|
+
/* @__PURE__ */ jsx14(Box14, { flexGrow: 1 }),
|
|
2757
|
+
/* @__PURE__ */ jsx14(Text14, { color: theme.border, children: borders.vertical })
|
|
2758
|
+
] }, `${chainData.chainId}-${balance.symbol}-${i}`)),
|
|
2759
|
+
chainIndex < chainBalances.length - 1 && /* @__PURE__ */ jsx14(Box14, { children: /* @__PURE__ */ jsxs14(Text14, { color: theme.border, children: [
|
|
2760
|
+
borders.leftT,
|
|
2761
|
+
borders.horizontal.repeat(boxWidth - 2),
|
|
2762
|
+
borders.rightT
|
|
2763
|
+
] }) })
|
|
2764
|
+
] }, chainData.chainId)),
|
|
2765
|
+
/* @__PURE__ */ jsx14(Box14, { children: /* @__PURE__ */ jsxs14(Text14, { color: theme.border, children: [
|
|
2766
|
+
borders.bottomLeft,
|
|
2767
|
+
borders.horizontal.repeat(boxWidth - 2),
|
|
2768
|
+
borders.bottomRight
|
|
2769
|
+
] }) }),
|
|
2770
|
+
hasAnyBalances && totalUsd > 0 && /* @__PURE__ */ jsxs14(Box14, { marginTop: 1, children: [
|
|
2771
|
+
/* @__PURE__ */ jsxs14(Text14, { color: theme.secondary, children: [
|
|
2772
|
+
symbols.arrow,
|
|
2773
|
+
" Total:"
|
|
2774
|
+
] }),
|
|
2775
|
+
/* @__PURE__ */ jsxs14(Text14, { color: theme.success, bold: true, children: [
|
|
2776
|
+
" ",
|
|
2777
|
+
"$",
|
|
2778
|
+
formatNumber(totalUsd, 2)
|
|
2779
|
+
] })
|
|
2780
|
+
] }),
|
|
2781
|
+
/* @__PURE__ */ jsx14(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text14, { color: theme.muted, dimColor: true, children: chain ? "Use --all to show zero balances" : "Use --chain <name> for specific chain, --all for zero balances" }) })
|
|
2782
|
+
] });
|
|
2783
|
+
}
|
|
2784
|
+
|
|
2785
|
+
// src/commands/config.tsx
|
|
2786
|
+
import { useState as useState9, useEffect as useEffect9 } from "react";
|
|
2787
|
+
import { Box as Box15, Text as Text15, useApp as useApp9 } from "ink";
|
|
2788
|
+
|
|
2789
|
+
// src/lib/config.ts
|
|
2790
|
+
import os2 from "os";
|
|
2791
|
+
import fs3 from "fs";
|
|
2792
|
+
import path2 from "path";
|
|
2793
|
+
var CONFIG_PATH = path2.join(os2.homedir(), ".mina", "config.json");
|
|
2794
|
+
var DEFAULT_CONFIG = {
|
|
2795
|
+
slippage: 0.5,
|
|
2796
|
+
autoDeposit: true,
|
|
2797
|
+
defaultChain: "arbitrum",
|
|
2798
|
+
rpc: {}
|
|
2799
|
+
};
|
|
2800
|
+
var VALID_KEYS = ["slippage", "autoDeposit", "defaultChain", "rpc"];
|
|
2801
|
+
function ensureDir2() {
|
|
2802
|
+
const dir = path2.dirname(CONFIG_PATH);
|
|
2803
|
+
if (!fs3.existsSync(dir)) {
|
|
2804
|
+
fs3.mkdirSync(dir, { recursive: true });
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
function getConfig() {
|
|
2808
|
+
ensureDir2();
|
|
2809
|
+
if (!fs3.existsSync(CONFIG_PATH)) {
|
|
2810
|
+
return { ...DEFAULT_CONFIG };
|
|
2811
|
+
}
|
|
2812
|
+
try {
|
|
2813
|
+
const data = JSON.parse(fs3.readFileSync(CONFIG_PATH, "utf-8"));
|
|
2814
|
+
return {
|
|
2815
|
+
...DEFAULT_CONFIG,
|
|
2816
|
+
...data,
|
|
2817
|
+
// Ensure rpc is always an object
|
|
2818
|
+
rpc: { ...DEFAULT_CONFIG.rpc, ...data.rpc || {} }
|
|
2819
|
+
};
|
|
2820
|
+
} catch {
|
|
2821
|
+
return { ...DEFAULT_CONFIG };
|
|
2822
|
+
}
|
|
2823
|
+
}
|
|
2824
|
+
function parseValue(value) {
|
|
2825
|
+
if (value.toLowerCase() === "true") return true;
|
|
2826
|
+
if (value.toLowerCase() === "false") return false;
|
|
2827
|
+
const num = Number(value);
|
|
2828
|
+
if (!isNaN(num) && value.trim() !== "") return num;
|
|
2829
|
+
return value;
|
|
2830
|
+
}
|
|
2831
|
+
function isValidKey(key) {
|
|
2832
|
+
const parts = key.split(".");
|
|
2833
|
+
if (parts.length === 2 && parts[0] === "rpc") {
|
|
2834
|
+
return true;
|
|
2835
|
+
}
|
|
2836
|
+
return VALID_KEYS.includes(key);
|
|
2837
|
+
}
|
|
2838
|
+
function setConfig(key, value) {
|
|
2839
|
+
ensureDir2();
|
|
2840
|
+
const config = getConfig();
|
|
2841
|
+
const keys = key.split(".");
|
|
2842
|
+
const rpcKey = keys[1];
|
|
2843
|
+
if (keys.length === 2 && keys[0] === "rpc" && rpcKey) {
|
|
2844
|
+
config.rpc[rpcKey] = value;
|
|
2845
|
+
} else if (VALID_KEYS.includes(key)) {
|
|
2846
|
+
config[key] = value;
|
|
2847
|
+
} else {
|
|
2848
|
+
throw new Error(`Unknown config key: ${key}`);
|
|
2849
|
+
}
|
|
2850
|
+
fs3.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
2851
|
+
}
|
|
2852
|
+
function getConfigValue(key) {
|
|
2853
|
+
const config = getConfig();
|
|
2854
|
+
const keys = key.split(".");
|
|
2855
|
+
const rpcKey = keys[1];
|
|
2856
|
+
if (keys.length === 2 && keys[0] === "rpc" && rpcKey) {
|
|
2857
|
+
return config.rpc[rpcKey] ?? null;
|
|
2858
|
+
}
|
|
2859
|
+
if (VALID_KEYS.includes(key)) {
|
|
2860
|
+
return config[key];
|
|
2861
|
+
}
|
|
2862
|
+
return void 0;
|
|
2863
|
+
}
|
|
2864
|
+
function getConfigList() {
|
|
2865
|
+
const config = getConfig();
|
|
2866
|
+
const savedConfig = fs3.existsSync(CONFIG_PATH) ? JSON.parse(fs3.readFileSync(CONFIG_PATH, "utf-8")) : {};
|
|
2867
|
+
const items = [];
|
|
2868
|
+
items.push({
|
|
2869
|
+
key: "slippage",
|
|
2870
|
+
value: String(config.slippage),
|
|
2871
|
+
isDefault: savedConfig.slippage === void 0
|
|
2872
|
+
});
|
|
2873
|
+
items.push({
|
|
2874
|
+
key: "autoDeposit",
|
|
2875
|
+
value: String(config.autoDeposit),
|
|
2876
|
+
isDefault: savedConfig.autoDeposit === void 0
|
|
2877
|
+
});
|
|
2878
|
+
items.push({
|
|
2879
|
+
key: "defaultChain",
|
|
2880
|
+
value: config.defaultChain,
|
|
2881
|
+
isDefault: savedConfig.defaultChain === void 0
|
|
2882
|
+
});
|
|
2883
|
+
const commonChains = ["ethereum", "arbitrum", "polygon", "optimism", "base"];
|
|
2884
|
+
for (const chain of commonChains) {
|
|
2885
|
+
const customRpc = config.rpc[chain];
|
|
2886
|
+
items.push({
|
|
2887
|
+
key: `rpc.${chain}`,
|
|
2888
|
+
value: customRpc || "(default)",
|
|
2889
|
+
isDefault: !customRpc
|
|
2890
|
+
});
|
|
2891
|
+
}
|
|
2892
|
+
for (const [chain, url] of Object.entries(config.rpc)) {
|
|
2893
|
+
if (!commonChains.includes(chain) && url) {
|
|
2894
|
+
items.push({
|
|
2895
|
+
key: `rpc.${chain}`,
|
|
2896
|
+
value: url,
|
|
2897
|
+
isDefault: false
|
|
2898
|
+
});
|
|
2899
|
+
}
|
|
2900
|
+
}
|
|
2901
|
+
return items;
|
|
2902
|
+
}
|
|
2903
|
+
function getConfigPath() {
|
|
2904
|
+
return CONFIG_PATH;
|
|
2905
|
+
}
|
|
2906
|
+
|
|
2907
|
+
// src/commands/config.tsx
|
|
2908
|
+
import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2909
|
+
function ConfigList() {
|
|
2910
|
+
const { exit } = useApp9();
|
|
2911
|
+
const [items, setItems] = useState9([]);
|
|
2912
|
+
useEffect9(() => {
|
|
2913
|
+
const configItems = getConfigList();
|
|
2914
|
+
setItems(
|
|
2915
|
+
configItems.map((item) => ({
|
|
2916
|
+
setting: item.key,
|
|
2917
|
+
value: item.value,
|
|
2918
|
+
isDefault: item.isDefault
|
|
2919
|
+
}))
|
|
2920
|
+
);
|
|
2921
|
+
setTimeout(() => exit(), 100);
|
|
2922
|
+
}, [exit]);
|
|
2923
|
+
const columns = [
|
|
2924
|
+
{
|
|
2925
|
+
header: "Setting",
|
|
2926
|
+
accessor: "setting",
|
|
2927
|
+
headerColor: theme.primary,
|
|
2928
|
+
cellColor: theme.secondary
|
|
2929
|
+
},
|
|
2930
|
+
{
|
|
2931
|
+
header: "Value",
|
|
2932
|
+
accessor: "value",
|
|
2933
|
+
headerColor: theme.primary,
|
|
2934
|
+
cellColor: (value, row) => row.isDefault ? theme.muted : theme.success
|
|
2935
|
+
}
|
|
2936
|
+
];
|
|
2937
|
+
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", padding: 1, children: [
|
|
2938
|
+
/* @__PURE__ */ jsx15(Header, { compact: true, showTagline: false }),
|
|
2939
|
+
/* @__PURE__ */ jsx15(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsxs15(Text15, { color: theme.secondary, children: [
|
|
2940
|
+
symbols.arrow,
|
|
2941
|
+
" Configuration Settings"
|
|
2942
|
+
] }) }),
|
|
2943
|
+
/* @__PURE__ */ jsx15(Table, { data: items, columns, bordered: true, borderColor: theme.border }),
|
|
2944
|
+
/* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text15, { color: theme.muted, dimColor: true, children: "Values in gray are defaults. Use 'mina config set <key> <value>' to customize." }) })
|
|
2945
|
+
] });
|
|
2946
|
+
}
|
|
2947
|
+
function ConfigGet({ configKey }) {
|
|
2948
|
+
const { exit } = useApp9();
|
|
2949
|
+
const [value, setValue] = useState9(null);
|
|
2950
|
+
const [error, setError] = useState9(null);
|
|
2951
|
+
useEffect9(() => {
|
|
2952
|
+
if (!isValidKey(configKey)) {
|
|
2953
|
+
setError(`Unknown config key: ${configKey}`);
|
|
2954
|
+
} else {
|
|
2955
|
+
const val = getConfigValue(configKey);
|
|
2956
|
+
setValue(val === void 0 || val === null ? "(not set)" : String(val));
|
|
2957
|
+
}
|
|
2958
|
+
setTimeout(() => exit(), 100);
|
|
2959
|
+
}, [configKey, exit]);
|
|
2960
|
+
if (error) {
|
|
2961
|
+
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", padding: 1, children: [
|
|
2962
|
+
/* @__PURE__ */ jsx15(Header, { compact: true, showTagline: false }),
|
|
2963
|
+
/* @__PURE__ */ jsx15(Box15, { children: /* @__PURE__ */ jsxs15(Text15, { color: theme.error, children: [
|
|
2964
|
+
symbols.failed,
|
|
2965
|
+
" ",
|
|
2966
|
+
error
|
|
2967
|
+
] }) })
|
|
2968
|
+
] });
|
|
2969
|
+
}
|
|
2970
|
+
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", padding: 1, children: [
|
|
2971
|
+
/* @__PURE__ */ jsx15(Header, { compact: true, showTagline: false }),
|
|
2972
|
+
/* @__PURE__ */ jsxs15(Box15, { children: [
|
|
2973
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.secondary, children: configKey }),
|
|
2974
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.muted, children: " = " }),
|
|
2975
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.primary, children: value })
|
|
2976
|
+
] })
|
|
2977
|
+
] });
|
|
2978
|
+
}
|
|
2979
|
+
function ConfigSet({ configKey, configValue }) {
|
|
2980
|
+
const { exit } = useApp9();
|
|
2981
|
+
const [success, setSuccess] = useState9(false);
|
|
2982
|
+
const [error, setError] = useState9(null);
|
|
2983
|
+
const [parsedValue, setParsedValue] = useState9(null);
|
|
2984
|
+
useEffect9(() => {
|
|
2985
|
+
if (!isValidKey(configKey)) {
|
|
2986
|
+
setError(`Unknown config key: ${configKey}`);
|
|
2987
|
+
} else {
|
|
2988
|
+
try {
|
|
2989
|
+
const parsed = parseValue(configValue);
|
|
2990
|
+
setParsedValue(parsed);
|
|
2991
|
+
setConfig(configKey, parsed);
|
|
2992
|
+
setSuccess(true);
|
|
2993
|
+
} catch (err) {
|
|
2994
|
+
setError(err instanceof Error ? err.message : "Failed to set value");
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2997
|
+
setTimeout(() => exit(), 100);
|
|
2998
|
+
}, [configKey, configValue, exit]);
|
|
2999
|
+
if (error) {
|
|
3000
|
+
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", padding: 1, children: [
|
|
3001
|
+
/* @__PURE__ */ jsx15(Header, { compact: true, showTagline: false }),
|
|
3002
|
+
/* @__PURE__ */ jsx15(Box15, { children: /* @__PURE__ */ jsxs15(Text15, { color: theme.error, children: [
|
|
3003
|
+
symbols.failed,
|
|
3004
|
+
" ",
|
|
3005
|
+
error
|
|
3006
|
+
] }) })
|
|
3007
|
+
] });
|
|
3008
|
+
}
|
|
3009
|
+
if (success) {
|
|
3010
|
+
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", padding: 1, children: [
|
|
3011
|
+
/* @__PURE__ */ jsx15(Header, { compact: true, showTagline: false }),
|
|
3012
|
+
/* @__PURE__ */ jsxs15(Box15, { children: [
|
|
3013
|
+
/* @__PURE__ */ jsxs15(Text15, { color: theme.success, children: [
|
|
3014
|
+
symbols.completed,
|
|
3015
|
+
" "
|
|
3016
|
+
] }),
|
|
3017
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.secondary, children: "Set " }),
|
|
3018
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.primary, children: configKey }),
|
|
3019
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.muted, children: " = " }),
|
|
3020
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.success, children: String(parsedValue) })
|
|
3021
|
+
] })
|
|
3022
|
+
] });
|
|
3023
|
+
}
|
|
3024
|
+
return null;
|
|
3025
|
+
}
|
|
3026
|
+
function ConfigPath() {
|
|
3027
|
+
const { exit } = useApp9();
|
|
3028
|
+
useEffect9(() => {
|
|
3029
|
+
setTimeout(() => exit(), 100);
|
|
3030
|
+
}, [exit]);
|
|
3031
|
+
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", padding: 1, children: [
|
|
3032
|
+
/* @__PURE__ */ jsx15(Header, { compact: true, showTagline: false }),
|
|
3033
|
+
/* @__PURE__ */ jsxs15(Box15, { children: [
|
|
3034
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.muted, children: "Config file: " }),
|
|
3035
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.secondary, children: getConfigPath() })
|
|
3036
|
+
] })
|
|
3037
|
+
] });
|
|
3038
|
+
}
|
|
3039
|
+
function ConfigHelp() {
|
|
3040
|
+
const { exit } = useApp9();
|
|
3041
|
+
useEffect9(() => {
|
|
3042
|
+
setTimeout(() => exit(), 100);
|
|
3043
|
+
}, [exit]);
|
|
3044
|
+
return /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", padding: 1, children: [
|
|
3045
|
+
/* @__PURE__ */ jsx15(Header, { compact: true, showTagline: false }),
|
|
3046
|
+
/* @__PURE__ */ jsx15(Box, { bordered: true, title: "Config Usage", padding: 1, children: /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
|
|
3047
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.secondary, children: "mina config list" }),
|
|
3048
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.muted, children: " Show all settings" }),
|
|
3049
|
+
/* @__PURE__ */ jsx15(Text15, { children: " " }),
|
|
3050
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.secondary, children: "mina config get <key>" }),
|
|
3051
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.muted, children: " Show single value" }),
|
|
3052
|
+
/* @__PURE__ */ jsx15(Text15, { children: " " }),
|
|
3053
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.secondary, children: "mina config set <key> <value>" }),
|
|
3054
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.muted, children: " Set a value" }),
|
|
3055
|
+
/* @__PURE__ */ jsx15(Text15, { children: " " }),
|
|
3056
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.secondary, children: "mina config path" }),
|
|
3057
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.muted, children: " Show config file location" })
|
|
3058
|
+
] }) }),
|
|
3059
|
+
/* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx15(Box, { bordered: true, title: "Available Settings", padding: 1, children: /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
|
|
3060
|
+
/* @__PURE__ */ jsxs15(Box15, { children: [
|
|
3061
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.primary, bold: true, children: "slippage" }),
|
|
3062
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.muted, children: " - Default slippage tolerance (e.g., 0.5)" })
|
|
3063
|
+
] }),
|
|
3064
|
+
/* @__PURE__ */ jsxs15(Box15, { children: [
|
|
3065
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.primary, bold: true, children: "autoDeposit" }),
|
|
3066
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.muted, children: " - Auto-deposit to Hyperliquid (true/false)" })
|
|
3067
|
+
] }),
|
|
3068
|
+
/* @__PURE__ */ jsxs15(Box15, { children: [
|
|
3069
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.primary, bold: true, children: "defaultChain" }),
|
|
3070
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.muted, children: " - Default source chain (e.g., arbitrum)" })
|
|
3071
|
+
] }),
|
|
3072
|
+
/* @__PURE__ */ jsxs15(Box15, { children: [
|
|
3073
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.primary, bold: true, children: "rpc.<chain>" }),
|
|
3074
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.muted, children: " - Custom RPC URL for a chain" })
|
|
3075
|
+
] })
|
|
3076
|
+
] }) }) }),
|
|
3077
|
+
/* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx15(Box, { bordered: true, title: "Examples", padding: 1, children: /* @__PURE__ */ jsxs15(Box15, { flexDirection: "column", children: [
|
|
3078
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.secondary, children: "mina config set slippage 0.5" }),
|
|
3079
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.secondary, children: "mina config set autoDeposit false" }),
|
|
3080
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.secondary, children: "mina config set rpc.arbitrum https://arb1.example.com" }),
|
|
3081
|
+
/* @__PURE__ */ jsx15(Text15, { color: theme.secondary, children: "mina config get slippage" })
|
|
3082
|
+
] }) }) })
|
|
3083
|
+
] });
|
|
3084
|
+
}
|
|
3085
|
+
function ConfigCommand({ action, key: configKey, value }) {
|
|
3086
|
+
switch (action) {
|
|
3087
|
+
case "list":
|
|
3088
|
+
return /* @__PURE__ */ jsx15(ConfigList, {});
|
|
3089
|
+
case "get":
|
|
3090
|
+
if (!configKey) {
|
|
3091
|
+
return /* @__PURE__ */ jsx15(ConfigHelp, {});
|
|
3092
|
+
}
|
|
3093
|
+
return /* @__PURE__ */ jsx15(ConfigGet, { configKey });
|
|
3094
|
+
case "set":
|
|
3095
|
+
if (!configKey || value === void 0) {
|
|
3096
|
+
return /* @__PURE__ */ jsx15(ConfigHelp, {});
|
|
3097
|
+
}
|
|
3098
|
+
return /* @__PURE__ */ jsx15(ConfigSet, { configKey, configValue: value });
|
|
3099
|
+
case "path":
|
|
3100
|
+
return /* @__PURE__ */ jsx15(ConfigPath, {});
|
|
3101
|
+
default:
|
|
3102
|
+
return /* @__PURE__ */ jsx15(ConfigHelp, {});
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
|
|
3106
|
+
// src/index.tsx
|
|
3107
|
+
var VERSION = "1.0.0";
|
|
3108
|
+
var program = new Command();
|
|
3109
|
+
program.name("mina").description("Mina Bridge CLI - Bridge assets from any chain to Hyperliquid").version(VERSION, "-v, --version", "Display the current version").helpOption("-h, --help", "Display help information");
|
|
3110
|
+
program.command("wizard").description("Launch the interactive bridge wizard").action(() => {
|
|
3111
|
+
render(React11.createElement(Wizard));
|
|
3112
|
+
});
|
|
3113
|
+
program.command("quote").description("Get a bridge quote").requiredOption("--from <chain>", "Source chain (e.g., ethereum, arbitrum, polygon)").option("--to <chain>", "Destination chain", "hyperliquid").requiredOption("--token <symbol>", "Token to bridge (e.g., USDC, ETH)").requiredOption("--amount <number>", "Amount to bridge").option("--json", "Output as JSON for machine-readable format").action((options) => {
|
|
3114
|
+
render(
|
|
3115
|
+
React11.createElement(QuoteDisplay, {
|
|
3116
|
+
fromChain: options.from,
|
|
3117
|
+
toChain: options.to,
|
|
3118
|
+
token: options.token,
|
|
3119
|
+
amount: options.amount,
|
|
3120
|
+
jsonOutput: options.json || false
|
|
3121
|
+
})
|
|
3122
|
+
);
|
|
3123
|
+
});
|
|
3124
|
+
program.command("status <txHash>").description("Check bridge status").option("--watch", "Poll for updates").action((txHash, options) => {
|
|
3125
|
+
render(
|
|
3126
|
+
React11.createElement(Status, {
|
|
3127
|
+
txHash,
|
|
3128
|
+
watch: options.watch || false
|
|
3129
|
+
})
|
|
3130
|
+
);
|
|
3131
|
+
});
|
|
3132
|
+
program.command("chains").description("List supported chains").option("--json", "Output as JSON").action((options) => {
|
|
3133
|
+
render(
|
|
3134
|
+
React11.createElement(ChainsCommand, {
|
|
3135
|
+
json: options.json || false
|
|
3136
|
+
})
|
|
3137
|
+
);
|
|
3138
|
+
});
|
|
3139
|
+
program.command("tokens").description("List bridgeable tokens").option("--chain <chain>", "Filter by chain (name or ID)").option("--json", "Output as JSON").action((options) => {
|
|
3140
|
+
render(
|
|
3141
|
+
React11.createElement(TokensCommand, {
|
|
3142
|
+
chain: options.chain,
|
|
3143
|
+
json: options.json || false
|
|
3144
|
+
})
|
|
3145
|
+
);
|
|
3146
|
+
});
|
|
3147
|
+
program.command("bridge").description("Execute a bridge transaction").requiredOption("--from <chain>", "Source chain (e.g., arbitrum, ethereum, polygon)").requiredOption("--token <symbol>", "Token to bridge (e.g., USDC, ETH)").requiredOption("--amount <number>", "Amount to bridge").option("--key <path>", "Path to private key file (JSON or plain text)").option("--yes", "Skip confirmation prompt").option("--auto-deposit", "Auto-deposit to Hyperliquid L1", true).action((options) => {
|
|
3148
|
+
bridgeCommand(options);
|
|
3149
|
+
});
|
|
3150
|
+
program.command("history").description("View bridge transaction history").option("--limit <number>", "Number of entries to show", "10").option("--address <address>", "Filter by wallet address").option("--json", "Output as JSON").action((options) => {
|
|
3151
|
+
render(
|
|
3152
|
+
React11.createElement(HistoryCommand, {
|
|
3153
|
+
limit: parseInt(options.limit, 10) || 10,
|
|
3154
|
+
address: options.address,
|
|
3155
|
+
json: options.json || false
|
|
3156
|
+
})
|
|
3157
|
+
);
|
|
3158
|
+
});
|
|
3159
|
+
program.command("balance").description("Check wallet balances").requiredOption("--address <address>", "Wallet address").option("--chain <chain>", "Specific chain (name or ID)").option("--all", "Show all tokens including zero balance").option("--json", "Output as JSON").action((options) => {
|
|
3160
|
+
render(
|
|
3161
|
+
React11.createElement(BalanceCommand, {
|
|
3162
|
+
address: options.address,
|
|
3163
|
+
chain: options.chain,
|
|
3164
|
+
showAll: options.all || false,
|
|
3165
|
+
json: options.json || false
|
|
3166
|
+
})
|
|
3167
|
+
);
|
|
3168
|
+
});
|
|
3169
|
+
program.command("config [action] [key] [value]").description("Manage CLI configuration").action((action, key, value) => {
|
|
3170
|
+
render(
|
|
3171
|
+
React11.createElement(ConfigCommand, {
|
|
3172
|
+
action,
|
|
3173
|
+
key,
|
|
3174
|
+
value
|
|
3175
|
+
})
|
|
3176
|
+
);
|
|
3177
|
+
});
|
|
3178
|
+
program.addHelpText("after", `
|
|
3179
|
+
${chalk.bold("Examples:")}
|
|
3180
|
+
${chalk.dim("# Launch interactive wizard (default when no command given)")}
|
|
3181
|
+
$ mina
|
|
3182
|
+
|
|
3183
|
+
${chalk.dim("# Get a bridge quote")}
|
|
3184
|
+
$ mina quote --from arbitrum --token USDC --amount 100
|
|
3185
|
+
$ mina quote --from ethereum --token ETH --amount 0.5 --json
|
|
3186
|
+
|
|
3187
|
+
${chalk.dim("# Bridge USDC from Ethereum to Hyperliquid")}
|
|
3188
|
+
$ mina bridge --from arbitrum --token USDC --amount 100 --key ./key.json
|
|
3189
|
+
$ mina bridge --from ethereum --token ETH --amount 0.5 --yes
|
|
3190
|
+
|
|
3191
|
+
${chalk.dim("# Check transaction status")}
|
|
3192
|
+
$ mina status 0x1234...abcd
|
|
3193
|
+
$ mina status 0x1234...abcd --watch
|
|
3194
|
+
|
|
3195
|
+
${chalk.dim("# View supported chains and tokens")}
|
|
3196
|
+
$ mina chains
|
|
3197
|
+
$ mina tokens --chain arbitrum
|
|
3198
|
+
$ mina tokens --chain 1
|
|
3199
|
+
|
|
3200
|
+
${chalk.dim("# View bridge transaction history")}
|
|
3201
|
+
$ mina history
|
|
3202
|
+
$ mina history --limit 20
|
|
3203
|
+
$ mina history --address 0x1234...abcd
|
|
3204
|
+
$ mina history --json
|
|
3205
|
+
|
|
3206
|
+
${chalk.dim("# Check wallet balances")}
|
|
3207
|
+
$ mina balance --address 0x1234...abcd
|
|
3208
|
+
$ mina balance --address 0x1234...abcd --chain arbitrum
|
|
3209
|
+
$ mina balance --address 0x1234...abcd --all
|
|
3210
|
+
$ mina balance --address 0x1234...abcd --json
|
|
3211
|
+
|
|
3212
|
+
${chalk.dim("# Manage configuration")}
|
|
3213
|
+
$ mina config list
|
|
3214
|
+
$ mina config get slippage
|
|
3215
|
+
$ mina config set slippage 0.5
|
|
3216
|
+
$ mina config set rpc.arbitrum https://arb1.example.com
|
|
3217
|
+
|
|
3218
|
+
${chalk.bold("Documentation:")}
|
|
3219
|
+
https://github.com/siphoyawe/mina-sdk
|
|
3220
|
+
`);
|
|
3221
|
+
var args = process.argv.slice(2);
|
|
3222
|
+
var hasCommand = args.length > 0 && !args[0]?.startsWith("-");
|
|
3223
|
+
var isHelpOrVersion = args.some((arg) => ["-h", "--help", "-v", "--version"].includes(arg));
|
|
3224
|
+
if (!hasCommand && !isHelpOrVersion) {
|
|
3225
|
+
render(React11.createElement(Wizard));
|
|
3226
|
+
} else {
|
|
3227
|
+
program.parse(process.argv);
|
|
3228
|
+
}
|
|
3229
|
+
//# sourceMappingURL=index.js.map
|