From 5e43b8a185af696632d9c3b95ae5f51a66d684ea Mon Sep 17 00:00:00 2001 From: Victor Oliva Date: Thu, 7 May 2026 11:31:18 +0200 Subject: [PATCH] refactor: move extrinsic stuff to a separate folder --- .../Extrinsics}/MortalityAnalyzer.tsx | 41 ++++++++----------- .../Extrinsics}/Priority.tsx | 18 +++----- src/components/Extrinsics/Sender.tsx | 19 +++++++++ .../Extrinsics}/SignedExtensions.tsx | 8 ++-- src/components/Extrinsics/index.ts | 4 ++ src/pages/Explorer/BlockPopover.tsx | 2 +- src/pages/Explorer/BlockTable.tsx | 10 ++--- src/pages/Explorer/Detail/BlockAuthor.tsx | 2 +- src/pages/Explorer/Detail/BlockBody.tsx | 3 +- src/pages/Explorer/Detail/BlockDetail.tsx | 3 +- src/pages/Explorer/Detail/BlockEvents.tsx | 2 +- src/pages/Explorer/Detail/BlockInfo.tsx | 8 +++- src/pages/Explorer/Detail/BlockState.tsx | 4 +- .../Explorer/Detail/BlockStorageDiff.tsx | 4 +- src/pages/Explorer/Detail/Extrinsic.tsx | 35 +++++----------- src/pages/Explorer/Detail/blockContext.ts | 4 -- src/pages/Explorer/Events.tsx | 2 +- src/pages/Explorer/events.state.ts | 2 +- src/pages/Explorer/index.ts | 2 +- .../Extrinsics/Analyzer/ExtrinsicDecoder.tsx | 11 ++--- src/pages/Storage/BlockPicker.tsx | 2 +- src/pages/Storage/StorageSubscriptions.tsx | 4 +- src/{pages/Explorer => state}/block.state.ts | 2 + 23 files changed, 94 insertions(+), 98 deletions(-) rename src/{pages/Explorer/Detail => components/Extrinsics}/MortalityAnalyzer.tsx (85%) rename src/{pages/Extrinsics/Analyzer => components/Extrinsics}/Priority.tsx (95%) create mode 100644 src/components/Extrinsics/Sender.tsx rename src/{pages/Explorer/Detail => components/Extrinsics}/SignedExtensions.tsx (90%) create mode 100644 src/components/Extrinsics/index.ts delete mode 100644 src/pages/Explorer/Detail/blockContext.ts rename src/{pages/Explorer => state}/block.state.ts (99%) diff --git a/src/pages/Explorer/Detail/MortalityAnalyzer.tsx b/src/components/Extrinsics/MortalityAnalyzer.tsx similarity index 85% rename from src/pages/Explorer/Detail/MortalityAnalyzer.tsx rename to src/components/Extrinsics/MortalityAnalyzer.tsx index 1931b59..e78d4c6 100644 --- a/src/pages/Explorer/Detail/MortalityAnalyzer.tsx +++ b/src/components/Extrinsics/MortalityAnalyzer.tsx @@ -1,9 +1,9 @@ +import { BlockContext } from "@/state/block.state" import { client$ } from "@/state/chains/chain.state" import { useStateObservable, withDefault } from "@react-rxjs/core" import { jsonSerialize } from "polkadot-api/utils" import { FC, useContext } from "react" import { map, switchMap } from "rxjs" -import { BlockContext } from "./blockContext" const finalizedNumber$ = client$.pipeState( switchMap((v) => v.bestBlocks$), @@ -29,8 +29,8 @@ export const MortalityAnalyzer: FC<{ ) } - const parseResult = /^Mortal(\d+)$/.exec(mortality.type) - if (parseResult === null) { + const decoded = decodeMortality(mortality) + if (decoded === null) { return (
@@ -43,14 +43,7 @@ export const MortalityAnalyzer: FC<{ ) } - const first = BigInt(parseResult[1]) - const second = BigInt(mortality.value) - // from polkadot-sdk primitives runtime generic era fn decode - const encoded = first + (second << 8n) - const period = Number(2n << (encoded % (1n << 4n))) - const factor = period >> 12 || 1 - const phase = Number(encoded >> 4n) * factor - + const { period, phase } = decoded // From fn birth const birthBlock = selectedBlockNumber ? Math.floor((Math.max(selectedBlockNumber, phase) - phase) / period) * @@ -69,12 +62,8 @@ export const MortalityAnalyzer: FC<{ return (
-
-
-
- Lifetime -
-
+
+ Lifetime
@@ -152,11 +141,7 @@ const Metric: FC<{ label: string; value: string }> = ({ label, value }) => ( const formatOptional = (value: number | null) => value == null ? "N/A" : value.toLocaleString() -const getTimelineProgress = ( - start: number, - end: number, - value: number, -): number => { +const getTimelineProgress = (start: number, end: number, value: number) => { if (end <= start) return 0 const raw = ((value - start) / (end - start)) * 100 return Math.max(0, Math.min(100, raw)) @@ -167,8 +152,15 @@ export const InlineMortality: FC<{ }> = ({ mortality }) => { if (mortality.type === "Immortal") return "Immortal" + const decoded = decodeMortality(mortality) + if (decoded === null) return JSON.stringify(mortality, jsonSerialize) + + return JSON.stringify(decoded) + " " + JSON.stringify(mortality) +} + +const decodeMortality = (mortality: { type: string; value: number }) => { const parseResult = /^Mortal(\d+)$/.exec(mortality.type) - if (parseResult === null) return JSON.stringify(mortality, jsonSerialize) + if (!parseResult) return null const first = BigInt(parseResult[1]) const second = BigInt(mortality.value) @@ -177,6 +169,5 @@ export const InlineMortality: FC<{ const period = Number(2n << (encoded % (1n << 4n))) const factor = period >> 12 || 1 const phase = Number(encoded >> 4n) * factor - - return JSON.stringify({ period, phase }) + " " + JSON.stringify(mortality) + return { period, phase } } diff --git a/src/pages/Extrinsics/Analyzer/Priority.tsx b/src/components/Extrinsics/Priority.tsx similarity index 95% rename from src/pages/Extrinsics/Analyzer/Priority.tsx rename to src/components/Extrinsics/Priority.tsx index 333131f..156aa08 100644 --- a/src/pages/Extrinsics/Analyzer/Priority.tsx +++ b/src/components/Extrinsics/Priority.tsx @@ -1,5 +1,5 @@ import { TokenAmount } from "@/components/TokenAmount" -import { BlockContext } from "@/pages/Explorer/Detail/blockContext" +import { BlockContext } from "@/state/block.state" import { client$ } from "@/state/chains/chain.state" import { polkadot_people } from "@polkadot-api/descriptors" import { Binary, HexString } from "polkadot-api" @@ -127,18 +127,12 @@ const getPriority = async ( try { const [length, weights, queryInfo] = await Promise.all([ - typedApi.constants.System.BlockLength({ - at, - }), - typedApi.constants.System.BlockWeights({ - at, - }), + typedApi.constants.System.BlockLength({ at }), + typedApi.constants.System.BlockWeights({ at }), typedApi.apis.TransactionPaymentApi.query_info( Binary.fromOpaque(extrinsic), extLength, - { - at, - }, + { at }, ), ]) @@ -176,6 +170,7 @@ const getPriority = async ( return null } } + type PriorityDetails = Awaited> const usePriorityAnalysis = ({ @@ -192,8 +187,7 @@ const usePriorityAnalysis = ({ useEffect(() => { let cancelled = false getPriority(extrinsic, txPayment, blockHex).then((r) => { - if (cancelled) return - setResult(r) + if (!cancelled) setResult(r) }) return () => { cancelled = true diff --git a/src/components/Extrinsics/Sender.tsx b/src/components/Extrinsics/Sender.tsx new file mode 100644 index 0000000..f5dec74 --- /dev/null +++ b/src/components/Extrinsics/Sender.tsx @@ -0,0 +1,19 @@ +import { AccountIdDisplay } from "@/components/AccountIdDisplay" +import { Enum, HexString, SS58String } from "polkadot-api" + +const senderToAddress = ( + sender: Enum<{ Id: SS58String }> | SS58String | HexString, +) => + typeof sender === "string" + ? sender + : "type" in sender && sender.type === "Id" + ? sender.value + : null + +export const Sender: React.FC<{ + sender: Enum<{ Id: SS58String }> | SS58String | HexString +}> = ({ sender }) => { + const value = senderToAddress(sender) + if (!value) return null + return value ? : null +} diff --git a/src/pages/Explorer/Detail/SignedExtensions.tsx b/src/components/Extrinsics/SignedExtensions.tsx similarity index 90% rename from src/pages/Explorer/Detail/SignedExtensions.tsx rename to src/components/Extrinsics/SignedExtensions.tsx index 3bfe6b9..9349881 100644 --- a/src/pages/Explorer/Detail/SignedExtensions.tsx +++ b/src/components/Extrinsics/SignedExtensions.tsx @@ -76,7 +76,7 @@ const ExpandableLine: FC< return (
  • -
    +
    {id} {inlineContent ? ( -
    +
    - {inlineContent}
    ) : null} @@ -99,12 +99,12 @@ const InlineLine: FC<{ id: string; inlineContent?: ReactNode }> = ({ inlineContent, }) => (
  • -
    +
    {id}
    {inlineContent ? ( -
    +
    - {inlineContent}
    ) : null} diff --git a/src/components/Extrinsics/index.ts b/src/components/Extrinsics/index.ts new file mode 100644 index 0000000..8d2f4db --- /dev/null +++ b/src/components/Extrinsics/index.ts @@ -0,0 +1,4 @@ +export { MortalityAnalyzer, InlineMortality } from "./MortalityAnalyzer" +export { AnalyzePriority, PriorityValue } from "./Priority" +export { Sender } from "./Sender" +export { SignedExtensions, knownSignedExtensions } from "./SignedExtensions" diff --git a/src/pages/Explorer/BlockPopover.tsx b/src/pages/Explorer/BlockPopover.tsx index 259df22..bba15cf 100644 --- a/src/pages/Explorer/BlockPopover.tsx +++ b/src/pages/Explorer/BlockPopover.tsx @@ -2,11 +2,11 @@ import { CircularProgress } from "@/components/CircularProgress" import { CopyText } from "@/components/Copy" import { Link } from "@/hashParams" import { groupBy } from "@/lib/groupBy" +import { blockInfoState$ } from "@/state/block.state" import { runtimeCtx$ } from "@/state/chains/chain.state" import { state, useStateObservable } from "@react-rxjs/core" import { FC } from "react" import { map } from "rxjs" -import { blockInfoState$ } from "./block.state" import { BlockStatusIcon, statusText } from "./Detail/BlockState" import { filterEvt } from "./events.state" diff --git a/src/pages/Explorer/BlockTable.tsx b/src/pages/Explorer/BlockTable.tsx index d23b284..f3e5d40 100644 --- a/src/pages/Explorer/BlockTable.tsx +++ b/src/pages/Explorer/BlockTable.tsx @@ -1,17 +1,17 @@ import { CopyText } from "@/components/Copy" import { Popover } from "@/components/Popover" +import { Button } from "@/components/ui/button" +import { SearchInput } from "@/components/ui/search-input" import { Link, useNavigate } from "@/hashParams" +import { BlockInfo, blocksByHeight$, finalized$ } from "@/state/block.state" import { client$ } from "@/state/chains/chain.state" import { state, useStateObservable } from "@react-rxjs/core" +import { Search } from "lucide-react" import { FC } from "react" import { combineLatest, debounceTime, map, switchMap } from "rxjs" import { twMerge } from "tailwind-merge" -import { BlockInfo, blocksByHeight$, finalized$ } from "./block.state" import { BlockPopover } from "./BlockPopover" import * as Finalizing from "./FinalizingTable" -import { Button } from "@/components/ui/button" -import { SearchInput } from "@/components/ui/search-input" -import { Search } from "lucide-react" const best$ = client$.pipeState( switchMap((client) => client.bestBlocks$.pipe(map(([best]) => best))), @@ -174,7 +174,7 @@ export const BlockTable = () => {