Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
75ab6ad
feat(tx): add TX Analyser with call tree, state changes, and contract…
AugustoL Mar 6, 2026
09dfa1e
fix(search): block ENS resolution on non-EVM networks
AugustoL Mar 11, 2026
bc7cfed
fix(bitcoin): decode and display OP_RETURN payload in transaction out…
AugustoL Mar 11, 2026
d8d8899
Merge pull request #302 from AugustoL/fix/ens-search-non-evm-networks
AugustoL Mar 12, 2026
784678a
fix(routing): redirect to home with warning for invalid network URLs
AugustoL Mar 12, 2026
c80adf8
style(notifications): yellow warning style, rounded borders, position…
AugustoL Mar 12, 2026
2ad6ba3
fix(notifications): constrain width, float over content, prevent dupl…
AugustoL Mar 12, 2026
e82b161
Merge pull request #305 from AugustoL/fix/invalid-network-routing
AugustoL Mar 12, 2026
dfd82ba
feat(ai): add free Groq AI analysis via Cloudflare Worker proxy
AugustoL Mar 12, 2026
3780c54
fix(worker): fix CORS wildcard matching for Netlify preview URLs
AugustoL Mar 12, 2026
475b027
fix(nav): prevent Super User toggle button layout shift
AugustoL Mar 12, 2026
a676b73
feat(rpc): auto-sync RPCs every 24h and add Alchemy Bitcoin endpoints
AugustoL Mar 12, 2026
3ff5a44
chore: bump version to 1.2.4-alpha
AugustoL Mar 12, 2026
60385b9
Merge pull request #307 from AugustoL/fix/super-user-toggle-position
AugustoL Mar 12, 2026
2c03a71
Merge pull request #306 from AugustoL/feat/ai-cloudflare-worker
AugustoL Mar 12, 2026
f3f10a7
Merge pull request #303 from AugustoL/fix/op-return-bitcoin
AugustoL Mar 12, 2026
40342df
Merge pull request #308 from AugustoL/feat/rpc-24h-sync-and-alchemy-btc
AugustoL Mar 12, 2026
40bb693
Merge pull request #309 from AugustoL/chore/bump-v1.2.4-alpha
AugustoL Mar 12, 2026
c58d21a
Merge branch 'dev' into feat/tx-analyser
AugustoL Mar 12, 2026
5d95719
feat(tx-analyser): add Gas Profiler tab
AugustoL Mar 12, 2026
9c60db2
feat(tx-analyser): replace table gas profiler with flame chart and wa…
AugustoL Mar 12, 2026
292358f
Merge remote-tracking branch 'openscan-explorer/dev' into feat/tx-ana…
AugustoL Mar 12, 2026
838791e
feat(tx-analyser): move event logs into analyser as Events tab
AugustoL Mar 12, 2026
bd8f094
feat(tx-analyser): move input data into analyser as Input Data tab
AugustoL Mar 12, 2026
d6f137f
feat(tx-analyser): always show analyser in super user mode
AugustoL Mar 12, 2026
73b9038
fix(tx-analyser): prevent rendering before contract enrichment starts
AugustoL Mar 12, 2026
35eb444
fix(tx-analyser): ensure enrichment blocks rendering until complete
AugustoL Mar 12, 2026
fcdea82
fix(contractLookup): use correct Sourcify V2 API fields
AugustoL Mar 12, 2026
e1aa71e
feat(tx-analyser): show analyser for all users with Events and Input …
AugustoL Mar 12, 2026
0d7154d
style(tx-analyser): differentiate base and super user tab highlights
AugustoL Mar 12, 2026
cd81a3e
feat(tx-analyser): collapsible events/state changes, full addresses, …
AugustoL Mar 13, 2026
3a5575c
feat(tx-analyser): enrich log addresses and resolve proxy ABIs
AugustoL Mar 13, 2026
59d9ac5
fix(tx): prevent re-fetch when toggling super user mode
AugustoL Mar 13, 2026
93df6b6
feat(tx-analyser): add collapse/expand toggle with smart defaults
AugustoL Mar 13, 2026
50a7c5d
refactor(tx-analyser): split into separate files and fix review findings
AugustoL Mar 13, 2026
c5fbff4
feat(settings): add export/import configuration
AugustoL Mar 13, 2026
315d089
Merge pull request #281 from openscan-explorer/feat/tx-analyser
AugustoL Mar 13, 2026
7dadf7d
feat(settings): add devtools-style tabs and autosave config
josealoha666 Mar 12, 2026
38710ec
fix(settings): require manual save on providers tab
josealoha666 Mar 12, 2026
bd03364
fix(settings): save providers config on first click and align status row
josealoha666 Mar 12, 2026
f62b246
Refine Providers tab save row and prompt spacing
josealoha666 Mar 12, 2026
2c0391c
fix(settings): keep providers save button centered with right-aligned…
josealoha666 Mar 12, 2026
677b1ca
fix(settings): align providers save button and status on one desktop row
josealoha666 Mar 12, 2026
2c0eae8
fix(settings): disable autosave indicators in providers tab
josealoha666 Mar 12, 2026
69a1a97
fix(settings): address rpc tag static interaction a11y lint
josealoha666 Mar 12, 2026
78cfc0a
fix(settings): remove duplicate tab hash and group provider keys
josealoha666 Mar 12, 2026
60d0cf8
fix(settings): normalize tabs URL and split API key sections
josealoha666 Mar 12, 2026
d0d216b
feat(evm): display EIP-4844 blob metadata on blocks and transactions
AugustoL Mar 16, 2026
0286df9
fix(address): recognize Sourcify V2 "match" value as verified
AugustoL Mar 16, 2026
49b4ee0
Merge pull request #316 from AugustoL/fix/issue-312-sourcify-match
AugustoL Mar 16, 2026
442a54e
Merge pull request #315 from AugustoL/feat/eip-4844-blob-metadata
AugustoL Mar 16, 2026
448789f
Merge pull request #314 from AugustoL/fix/issue-292-config-tabs-v2
AugustoL Mar 16, 2026
1ab188f
merge: resolve dev merge conflicts in settings page
AugustoL Mar 16, 2026
0663699
fix(ci): fail audit on moderate+ severity vulnerabilities
AugustoL Mar 16, 2026
8b55597
Merge pull request #310 from AugustoL/feat/config-export-import
AugustoL Mar 16, 2026
a75a8bb
fix(settings): prevent tab flicker, always show super user section, a…
AugustoL Mar 16, 2026
4e88b0c
feat(settings): add super user toggle to advanced tab
AugustoL Mar 16, 2026
ef25cf5
feat(tx): decode and display UTF-8 text in transaction input data
AugustoL Mar 16, 2026
cb382cf
Merge pull request #319 from AugustoL/fix/settings-tab-flicker
AugustoL Mar 16, 2026
3e1941b
Merge pull request #317 from AugustoL/fix/audit-fail-on-moderate
AugustoL Mar 16, 2026
1426f3b
Merge pull request #320 from AugustoL/feat/decode-utf8-input-data
AugustoL Mar 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "openscan",
"version": "1.2.3-alpha",
"version": "1.2.4-alpha",
"private": true,
"type": "module",
"packageManager": "bun@1.1.0",
Expand Down
2 changes: 1 addition & 1 deletion scripts/audit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ echo "Generating package-lock.json..."
npm i --package-lock-only --silent

