From e2b61db480fd403b94ae88cea9deba8e8da426b3 Mon Sep 17 00:00:00 2001 From: David Rusu Date: Mon, 16 Feb 2026 23:46:08 +0400 Subject: [PATCH] query tx by fork --- src/api/v1/transactions.py | 4 ++-- src/db/transaction.py | 10 +++++++--- static/lib/api.js | 3 ++- static/pages/TransactionDetail.js | 12 ++++++++++-- 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/api/v1/transactions.py b/src/api/v1/transactions.py index 4cd4e67..1f1e7ff 100644 --- a/src/api/v1/transactions.py +++ b/src/api/v1/transactions.py @@ -41,11 +41,11 @@ async def stream( return NDJsonStreamingResponse(ndjson_transactions_stream) -async def get(request: NBERequest, transaction_hash: str) -> Response: +async def get(request: NBERequest, transaction_hash: str, fork: int = Query(...)) -> Response: if not transaction_hash: return Response(status_code=NOT_FOUND) transaction_hash = dehexify(transaction_hash) - transaction = await request.app.state.transaction_repository.get_by_hash(transaction_hash) + transaction = await request.app.state.transaction_repository.get_by_hash(transaction_hash, fork=fork) return transaction.map( lambda _transaction: JSONResponse(TransactionRead.from_transaction(_transaction).model_dump(mode="json")) ).unwrap_or_else(lambda: Response(status_code=NOT_FOUND)) diff --git a/src/db/transaction.py b/src/db/transaction.py index ca318c8..892cd3e 100644 --- a/src/db/transaction.py +++ b/src/db/transaction.py @@ -51,12 +51,16 @@ class TransactionRepository: else: return Empty() - async def get_by_hash(self, transaction_hash: bytes) -> Option[Transaction]: - statement = select(Transaction).where(Transaction.hash == transaction_hash) + async def get_by_hash(self, transaction_hash: bytes, *, fork: int) -> Option[Transaction]: + statement = ( + select(Transaction) + .join(Block, Transaction.block_id == Block.id) + .where(Transaction.hash == transaction_hash, Block.fork == fork) + ) with self.client.session() as session: result: Result[Transaction] = session.exec(statement) - if (transaction := result.one_or_none()) is not None: + if (transaction := result.first()) is not None: return Some(transaction) else: return Empty() diff --git a/static/lib/api.js b/static/lib/api.js index 24a9e63..214ae80 100644 --- a/static/lib/api.js +++ b/static/lib/api.js @@ -8,7 +8,8 @@ const encodeHash = (hash) => encodeURIComponent(String(hash)); const HEALTH_ENDPOINT = joinUrl(API_PREFIX, 'health/stream'); -const TRANSACTION_DETAIL_BY_HASH = (hash) => joinUrl(API_PREFIX, 'transactions', encodeHash(hash)); +const TRANSACTION_DETAIL_BY_HASH = (hash, fork) => + `${joinUrl(API_PREFIX, 'transactions', encodeHash(hash))}?fork=${encodeURIComponent(fork)}`; const TRANSACTIONS_STREAM = joinUrl(API_PREFIX, 'transactions/stream'); const FORK_CHOICE = joinUrl(API_PREFIX, 'fork-choice'); diff --git a/static/pages/TransactionDetail.js b/static/pages/TransactionDetail.js index 5c7a30a..e80e33f 100644 --- a/static/pages/TransactionDetail.js +++ b/static/pages/TransactionDetail.js @@ -3,6 +3,7 @@ import { h, Fragment } from 'preact'; import { useEffect, useMemo, useState } from 'preact/hooks'; import { API, PAGE, BASE_PATH } from '../lib/api.js'; import { shortenHex } from '../lib/utils.js'; +import { subscribeFork } from '../lib/fork.js'; // ————— helpers ————— const isNumber = (v) => typeof v === 'number' && !Number.isNaN(v); @@ -485,6 +486,11 @@ export default function TransactionDetail({ parameters }) { const [tx, setTx] = useState(null); const [err, setErr] = useState(null); // { kind: 'invalid'|'not-found'|'network', msg: string } + const [fork, setFork] = useState(null); + + useEffect(() => { + return subscribeFork((newFork) => setFork(newFork)); + }, []); const pageTitle = useMemo(() => `Transaction ${shortenHex(transactionHash)}`, [transactionHash]); useEffect(() => { @@ -500,12 +506,14 @@ export default function TransactionDetail({ parameters }) { return; } + if (fork == null) return; + let alive = true; const controller = new AbortController(); (async () => { try { - const res = await fetch(API.TRANSACTION_DETAIL_BY_HASH(transactionHash), { + const res = await fetch(API.TRANSACTION_DETAIL_BY_HASH(transactionHash, fork), { cache: 'no-cache', signal: controller.signal, }); @@ -527,7 +535,7 @@ export default function TransactionDetail({ parameters }) { alive = false; controller.abort(); }; - }, [transactionHash, isValidHash]); + }, [transactionHash, isValidHash, fork]); return h( 'main',