@rubanrubi/hardhat-dashboard 0.1.0 → 0.2.1
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/CHANGELOG.md +50 -0
- package/dist/types/rpc.d.ts +2 -0
- package/dist/types/rpc.js.map +1 -1
- package/dist/ui/app.js +366 -58
- package/dist/ui/app.js.map +3 -3
- package/dist/ui/styles.css +206 -0
- package/package.json +2 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@rubanrubi/hardhat-dashboard` are documented here.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## [0.2.0] — 2026-03-29
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
#### Network names
|
|
12
|
+
- Expanded chain ID → name mapping from 7 to 35+ EVM networks and testnets.
|
|
13
|
+
- Now covers: Ethereum, Sepolia, Goerli, Polygon Mainnet, **Polygon Amoy Testnet**, Polygon Mumbai, Polygon zkEVM, BNB Smart Chain, Avalanche C-Chain, Avalanche Fuji, Arbitrum One, Arbitrum Sepolia, Optimism Mainnet, Optimism Sepolia, Base Mainnet, Base Sepolia, zkSync Era, Linea, Scroll, Fantom Opera, Gnosis Chain, Celo, Blast, Mantle, Moonbeam, Moonriver, Moonbase Alpha, Metis Andromeda, Cronos, Hardhat Local, Anvil Local, and more.
|
|
14
|
+
- Each chain displays its correct **native currency symbol** (ETH, POL, BNB, AVAX, FTM, xDAI, CELO, GLMR, MOVR, CRO) so balance values are labelled accurately.
|
|
15
|
+
|
|
16
|
+
#### Wallet details panel
|
|
17
|
+
- The wallet badge now shows: network name with a live green indicator dot, shortened address (`0x1234…5678`), a **Copy** button that writes the full address to the clipboard (shows `✓` for 2 s as feedback), and the current native token balance.
|
|
18
|
+
- Balance is fetched live via `eth_getBalance` on connect and re-fetched whenever the account or chain changes.
|
|
19
|
+
- The **Balance** metric in the status strip replaces the redundant "Wallet" name field.
|
|
20
|
+
|
|
21
|
+
#### Human-readable transaction display
|
|
22
|
+
- `eth_sendTransaction` cards now show a structured summary: **From**, **To** (shortened), and **Value** in ETH rather than raw hex.
|
|
23
|
+
- Transactions with no `to` address display a **Contract Deployment** badge and read *Deploy `<ContractName>`* when the ABI is available.
|
|
24
|
+
- Decoded call section shows `ContractName.functionName(…)` with the full signature and each argument on its own line.
|
|
25
|
+
- When a function selector is recognised but no ABI match is found, an "Unknown function — selector: `0x…`" warning is shown instead of silently hiding the call.
|
|
26
|
+
- **`personal_sign`** cards decode the hex message to UTF-8 and display it as plain text.
|
|
27
|
+
- **`eth_sign`** cards show the raw message hash.
|
|
28
|
+
- **`eth_signTypedData*`** cards parse the EIP-712 payload and display the **Domain** and **Message** fields as formatted JSON, so users can read what they are signing before confirming.
|
|
29
|
+
- Raw hex calldata is no longer shown in pending request cards.
|
|
30
|
+
|
|
31
|
+
#### Activity / Tx History filtering
|
|
32
|
+
- The **Tx History** panel now shows only signing and deployment events (`eth_sendTransaction`, `eth_sign`, `personal_sign`, `eth_signTypedData*`), filtering out read-only `eth_call` passthroughs that were adding noise.
|
|
33
|
+
- Confirmed transactions display the truncated **tx hash** when available.
|
|
34
|
+
- Failed/rejected entries show the human-readable **error message** instead of a raw JSON error object.
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
- `WalletBadge` component refactored to a multi-line detail panel; the old single-line pill is preserved for disconnected/unavailable states.
|
|
38
|
+
- Status strip "Wallet" metric renamed to **Balance** and now reflects live on-chain balance.
|
|
39
|
+
- History entries show `→` between queued and completed timestamps for clarity.
|
|
40
|
+
- Method names in cards use friendly labels (*Send Transaction*, *Sign Message*, *Sign Typed Data*) instead of raw RPC method strings.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## [0.1.1] — 2025-03-28
|
|
45
|
+
|
|
46
|
+
- Fix: updated README documentation and resolved TypeScript type-stub lint errors.
|
|
47
|
+
|
|
48
|
+
## [0.1.0] — 2025-03-28
|
|
49
|
+
|
|
50
|
+
- Initial release: Hardhat plugin that routes JSON-RPC signing through a local browser dashboard backed by MetaMask.
|
package/dist/types/rpc.d.ts
CHANGED
package/dist/types/rpc.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc.js","sourceRoot":"","sources":["../../src/types/rpc.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"rpc.js","sourceRoot":"","sources":["../../src/types/rpc.ts"],"names":[],"mappings":";;;AA2Ga,QAAA,kBAAkB,GAA0B;IACvD,SAAS,EAAE,KAAK;IAChB,SAAS,EAAE,KAAK;IAChB,MAAM,EAAE,KAAK;IACb,UAAU,EAAE,UAAU;CACvB,CAAC"}
|
package/dist/ui/app.js
CHANGED
|
@@ -1083,7 +1083,7 @@ var require_react_development = __commonJS({
|
|
|
1083
1083
|
}
|
|
1084
1084
|
return dispatcher.useContext(Context);
|
|
1085
1085
|
}
|
|
1086
|
-
function
|
|
1086
|
+
function useState3(initialState) {
|
|
1087
1087
|
var dispatcher = resolveDispatcher();
|
|
1088
1088
|
return dispatcher.useState(initialState);
|
|
1089
1089
|
}
|
|
@@ -1886,7 +1886,7 @@ var require_react_development = __commonJS({
|
|
|
1886
1886
|
exports.useMemo = useMemo;
|
|
1887
1887
|
exports.useReducer = useReducer;
|
|
1888
1888
|
exports.useRef = useRef2;
|
|
1889
|
-
exports.useState =
|
|
1889
|
+
exports.useState = useState3;
|
|
1890
1890
|
exports.useSyncExternalStore = useSyncExternalStore;
|
|
1891
1891
|
exports.useTransition = useTransition;
|
|
1892
1892
|
exports.version = ReactVersion;
|
|
@@ -24486,7 +24486,7 @@ var require_jsx_runtime = __commonJS({
|
|
|
24486
24486
|
});
|
|
24487
24487
|
|
|
24488
24488
|
// src/ui/app.tsx
|
|
24489
|
-
var
|
|
24489
|
+
var import_react2 = __toESM(require_react());
|
|
24490
24490
|
var import_client = __toESM(require_client());
|
|
24491
24491
|
|
|
24492
24492
|
// src/types/rpc.ts
|
|
@@ -24506,8 +24506,98 @@ function formatTimestamp(value) {
|
|
|
24506
24506
|
second: "2-digit"
|
|
24507
24507
|
});
|
|
24508
24508
|
}
|
|
24509
|
-
function
|
|
24510
|
-
return
|
|
24509
|
+
function shortenAddress(addr) {
|
|
24510
|
+
return `${addr.slice(0, 8)}...${addr.slice(-6)}`;
|
|
24511
|
+
}
|
|
24512
|
+
function formatWeiValue(hex) {
|
|
24513
|
+
try {
|
|
24514
|
+
const wei = BigInt(hex);
|
|
24515
|
+
if (wei === 0n) return "";
|
|
24516
|
+
const eth = Number(wei) / 1e18;
|
|
24517
|
+
return `${eth.toFixed(6)} ETH`;
|
|
24518
|
+
} catch {
|
|
24519
|
+
return hex;
|
|
24520
|
+
}
|
|
24521
|
+
}
|
|
24522
|
+
function hexToUtf8(hex) {
|
|
24523
|
+
try {
|
|
24524
|
+
const bytes = (hex.startsWith("0x") ? hex.slice(2) : hex).match(/.{2}/g) ?? [];
|
|
24525
|
+
const decoded = bytes.map((b) => String.fromCharCode(parseInt(b, 16))).join("");
|
|
24526
|
+
return decoded.trim() || hex;
|
|
24527
|
+
} catch {
|
|
24528
|
+
return hex;
|
|
24529
|
+
}
|
|
24530
|
+
}
|
|
24531
|
+
function extractTxFields(request) {
|
|
24532
|
+
if (request.method !== "eth_sendTransaction") return null;
|
|
24533
|
+
const params = request.params;
|
|
24534
|
+
const first = Array.isArray(params) ? params[0] : params;
|
|
24535
|
+
if (!first || typeof first !== "object") return null;
|
|
24536
|
+
const tx = first;
|
|
24537
|
+
const to = typeof tx.to === "string" && tx.to.length > 2 ? tx.to : void 0;
|
|
24538
|
+
const from = typeof tx.from === "string" ? tx.from : void 0;
|
|
24539
|
+
const rawValue = typeof tx.value === "string" ? tx.value : void 0;
|
|
24540
|
+
const value = rawValue && rawValue !== "0x0" && rawValue !== "0x00" && rawValue !== "0x" ? formatWeiValue(rawValue) : void 0;
|
|
24541
|
+
return { from, to, value: value || void 0, isDeployment: !to };
|
|
24542
|
+
}
|
|
24543
|
+
function getMethodLabel(method) {
|
|
24544
|
+
switch (method) {
|
|
24545
|
+
case "eth_sendTransaction":
|
|
24546
|
+
return "Send Transaction";
|
|
24547
|
+
case "eth_sign":
|
|
24548
|
+
return "Sign Message";
|
|
24549
|
+
case "personal_sign":
|
|
24550
|
+
return "Sign Message";
|
|
24551
|
+
case "eth_signTypedData":
|
|
24552
|
+
case "eth_signTypedData_v1":
|
|
24553
|
+
case "eth_signTypedData_v3":
|
|
24554
|
+
case "eth_signTypedData_v4":
|
|
24555
|
+
return "Sign Typed Data";
|
|
24556
|
+
default:
|
|
24557
|
+
return method;
|
|
24558
|
+
}
|
|
24559
|
+
}
|
|
24560
|
+
function SigningMessageView({ request }) {
|
|
24561
|
+
const params = Array.isArray(request.params) ? request.params : [];
|
|
24562
|
+
if (request.method === "personal_sign") {
|
|
24563
|
+
const raw = typeof params[0] === "string" ? params[0] : "";
|
|
24564
|
+
const message = raw.startsWith("0x") ? hexToUtf8(raw) : raw;
|
|
24565
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tx-message-box", children: [
|
|
24566
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tx-field-label", children: "Message" }),
|
|
24567
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tx-message-content", children: message || raw })
|
|
24568
|
+
] });
|
|
24569
|
+
}
|
|
24570
|
+
if (request.method === "eth_sign") {
|
|
24571
|
+
const raw = typeof params[1] === "string" ? params[1] : "";
|
|
24572
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tx-message-box", children: [
|
|
24573
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tx-field-label", children: "Message hash" }),
|
|
24574
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tx-message-content mono", children: raw })
|
|
24575
|
+
] });
|
|
24576
|
+
}
|
|
24577
|
+
if (request.method.startsWith("eth_signTypedData")) {
|
|
24578
|
+
const rawData = typeof params[1] === "string" ? params[1] : JSON.stringify(params[1]);
|
|
24579
|
+
try {
|
|
24580
|
+
const parsed = typeof rawData === "string" ? JSON.parse(rawData) : rawData;
|
|
24581
|
+
const domain = parsed?.domain;
|
|
24582
|
+
const message = parsed?.message;
|
|
24583
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tx-message-box", children: [
|
|
24584
|
+
domain ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tx-typed-section", children: [
|
|
24585
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tx-field-label", children: "Domain" }),
|
|
24586
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { className: "tx-typed-pre", children: JSON.stringify(domain, null, 2) })
|
|
24587
|
+
] }) : null,
|
|
24588
|
+
message ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tx-typed-section", children: [
|
|
24589
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tx-field-label", children: "Message" }),
|
|
24590
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { className: "tx-typed-pre", children: JSON.stringify(message, null, 2) })
|
|
24591
|
+
] }) : null
|
|
24592
|
+
] });
|
|
24593
|
+
} catch {
|
|
24594
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tx-message-box", children: [
|
|
24595
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tx-field-label", children: "Typed data" }),
|
|
24596
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "tx-message-content mono", children: rawData })
|
|
24597
|
+
] });
|
|
24598
|
+
}
|
|
24599
|
+
}
|
|
24600
|
+
return null;
|
|
24511
24601
|
}
|
|
24512
24602
|
function PendingTxCard({
|
|
24513
24603
|
request,
|
|
@@ -24516,26 +24606,63 @@ function PendingTxCard({
|
|
|
24516
24606
|
onReject
|
|
24517
24607
|
}) {
|
|
24518
24608
|
const actionLabel = request.requiresApproval ? "Confirm" : "Run";
|
|
24609
|
+
const txFields = extractTxFields(request);
|
|
24610
|
+
const isSigningMethod = [
|
|
24611
|
+
"eth_sign",
|
|
24612
|
+
"personal_sign",
|
|
24613
|
+
"eth_signTypedData",
|
|
24614
|
+
"eth_signTypedData_v1",
|
|
24615
|
+
"eth_signTypedData_v3",
|
|
24616
|
+
"eth_signTypedData_v4"
|
|
24617
|
+
].includes(request.method);
|
|
24519
24618
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("article", { className: `request-card ${request.status === "processing" ? "processing" : ""}`, children: [
|
|
24520
24619
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "request-top", children: [
|
|
24521
24620
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
24522
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "method", children: request.method }),
|
|
24621
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "method", children: getMethodLabel(request.method) }),
|
|
24523
24622
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "timestamp", children: [
|
|
24524
24623
|
"Queued at ",
|
|
24525
24624
|
formatTimestamp(request.createdAt)
|
|
24526
24625
|
] })
|
|
24527
24626
|
] }),
|
|
24528
24627
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tag-row", children: [
|
|
24628
|
+
txFields?.isDeployment ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "tag deploy", children: "Contract Deployment" }) : null,
|
|
24529
24629
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "tag", children: request.requiresApproval ? "Approval required" : "Read-only passthrough" }),
|
|
24530
24630
|
request.status === "processing" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "tag success", children: "Processing" }) : null
|
|
24531
24631
|
] })
|
|
24532
24632
|
] }),
|
|
24533
|
-
|
|
24534
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
24535
|
-
|
|
24536
|
-
|
|
24633
|
+
txFields ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tx-summary", children: [
|
|
24634
|
+
txFields.isDeployment ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tx-summary-row", children: [
|
|
24635
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "tx-field-label", children: "Action" }),
|
|
24636
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "tx-field-value", children: request.decodedCall?.contractName ? `Deploy ${request.decodedCall.contractName}` : "Deploy new contract" })
|
|
24637
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tx-summary-row", children: [
|
|
24638
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "tx-field-label", children: "To" }),
|
|
24639
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "tx-field-value mono", children: txFields.to ? shortenAddress(txFields.to) : "\u2014" })
|
|
24640
|
+
] }),
|
|
24641
|
+
txFields.from ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tx-summary-row", children: [
|
|
24642
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "tx-field-label", children: "From" }),
|
|
24643
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "tx-field-value mono", children: shortenAddress(txFields.from) })
|
|
24644
|
+
] }) : null,
|
|
24645
|
+
txFields.value ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "tx-summary-row", children: [
|
|
24646
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "tx-field-label", children: "Value" }),
|
|
24647
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "tx-field-value value-accent", children: txFields.value })
|
|
24648
|
+
] }) : null
|
|
24537
24649
|
] }) : null,
|
|
24538
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
24650
|
+
request.decodedCall?.matched ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "decoded-call", children: [
|
|
24651
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("strong", { children: [
|
|
24652
|
+
request.decodedCall.contractName ?? "Contract",
|
|
24653
|
+
".",
|
|
24654
|
+
request.decodedCall.functionName ?? "call"
|
|
24655
|
+
] }),
|
|
24656
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "decoded-sig", children: request.decodedCall.signature }),
|
|
24657
|
+
request.decodedCall.args.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "decoded-args", children: request.decodedCall.args.map((argument, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: argument }, `${request.queueId}-arg-${index}`)) }) : null
|
|
24658
|
+
] }) : request.decodedCall && !request.decodedCall.matched && request.decodedCall.selector ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: "decoded-call unmatched", children: [
|
|
24659
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: "Unknown function" }),
|
|
24660
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "decoded-sig", children: [
|
|
24661
|
+
"Selector: ",
|
|
24662
|
+
request.decodedCall.selector
|
|
24663
|
+
] })
|
|
24664
|
+
] }) : null,
|
|
24665
|
+
isSigningMethod ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SigningMessageView, { request }) : null,
|
|
24539
24666
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "button-row", style: { marginTop: 14 }, children: [
|
|
24540
24667
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "button-primary", disabled: busy, onClick: () => onConfirm(request), children: busy ? "Waiting for wallet..." : actionLabel }),
|
|
24541
24668
|
request.requiresApproval ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "button-danger", disabled: busy, onClick: () => onReject(request), children: "Reject" }) : null
|
|
@@ -24545,6 +24672,15 @@ function PendingTxCard({
|
|
|
24545
24672
|
|
|
24546
24673
|
// src/ui/components/TxHistory.tsx
|
|
24547
24674
|
var import_jsx_runtime2 = __toESM(require_jsx_runtime());
|
|
24675
|
+
var DEPLOYMENT_METHODS = /* @__PURE__ */ new Set([
|
|
24676
|
+
"eth_sendTransaction",
|
|
24677
|
+
"eth_sign",
|
|
24678
|
+
"personal_sign",
|
|
24679
|
+
"eth_signTypedData",
|
|
24680
|
+
"eth_signTypedData_v1",
|
|
24681
|
+
"eth_signTypedData_v3",
|
|
24682
|
+
"eth_signTypedData_v4"
|
|
24683
|
+
]);
|
|
24548
24684
|
function formatTimestamp2(value) {
|
|
24549
24685
|
return new Date(value).toLocaleTimeString([], {
|
|
24550
24686
|
hour: "2-digit",
|
|
@@ -24555,41 +24691,98 @@ function formatTimestamp2(value) {
|
|
|
24555
24691
|
function getStatusClass(status) {
|
|
24556
24692
|
return status === "confirmed" ? "success" : "error";
|
|
24557
24693
|
}
|
|
24558
|
-
function
|
|
24559
|
-
|
|
24694
|
+
function getMethodLabel2(method) {
|
|
24695
|
+
switch (method) {
|
|
24696
|
+
case "eth_sendTransaction":
|
|
24697
|
+
return "Send Transaction";
|
|
24698
|
+
case "eth_sign":
|
|
24699
|
+
return "Sign Message";
|
|
24700
|
+
case "personal_sign":
|
|
24701
|
+
return "Sign Message";
|
|
24702
|
+
case "eth_signTypedData":
|
|
24703
|
+
case "eth_signTypedData_v1":
|
|
24704
|
+
case "eth_signTypedData_v3":
|
|
24705
|
+
case "eth_signTypedData_v4":
|
|
24706
|
+
return "Sign Typed Data";
|
|
24707
|
+
default:
|
|
24708
|
+
return method;
|
|
24709
|
+
}
|
|
24710
|
+
}
|
|
24711
|
+
function getActionSummary(entry) {
|
|
24712
|
+
if (entry.decodedCall?.matched) {
|
|
24713
|
+
const name = entry.decodedCall.contractName ?? "Contract";
|
|
24714
|
+
const fn = entry.decodedCall.functionName ?? "call";
|
|
24715
|
+
return `${name}.${fn}`;
|
|
24716
|
+
}
|
|
24717
|
+
if (entry.method === "eth_sendTransaction") {
|
|
24718
|
+
const params = entry;
|
|
24719
|
+
const first = Array.isArray(params) ? params[0] : void 0;
|
|
24720
|
+
const tx = first && typeof first === "object" ? first : void 0;
|
|
24721
|
+
const hasTo = tx && typeof tx.to === "string" && tx.to.length > 2;
|
|
24722
|
+
return hasTo ? "Contract interaction" : "Contract deployment";
|
|
24723
|
+
}
|
|
24724
|
+
return "";
|
|
24725
|
+
}
|
|
24726
|
+
function getTxHash(entry) {
|
|
24727
|
+
if (typeof entry.result === "string" && entry.result.startsWith("0x") && entry.result.length === 66) {
|
|
24728
|
+
return entry.result;
|
|
24729
|
+
}
|
|
24730
|
+
return void 0;
|
|
24560
24731
|
}
|
|
24561
24732
|
function TxHistory({ entries }) {
|
|
24562
|
-
|
|
24563
|
-
|
|
24733
|
+
const relevant = entries.filter((e) => DEPLOYMENT_METHODS.has(e.method));
|
|
24734
|
+
if (!relevant.length) {
|
|
24735
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "empty-state", children: "Confirmed transactions and signing events will appear here during this session." });
|
|
24564
24736
|
}
|
|
24565
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "card-list", children:
|
|
24566
|
-
|
|
24567
|
-
|
|
24568
|
-
|
|
24569
|
-
|
|
24570
|
-
|
|
24571
|
-
"
|
|
24572
|
-
|
|
24573
|
-
|
|
24574
|
-
|
|
24575
|
-
|
|
24576
|
-
|
|
24577
|
-
|
|
24578
|
-
|
|
24579
|
-
entry.decodedCall.contractName,
|
|
24580
|
-
".",
|
|
24581
|
-
entry.decodedCall.functionName
|
|
24737
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "card-list", children: relevant.map((entry) => {
|
|
24738
|
+
const actionSummary = getActionSummary(entry);
|
|
24739
|
+
const txHash = getTxHash(entry);
|
|
24740
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("article", { className: "history-card", children: [
|
|
24741
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "history-top", children: [
|
|
24742
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
24743
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "method", children: getMethodLabel2(entry.method) }),
|
|
24744
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "timestamp", children: [
|
|
24745
|
+
formatTimestamp2(entry.createdAt),
|
|
24746
|
+
" \u2192 ",
|
|
24747
|
+
formatTimestamp2(entry.completedAt)
|
|
24748
|
+
] })
|
|
24749
|
+
] }),
|
|
24750
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: `tag ${getStatusClass(entry.status)}`, children: entry.status })
|
|
24582
24751
|
] }),
|
|
24583
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children:
|
|
24584
|
-
|
|
24585
|
-
|
|
24586
|
-
|
|
24587
|
-
|
|
24752
|
+
actionSummary ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "history-action", children: actionSummary }) : null,
|
|
24753
|
+
entry.decodedCall?.matched ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { className: "decoded-call", children: [
|
|
24754
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("strong", { children: [
|
|
24755
|
+
entry.decodedCall.contractName,
|
|
24756
|
+
".",
|
|
24757
|
+
entry.decodedCall.functionName
|
|
24758
|
+
] }),
|
|
24759
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "decoded-sig", children: entry.decodedCall.signature })
|
|
24760
|
+
] }) : null,
|
|
24761
|
+
txHash ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "history-hash", children: [
|
|
24762
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "tx-field-label", children: "Tx hash" }),
|
|
24763
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "history-hash-value mono", children: [
|
|
24764
|
+
txHash.slice(0, 18),
|
|
24765
|
+
"...",
|
|
24766
|
+
txHash.slice(-8)
|
|
24767
|
+
] })
|
|
24768
|
+
] }) : null,
|
|
24769
|
+
entry.error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "history-error", children: entry.error.message }) : null
|
|
24770
|
+
] }, entry.queueId);
|
|
24771
|
+
}) });
|
|
24588
24772
|
}
|
|
24589
24773
|
|
|
24590
24774
|
// src/ui/components/WalletBadge.tsx
|
|
24775
|
+
var import_react = __toESM(require_react());
|
|
24591
24776
|
var import_jsx_runtime3 = __toESM(require_jsx_runtime());
|
|
24592
24777
|
function WalletBadge({ wallet }) {
|
|
24778
|
+
const [copied, setCopied] = (0, import_react.useState)(false);
|
|
24779
|
+
function copyAddress() {
|
|
24780
|
+
if (!wallet.account) return;
|
|
24781
|
+
void navigator.clipboard.writeText(wallet.account).then(() => {
|
|
24782
|
+
setCopied(true);
|
|
24783
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
24784
|
+
});
|
|
24785
|
+
}
|
|
24593
24786
|
if (!wallet.available) {
|
|
24594
24787
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "network-pill", children: "MetaMask not detected" });
|
|
24595
24788
|
}
|
|
@@ -24599,21 +24792,105 @@ function WalletBadge({ wallet }) {
|
|
|
24599
24792
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: wallet.locked ? "Locked or not connected" : "Waiting for account access" })
|
|
24600
24793
|
] });
|
|
24601
24794
|
}
|
|
24602
|
-
|
|
24603
|
-
|
|
24604
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.
|
|
24795
|
+
const shortAddress = wallet.account ? `${wallet.account.slice(0, 6)}...${wallet.account.slice(-4)}` : "No account";
|
|
24796
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "wallet-detail-pill", children: [
|
|
24797
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "wallet-network-row", children: [
|
|
24798
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "wallet-dot" }),
|
|
24799
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { className: "wallet-network-name", children: wallet.networkName ?? wallet.chainId ?? "Connected" })
|
|
24800
|
+
] }),
|
|
24801
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "wallet-account-row", children: [
|
|
24802
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "wallet-address", title: wallet.account, children: shortAddress }),
|
|
24803
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "copy-btn", onClick: copyAddress, title: "Copy full address", children: copied ? "\u2713" : "Copy" })
|
|
24804
|
+
] }),
|
|
24805
|
+
wallet.balance ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "wallet-balance", children: wallet.balance }) : null
|
|
24605
24806
|
] });
|
|
24606
24807
|
}
|
|
24607
24808
|
|
|
24608
24809
|
// src/ui/wallet.ts
|
|
24609
24810
|
var CHAIN_NAMES = {
|
|
24811
|
+
// Ethereum
|
|
24610
24812
|
"0x1": "Ethereum Mainnet",
|
|
24611
|
-
"0xaa36a7": "Sepolia",
|
|
24612
|
-
"
|
|
24613
|
-
|
|
24813
|
+
"0xaa36a7": "Sepolia Testnet",
|
|
24814
|
+
"0x5": "Goerli Testnet",
|
|
24815
|
+
// Polygon
|
|
24816
|
+
"0x89": "Polygon Mainnet",
|
|
24817
|
+
"0x13882": "Polygon Amoy Testnet",
|
|
24818
|
+
"0x13881": "Polygon Mumbai Testnet",
|
|
24819
|
+
"0x44d": "Polygon zkEVM",
|
|
24820
|
+
"0x5a2": "Polygon zkEVM Testnet",
|
|
24821
|
+
// BNB Smart Chain
|
|
24822
|
+
"0x38": "BNB Smart Chain",
|
|
24823
|
+
"0x61": "BNB Testnet",
|
|
24824
|
+
// Avalanche
|
|
24825
|
+
"0xa86a": "Avalanche C-Chain",
|
|
24826
|
+
"0xa869": "Avalanche Fuji Testnet",
|
|
24827
|
+
// Arbitrum
|
|
24614
24828
|
"0xa4b1": "Arbitrum One",
|
|
24615
|
-
"
|
|
24616
|
-
"
|
|
24829
|
+
"0x66eee": "Arbitrum Sepolia",
|
|
24830
|
+
"0x66eed": "Arbitrum Goerli",
|
|
24831
|
+
// Optimism
|
|
24832
|
+
"0xa": "Optimism Mainnet",
|
|
24833
|
+
"0xaa37dc": "Optimism Sepolia",
|
|
24834
|
+
"0x1a4": "Optimism Goerli",
|
|
24835
|
+
// Base
|
|
24836
|
+
"0x2105": "Base Mainnet",
|
|
24837
|
+
"0x14a34": "Base Sepolia",
|
|
24838
|
+
// zkSync Era
|
|
24839
|
+
"0x144": "zkSync Era Mainnet",
|
|
24840
|
+
"0x12c": "zkSync Era Sepolia",
|
|
24841
|
+
// Linea
|
|
24842
|
+
"0xe708": "Linea Mainnet",
|
|
24843
|
+
"0xe704": "Linea Goerli",
|
|
24844
|
+
// Scroll
|
|
24845
|
+
"0x82750": "Scroll Mainnet",
|
|
24846
|
+
"0x8274f": "Scroll Sepolia",
|
|
24847
|
+
// Fantom
|
|
24848
|
+
"0xfa": "Fantom Opera",
|
|
24849
|
+
"0xfa2": "Fantom Testnet",
|
|
24850
|
+
// Gnosis
|
|
24851
|
+
"0x64": "Gnosis Chain",
|
|
24852
|
+
"0x27d8": "Chiado Testnet",
|
|
24853
|
+
// Celo
|
|
24854
|
+
"0xa4ec": "Celo Mainnet",
|
|
24855
|
+
"0xaef3": "Celo Alfajores",
|
|
24856
|
+
// Blast
|
|
24857
|
+
"0x13e31": "Blast Mainnet",
|
|
24858
|
+
"0xa0c71fd": "Blast Sepolia",
|
|
24859
|
+
// Mantle
|
|
24860
|
+
"0x1388": "Mantle Mainnet",
|
|
24861
|
+
"0x1389": "Mantle Testnet",
|
|
24862
|
+
// Moonbeam
|
|
24863
|
+
"0x504": "Moonbeam",
|
|
24864
|
+
"0x505": "Moonriver",
|
|
24865
|
+
"0x507": "Moonbase Alpha",
|
|
24866
|
+
// Metis
|
|
24867
|
+
"0x440": "Metis Andromeda",
|
|
24868
|
+
// Cronos
|
|
24869
|
+
"0x19": "Cronos Mainnet",
|
|
24870
|
+
"0x152": "Cronos Testnet",
|
|
24871
|
+
// Local development
|
|
24872
|
+
"0x539": "Hardhat Local (1337)",
|
|
24873
|
+
"0x7a69": "Hardhat / Anvil Local"
|
|
24874
|
+
};
|
|
24875
|
+
var NATIVE_SYMBOLS = {
|
|
24876
|
+
"0x89": "POL",
|
|
24877
|
+
"0x13882": "POL",
|
|
24878
|
+
"0x13881": "POL",
|
|
24879
|
+
"0x44d": "ETH",
|
|
24880
|
+
"0x38": "BNB",
|
|
24881
|
+
"0x61": "BNB",
|
|
24882
|
+
"0xa86a": "AVAX",
|
|
24883
|
+
"0xa869": "AVAX",
|
|
24884
|
+
"0xfa": "FTM",
|
|
24885
|
+
"0xfa2": "FTM",
|
|
24886
|
+
"0x64": "xDAI",
|
|
24887
|
+
"0x27d8": "xDAI",
|
|
24888
|
+
"0xa4ec": "CELO",
|
|
24889
|
+
"0xaef3": "CELO",
|
|
24890
|
+
"0x504": "GLMR",
|
|
24891
|
+
"0x505": "MOVR",
|
|
24892
|
+
"0x19": "CRO",
|
|
24893
|
+
"0x152": "CRO"
|
|
24617
24894
|
};
|
|
24618
24895
|
function getNetworkName(chainId) {
|
|
24619
24896
|
if (!chainId) {
|
|
@@ -24621,6 +24898,21 @@ function getNetworkName(chainId) {
|
|
|
24621
24898
|
}
|
|
24622
24899
|
return CHAIN_NAMES[chainId.toLowerCase()] ?? `Chain ${chainId}`;
|
|
24623
24900
|
}
|
|
24901
|
+
function getNativeSymbol(chainId) {
|
|
24902
|
+
if (!chainId) return "ETH";
|
|
24903
|
+
return NATIVE_SYMBOLS[chainId.toLowerCase()] ?? "ETH";
|
|
24904
|
+
}
|
|
24905
|
+
function formatBalance(balanceHex, symbol) {
|
|
24906
|
+
try {
|
|
24907
|
+
const wei = BigInt(balanceHex);
|
|
24908
|
+
const ethValue = Number(wei) / 1e18;
|
|
24909
|
+
if (ethValue === 0) return `0 ${symbol}`;
|
|
24910
|
+
if (ethValue < 1e-4) return `< 0.0001 ${symbol}`;
|
|
24911
|
+
return `${ethValue.toFixed(4)} ${symbol}`;
|
|
24912
|
+
} catch {
|
|
24913
|
+
return `\u2014 ${symbol}`;
|
|
24914
|
+
}
|
|
24915
|
+
}
|
|
24624
24916
|
function getEthereumProvider() {
|
|
24625
24917
|
return window.ethereum;
|
|
24626
24918
|
}
|
|
@@ -24639,6 +24931,20 @@ async function getWalletStatus(provider = getEthereumProvider()) {
|
|
|
24639
24931
|
]);
|
|
24640
24932
|
const accounts = Array.isArray(accountsResult) ? accountsResult : [];
|
|
24641
24933
|
const chainId = typeof chainIdResult === "string" ? chainIdResult : void 0;
|
|
24934
|
+
const nativeSymbol = getNativeSymbol(chainId);
|
|
24935
|
+
let balance;
|
|
24936
|
+
if (accounts.length > 0) {
|
|
24937
|
+
try {
|
|
24938
|
+
const balanceHex = await provider.request({
|
|
24939
|
+
method: "eth_getBalance",
|
|
24940
|
+
params: [accounts[0], "latest"]
|
|
24941
|
+
});
|
|
24942
|
+
if (typeof balanceHex === "string") {
|
|
24943
|
+
balance = formatBalance(balanceHex, nativeSymbol);
|
|
24944
|
+
}
|
|
24945
|
+
} catch {
|
|
24946
|
+
}
|
|
24947
|
+
}
|
|
24642
24948
|
return {
|
|
24643
24949
|
available: true,
|
|
24644
24950
|
connected: accounts.length > 0,
|
|
@@ -24646,7 +24952,9 @@ async function getWalletStatus(provider = getEthereumProvider()) {
|
|
|
24646
24952
|
walletName: provider.isMetaMask ? "MetaMask" : "Browser Wallet",
|
|
24647
24953
|
account: accounts[0],
|
|
24648
24954
|
chainId,
|
|
24649
|
-
networkName: getNetworkName(chainId)
|
|
24955
|
+
networkName: getNetworkName(chainId),
|
|
24956
|
+
balance,
|
|
24957
|
+
nativeSymbol
|
|
24650
24958
|
};
|
|
24651
24959
|
}
|
|
24652
24960
|
async function connectWallet(provider = getEthereumProvider()) {
|
|
@@ -24731,15 +25039,15 @@ function formatLastUpdated(timestamp) {
|
|
|
24731
25039
|
});
|
|
24732
25040
|
}
|
|
24733
25041
|
function App() {
|
|
24734
|
-
const [snapshot, setSnapshot] = (0,
|
|
24735
|
-
const [wallet, setWallet] = (0,
|
|
24736
|
-
const [socketConnected, setSocketConnected] = (0,
|
|
24737
|
-
const [busyIds, setBusyIds] = (0,
|
|
24738
|
-
const [errorMessage, setErrorMessage] = (0,
|
|
24739
|
-
const socketRef = (0,
|
|
24740
|
-
const autoRunningIdsRef = (0,
|
|
25042
|
+
const [snapshot, setSnapshot] = (0, import_react2.useState)(INITIAL_SNAPSHOT);
|
|
25043
|
+
const [wallet, setWallet] = (0, import_react2.useState)(EMPTY_WALLET_STATE);
|
|
25044
|
+
const [socketConnected, setSocketConnected] = (0, import_react2.useState)(false);
|
|
25045
|
+
const [busyIds, setBusyIds] = (0, import_react2.useState)([]);
|
|
25046
|
+
const [errorMessage, setErrorMessage] = (0, import_react2.useState)(null);
|
|
25047
|
+
const socketRef = (0, import_react2.useRef)(null);
|
|
25048
|
+
const autoRunningIdsRef = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
|
|
24741
25049
|
const provider = getEthereumProvider();
|
|
24742
|
-
(0,
|
|
25050
|
+
(0, import_react2.useEffect)(() => {
|
|
24743
25051
|
const socket = new WebSocket(`${window.location.origin.replace(/^http/, "ws")}/ws`);
|
|
24744
25052
|
socketRef.current = socket;
|
|
24745
25053
|
socket.onopen = async () => {
|
|
@@ -24769,7 +25077,7 @@ function App() {
|
|
|
24769
25077
|
socket.close();
|
|
24770
25078
|
};
|
|
24771
25079
|
}, [provider]);
|
|
24772
|
-
(0,
|
|
25080
|
+
(0, import_react2.useEffect)(() => {
|
|
24773
25081
|
const unsubscribe = subscribeToWallet(async () => {
|
|
24774
25082
|
try {
|
|
24775
25083
|
const nextWallet = await getWalletStatus(provider);
|
|
@@ -24812,7 +25120,7 @@ function App() {
|
|
|
24812
25120
|
setBusyIds((current) => current.filter((id) => id !== request.queueId));
|
|
24813
25121
|
}
|
|
24814
25122
|
}
|
|
24815
|
-
(0,
|
|
25123
|
+
(0, import_react2.useEffect)(() => {
|
|
24816
25124
|
if (!wallet.connected) {
|
|
24817
25125
|
return;
|
|
24818
25126
|
}
|
|
@@ -24900,8 +25208,8 @@ function App() {
|
|
|
24900
25208
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { children: wallet.networkName ?? wallet.chainId ?? "Not connected" })
|
|
24901
25209
|
] }),
|
|
24902
25210
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "metric", children: [
|
|
24903
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "label", children: "
|
|
24904
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { children: wallet.
|
|
25211
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "label", children: "Balance" }),
|
|
25212
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { children: wallet.balance ?? (wallet.connected ? "Fetching\u2026" : "\u2014") })
|
|
24905
25213
|
] })
|
|
24906
25214
|
] }),
|
|
24907
25215
|
warnings.length > 0 || errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { children: [
|