echo "Running npm audit..."
npm audit "$@" || true
npm audit --audit-level=moderate "$@"

echo "Cleaning up..."
rm -f package-lock.json
Expand Down
25 changes: 14 additions & 11 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import ErrorBoundary from "./components/common/ErrorBoundary";
import Footer from "./components/common/Footer";
import { IsometricBlocks } from "./components/common/IsometricBlocks";
import NotificationDisplay from "./components/common/NotificationDisplay";
import ValidateNetwork from "./components/common/ValidateNetwork";
import Navbar from "./components/navbar";
import "./styles/base.css";
import "./styles/styles.css";
Expand Down Expand Up @@ -148,17 +149,19 @@ function AppContent() {
<Route path="tbtc/address/:address" element={<LazyBitcoinAddress />} />
<Route path="tbtc/mempool" element={<LazyBitcoinMempool />} />
<Route path="tbtc/mempool/:filter" element={<LazyBitcoinTx />} />
{/* EVM network routes */}
<Route path=":networkId" element={<LazyChain />} />
<Route path=":networkId/gastracker" element={<LazyGasTracker />} />
<Route path=":networkId/blocks" element={<LazyBlocks />} />
<Route path=":networkId/block/:filter" element={<LazyBlock />} />
<Route path=":networkId/txs" element={<LazyTxs />} />
<Route path=":networkId/tx/:filter" element={<LazyTx />} />
<Route path=":networkId/address/:address" element={<LazyAddress />} />
<Route path=":networkId/address/:address/:tokenId" element={<LazyTokenDetails />} />
<Route path=":networkId/mempool" element={<LazyMempool />} />
<Route path=":networkId/mempool/:filter" element={<LazyTx />} />
{/* EVM network routes — validated */}
<Route path=":networkId" element={<ValidateNetwork />}>
<Route index element={<LazyChain />} />
<Route path="gastracker" element={<LazyGasTracker />} />
<Route path="blocks" element={<LazyBlocks />} />
<Route path="block/:filter" element={<LazyBlock />} />
<Route path="txs" element={<LazyTxs />} />
<Route path="tx/:filter" element={<LazyTx />} />
<Route path="address/:address" element={<LazyAddress />} />
<Route path="address/:address/:tokenId" element={<LazyTokenDetails />} />
<Route path="mempool" element={<LazyMempool />} />
<Route path="mempool/:filter" element={<LazyTx />} />
</Route>
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
<Footer />
Expand Down
30 changes: 30 additions & 0 deletions src/components/common/ValidateNetwork.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useEffect, useRef } from "react";
import { useTranslation } from "react-i18next";
import { Navigate, Outlet, useParams } from "react-router-dom";
import { isNetworkEnabled } from "../../config/networks";
import { useNotifications } from "../../context/NotificationContext";

