exclude entries that didn't actually change.

This commit is contained in:
Victor Oliva
2025-05-17 12:33:33 +02:00
parent 03907fbb04
commit 01c5c4fcb7
4 changed files with 105 additions and 78 deletions
+1 -1
View File
@@ -9,7 +9,7 @@ import { useTheme } from "@/ThemeProvider"
export const JsonDisplay: FC<{
src: unknown
collapsed?: boolean
collapsed?: boolean | number
}> = ({ src, ...props }) => {
const theme = useTheme()
+3 -29
View File
@@ -1,4 +1,4 @@
import { chopsticksInstance$ } from "@/chopsticks/chopsticks"
import { Chopsticks } from "@/components/Icons"
import { Loading } from "@/components/Loading"
import { groupBy } from "@/lib/groupBy"
import { runtimeCtx$ } from "@/state/chains/chain.state"
@@ -6,21 +6,13 @@ import * as Tabs from "@radix-ui/react-tabs"
import { state, useStateObservable } from "@react-rxjs/core"
import { FC, useState } from "react"
import { useLocation, useParams } from "react-router-dom"
import {
combineLatest,
distinctUntilChanged,
filter,
map,
switchMap,
take,
} from "rxjs"
import { combineLatest, distinctUntilChanged, filter, map, take } from "rxjs"
import { twMerge } from "tailwind-merge"
import { BlockInfo, blockInfoState$ } from "../block.state"
import { BlockEvents } from "./BlockEvents"
import { BlockStorageDiff } from "./BlockStorageDiff"
import { ApplyExtrinsicEvent, Extrinsic } from "./Extrinsic"
import { createExtrinsicCodec, DecodedExtrinsic } from "./extrinsicDecoder"
import { Chopsticks } from "@/components/Icons"
const blockExtrinsics$ = state((hash: string) => {
const decoder$ = runtimeCtx$.pipe(
@@ -42,23 +34,6 @@ const blockExtrinsics$ = state((hash: string) => {
)
}, [])
const blockHasDiff$ = state(
(hash: string) =>
chopsticksInstance$.pipe(
switchMap((chain) => {
if (!chain) return [null]
return chain.getBlock(hash as any)
}),
switchMap((block) => {
if (!block) return [null]
return block.storageDiff()
}),
map((v) => Boolean(v && Object.keys(v).length > 0)),
),
false,
)
type Tab = "signed" | "unsigned" | "events" | "diff"
export const BlockBody: FC<{
block: BlockInfo
@@ -66,7 +41,6 @@ export const BlockBody: FC<{
const { hash } = useParams()
const [selectedTab, setSelectedTab] = useState<Tab | null>(null)
const extrinsics = useStateObservable(blockExtrinsics$(hash ?? ""))
const diff = useStateObservable(blockHasDiff$(hash ?? ""))
const location = useLocation()
const hashParams = new URLSearchParams(location.hash.slice(1))
@@ -150,7 +124,7 @@ export const BlockBody: FC<{
>
Events
</Tabs.Trigger>
{diff && (
{block.diff && (
<Tabs.Trigger
className={twMerge(
"bg-secondary text-secondary-foreground/80 px-4 py-2 hover:text-polkadot-500 border-t border-r rounded-tr border-polkadot-200",
+50 -48
View File
@@ -1,6 +1,14 @@
import { FC } from "react"
import { BlockInfo } from "../block.state"
import { Binary, Codec } from "polkadot-api"
import { bytesToString } from "@/components/BinaryInput"
import { CopyText } from "@/components/Copy"
import { JsonDisplay } from "@/components/JsonDisplay"
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion"
import { groupBy } from "@/lib/groupBy"
import { dynamicBuilder$, lookup$ } from "@/state/chains/chain.state"
import {
HexString,
Struct,
@@ -11,49 +19,21 @@ import {
u8,
Vector,
} from "@polkadot-api/substrate-bindings"
import { state, useStateObservable } from "@react-rxjs/core"
import { dynamicBuilder$, lookup$ } from "@/state/chains/chain.state"
import { chopsticksInstance$ } from "@/chopsticks/chopsticks"
import { switchMap, map, combineLatest } from "rxjs"
import { toHex } from "@polkadot-api/utils"
import { groupBy } from "@/lib/groupBy"
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion"
import { bytesToString } from "@/components/BinaryInput"
import { CopyText } from "@/components/Copy"
import { JsonDisplay } from "@/components/JsonDisplay"
const storageDiff$ = (hash: string) =>
chopsticksInstance$.pipe(
switchMap((chain) => {
if (!chain) return [null]
return chain.getBlock(hash as any)
}),
switchMap((block) => {
if (!block) return [null]
return block.storageDiff()
}),
map((v) =>
v && Object.keys(v).length > 0
? (v as Record<string, string | null>)
: null,
),
// map((v) => v ?? testDiff),
)
import { state, useStateObservable } from "@react-rxjs/core"
import { Binary, Codec } from "polkadot-api"
import { FC } from "react"
import { combineLatest, map } from "rxjs"
import { BlockInfo, blockInfo$ } from "../block.state"
const TWOX128_LEN = 32
const blockDiff$ = state(
(hash: string) =>
// TODO for current block
combineLatest([lookup$, dynamicBuilder$, storageDiff$(hash)]).pipe(
map(([lookup, dynamicBuilder, diff]) => {
if (!diff) return null
combineLatest([lookup$, dynamicBuilder$, blockInfo$(hash)]).pipe(
map(([lookup, dynamicBuilder, block]) => {
if (!block.diff) return null
const palletKeys = Object.fromEntries(
lookup.metadata.pallets.map((pallet) => [
@@ -70,22 +50,29 @@ const blockDiff$ = state(
]),
)
return Object.entries(diff)
.filter(([, newValue]) => newValue !== null)
return Object.entries(block.diff)
.map(
([key, newValue]): {
([key, [prevValue, newValue]]): {
key: HexString
decodedKey: [string, ...unknown[]]
newValue: HexString
prevValue: HexString | null
decodedPrevValue: unknown
newValue: HexString | null
decodedNewValue: unknown
} | null => {
try {
if (wellKnownKeys[key]) {
return {
key,
newValue: newValue!,
prevValue,
newValue,
decodedKey: [wellKnownKeys[key].name],
decodedNewValue: wellKnownKeys[key].codec.dec(newValue!),
decodedNewValue: newValue
? wellKnownKeys[key].codec.dec(newValue)
: null,
decodedPrevValue: prevValue
? wellKnownKeys[key].codec.dec(prevValue)
: null,
}
}
const pallet = palletKeys[key.slice(2, 2 + TWOX128_LEN)]
@@ -99,13 +86,19 @@ const blockDiff$ = state(
if (storageCodec) {
return {
key,
newValue: newValue!,
prevValue,
newValue,
decodedKey: [
pallet.name,
entry,
...storageCodec.keys.dec(key),
],
decodedNewValue: storageCodec.value.dec(newValue!),
decodedNewValue: newValue
? storageCodec.value.dec(newValue)
: null,
decodedPrevValue: prevValue
? storageCodec.value.dec(prevValue)
: null,
}
}
}
@@ -164,7 +157,16 @@ export const BlockStorageDiff: FC<{
)
.join(".")}
</div>
<JsonDisplay collapsed src={change.decodedNewValue} />
<div className="grid grid-cols-2 gap-2">
<div className="overflow-auto">
<div className="text-muted-foreground">Previous Value</div>
<JsonDisplay collapsed={1} src={change.decodedPrevValue} />
</div>
<div className="overflow-auto">
<div className="text-muted-foreground">New Value</div>
<JsonDisplay collapsed={1} src={change.decodedNewValue} />
</div>
</div>
</div>
))}
</AccordionContent>
+51
View File
@@ -1,3 +1,4 @@
import { chopsticksInstance$ } from "@/chopsticks/chopsticks"
import { chainClient$, client$ } from "@/state/chains/chain.state"
import { SystemEvent } from "@polkadot-api/observable-client"
import { state } from "@react-rxjs/core"
@@ -30,6 +31,7 @@ import {
takeWhile,
tap,
timer,
toArray,
withLatestFrom,
} from "rxjs"
@@ -57,6 +59,7 @@ export interface BlockInfo {
digests: unknown[]
} | null
status: BlockState
diff: Record<string, [string | null, string | null]> | null
}
export const [blockInfo$, recordedBlocks$] = partitionByKey(
client$.pipe(switchMap((client) => client.blocks$)),
@@ -98,6 +101,7 @@ export const [blockInfo$, recordedBlocks$] = partitionByKey(
}),
),
status: getBlockStatus$(client, hash, number),
diff: getBlockDiff$(parent, hash),
}),
NEVER,
),
@@ -159,6 +163,7 @@ const getUnpinnedBlockInfo$ = (hash: string): Observable<BlockInfo> => {
},
number: Number(header.number),
status: BlockState.Finalized,
diff: null,
}),
),
tap((v) => disconnectedBlocks$.next(v)),
@@ -265,3 +270,49 @@ const getBlockStatus$ = (
true,
),
)
const getBlockDiff$ = (
parent: string,
hash: string,
): Observable<Record<string, [string | null, string | null]> | null> =>
chopsticksInstance$.pipe(
take(1),
switchMap((chain) => (chain ? chain.getBlock(hash as any) : [null])),
switchMap((block) => (block ? block.storageDiff() : [null])),
map((v) =>
v && Object.keys(v).length > 0
? (v as Record<string, string | null>)
: null,
),
withLatestFrom(chainClient$),
switchMap(([diff, { chainHead }]) => {
if (!diff) return [null]
return chainHead
.storageQueries$(
parent,
Object.keys(diff).map((key) => ({
key,
type: "value",
})),
)
.pipe(
toArray(),
map(
(previousResults): Record<string, [string | null, string | null]> =>
Object.fromEntries(
previousResults
.map((result) => [
result.key,
[result.value ?? null, diff[result.key]],
])
.filter(([, [prevVal, newVal]]) => prevVal !== newVal),
),
),
catchError((ex) => {
console.error(ex)
return [null]
}),
)
}),
)