Skip to content

Commit e828f45

Browse files
committed
fix(scan): probe block-by-hash for 64-hex search queries
Before: 64-hex regex matched but only ran probeTx() on both networks. A user pasting a block hash → both probes fail → "Transaction not found" error message even though the hash IS a valid block hash. After: 4 parallel probes (tx + block on current + other network). Block hits resolve through getBlock({blockHash}) and route to /blocks/<resolved-height> since the block route is height-keyed. Tx still wins precedence — they're disjoint hash spaces in practice but the explicit ordering keeps semantics deterministic. Cost: 4 RPC calls instead of 2, but they run concurrently → wall time stays bounded by the slowest single probe (~50ms).
1 parent c072fd2 commit e828f45

1 file changed

Lines changed: 41 additions & 12 deletions

File tree

apps/scan/lib/search-validate.ts

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,23 @@ async function probeTx(network: NetworkId, hash: `0x${string}`): Promise<boolean
5454
}
5555
}
5656

57+
// 64-hex strings are ambiguous between tx hashes and block hashes. We
58+
// probe both in parallel; if a block matches we resolve the height so
59+
// the caller can route to /blocks/<height> (the only block route is
60+
// height-keyed).
61+
async function probeBlockByHash(
62+
network: NetworkId,
63+
hash: `0x${string}`,
64+
): Promise<bigint | null> {
65+
try {
66+
const client = createClient(network);
67+
const block = await client.getBlock({ blockHash: hash });
68+
return block?.number ?? null;
69+
} catch {
70+
return null;
71+
}
72+
}
73+
5774
export async function validateAndResolveSearch(
5875
network: NetworkId,
5976
query: string,
@@ -77,25 +94,37 @@ export async function validateAndResolveSearch(
7794
return { kind: "not_found", reason: `Block #${q} not found on either network` };
7895
}
7996

80-
// Transaction hash — accept both 0x-prefixed (wallet shape) and bare
81-
// 64-hex (Sentrix internal shape). viem getTransaction needs the 0x
82-
// form, so we prepend if missing before probing. The href routes
83-
// through with whatever the user typed — fetchTransaction strips 0x
84-
// again for the REST call internally.
97+
// 64-hex — ambiguous between tx hash and block hash. Accept both
98+
// 0x-prefixed (wallet shape) and bare (Sentrix internal shape).
99+
// viem needs the 0x form for both getTransaction + getBlock, so we
100+
// prepend if missing before probing. Run all four probes (tx + block
101+
// on current + other network) in parallel — at chain-RPC latencies
102+
// (~50ms each) the cost is dominated by the slowest, not the sum.
85103
if (/^(0x)?[a-fA-F0-9]{64}$/.test(q)) {
86104
const probeHash = (q.startsWith("0x") ? q : `0x${q}`) as `0x${string}`;
87-
const [hereOk, otherOk] = await Promise.all([
105+
const other = OTHER[network];
106+
const [txHere, txOther, blockHere, blockOther] = await Promise.all([
88107
probeTx(network, probeHash),
89-
probeTx(OTHER[network], probeHash),
108+
probeTx(other, probeHash),
109+
probeBlockByHash(network, probeHash),
110+
probeBlockByHash(other, probeHash),
90111
]);
91-
if (hereOk) return { kind: "tx", href: `/tx/${q}`, onNetwork: network };
92-
if (otherOk)
112+
if (txHere) return { kind: "tx", href: `/tx/${q}`, onNetwork: network };
113+
if (blockHere !== null)
114+
return { kind: "block", href: `/blocks/${blockHere}`, onNetwork: network };
115+
if (txOther)
93116
return {
94117
kind: "tx",
95-
href: withNetwork(`/tx/${q}`, network, OTHER[network]),
96-
onNetwork: OTHER[network],
118+
href: withNetwork(`/tx/${q}`, network, other),
119+
onNetwork: other,
120+
};
121+
if (blockOther !== null)
122+
return {
123+
kind: "block",
124+
href: withNetwork(`/blocks/${blockOther}`, network, other),
125+
onNetwork: other,
97126
};
98-
return { kind: "not_found", reason: "Transaction not found on either network" };
127+
return { kind: "not_found", reason: "No transaction or block with that hash on either network" };
99128
}
100129

101130
// Address — viem's `isAddress` accepts 0x-prefixed only. Sentrix's

0 commit comments

Comments
 (0)