export default function ValidateNetwork() {
const { networkId } = useParams<{ networkId: string }>();
const { addNotification } = useNotifications();
const { t } = useTranslation();
const isValid = !!networkId && isNetworkEnabled(networkId);
const notifiedRef = useRef(false);

useEffect(() => {
if (!isValid && !notifiedRef.current) {
notifiedRef.current = true;
addNotification(
t("errors.networkNotFoundMessage", { network: networkId || "" }),
"warning",
8000,
);
}
}, [isValid, networkId, addNotification, t]);

if (!isValid) {
return <Navigate to="/" replace />;
}

return <Outlet />;
}
77 changes: 36 additions & 41 deletions src/components/navbar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,47 +108,6 @@ const Navbar = () => {

{/* Desktop icons - hidden on mobile */}
<ul className="hide-mobile">
{isSuperUser && (
<li>
<span className="navbar-super-user-badge">{t("nav.superUserBadge")}</span>
</li>
)}
<li>
<button
type="button"
onClick={toggleSuperUserMode}
className={`navbar-toggle-btn ${isSuperUser ? "navbar-toggle-active" : ""}`}
aria-label={isSuperUser ? t("nav.disableSuperUser") : t("nav.enableSuperUser")}
title={isSuperUser ? "Disable Super User Mode" : "Enable Super User Mode"}
>
<svg
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<title>Super User Mode</title>
<polyline
points="4 17 10 11 4 5"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<line
x1="12"
y1="19"
x2="20"
y2="19"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
/>
</svg>
</button>
</li>
{isSuperUser && (
<li>
<button
Expand Down Expand Up @@ -267,6 +226,42 @@ const Navbar = () => {
)}
</button>
</li>
<li>
<button
type="button"
onClick={toggleSuperUserMode}
className={`navbar-toggle-btn ${isSuperUser ? "navbar-toggle-active" : ""}`}
aria-label={isSuperUser ? t("nav.disableSuperUser") : t("nav.enableSuperUser")}
title={isSuperUser ? "Disable Super User Mode" : "Enable Super User Mode"}
>
<svg
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<title>Super User Mode</title>
<polyline
points="4 17 10 11 4 5"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<line
x1="12"
y1="19"
x2="20"
y2="19"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
/>
</svg>
</button>
</li>
<li>
<button
type="button"
Expand Down
24 changes: 19 additions & 5 deletions src/components/pages/bitcoin/BitcoinTransactionDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,28 @@ import {
import {
calculateTotalInput,
calculateTotalOutput,
decodeOpReturnData,
hasWitness,
isCoinbaseTransaction,
isRBFEnabled,
} from "../../../utils/bitcoinUtils";
import AIAnalysisPanel from "../../common/AIAnalysis/AIAnalysisPanel";

const OpReturnDisplay: React.FC<{ hex: string }> = ({ hex }) => {
const decoded = useMemo(() => decodeOpReturnData(hex), [hex]);
return (
<div className="btc-op-return">
<span className="btc-op-return-label">OP_RETURN</span>
{decoded.text ? (
<span className="btc-op-return-text">{decoded.text}</span>
) : (
<span className="btc-op-return-hex tx-mono">{decoded.hex}</span>
)}
<CopyButton value={decoded.text || decoded.hex} />
</div>
);
};

interface BitcoinTransactionDisplayProps {
transaction: BitcoinTransaction;
networkId?: string;
Expand Down Expand Up @@ -422,12 +438,10 @@ const BitcoinTransactionDisplay: React.FC<BitcoinTransactionDisplayProps> = Reac
)}
<CopyButton value={output.scriptPubKey.address} />
</>
) : output.scriptPubKey.type === "nulldata" ? (
<OpReturnDisplay hex={output.scriptPubKey.hex} />
) : (
<span className="tx-mono text-muted">
{output.scriptPubKey.type === "nulldata"
? "OP_RETURN (Data)"
: output.scriptPubKey.type}
</span>
<span className="tx-mono text-muted">{output.scriptPubKey.type}</span>
)}
</div>
<div className="btc-io-details">
Expand Down
26 changes: 26 additions & 0 deletions src/components/pages/evm/block/BlockDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,14 @@ const BlockDisplay: React.FC<BlockDisplayProps> = React.memo(
</span>
</div>
)}

{/* Blob Gas Used (EIP-4844) */}
{block.blobGasUsed && Number(block.blobGasUsed) > 0 && (
<div className="tx-row tx-row-blob">
<span className="tx-label">{t("blobGasUsed")}</span>
<span className="tx-value">{Number(block.blobGasUsed).toLocaleString()}</span>
</div>
)}
</div>

{/* Right Column */}
Expand Down Expand Up @@ -287,6 +295,24 @@ const BlockDisplay: React.FC<BlockDisplayProps> = React.memo(
</div>
)}

{/* Blob fields continued (EIP-4844) */}
{block.blobGasUsed && Number(block.blobGasUsed) > 0 && (
<>
<div className="tx-row tx-row-blob">
<span className="tx-label">{t("excessBlobGas")}</span>
<span className="tx-value">
{Number(block.excessBlobGas).toLocaleString()}
</span>
</div>
<div className="tx-row tx-row-blob">
<span className="tx-label">{t("blobCount")}</span>
<span className="tx-value">
{Math.floor(Number(block.blobGasUsed) / 131072)}
</span>
</div>
</>
)}

{/* Arbitrum-specific fields */}
{isArbitrumBlock(block) && (
<>
Expand Down
Loading
Loading