diff --git a/src/pages/Explorer/Detail/BlockBody.tsx b/src/pages/Explorer/Detail/BlockBody.tsx index f462368..b0a38bd 100644 --- a/src/pages/Explorer/Detail/BlockBody.tsx +++ b/src/pages/Explorer/Detail/BlockBody.tsx @@ -1,7 +1,7 @@ import { Chopsticks } from "@/components/Icons" import { Loading } from "@/components/Loading" import { groupBy } from "@/lib/groupBy" -import { runtimeCtx$ } from "@/state/chains/chain.state" +import { runtimeCtxAt$ } from "@/state/chains/chain.state" import * as Tabs from "@radix-ui/react-tabs" import { state, useStateObservable } from "@react-rxjs/core" import { FC, useState } from "react" @@ -24,7 +24,7 @@ const blockExtrinsics$ = state((hash: string) => { distinctUntilChanged(), ) - return combineLatest([body$, runtimeCtx$]).pipe( + return combineLatest([body$, runtimeCtxAt$(hash)]).pipe( take(1), map(([body, { txDecoder }]) => body.map((raw, idx) => ({ diff --git a/src/state/chains/chain.state.ts b/src/state/chains/chain.state.ts index df0142b..bbbf1cc 100644 --- a/src/state/chains/chain.state.ts +++ b/src/state/chains/chain.state.ts @@ -25,10 +25,13 @@ import { firstValueFrom, from, map, + mergeMap, NEVER, + Observable, of, startWith, switchMap, + take, } from "rxjs" import { addCustomNetwork, @@ -148,13 +151,16 @@ const selectedSource$ = selectedChain$.pipe(switchMap(getChainSource)) // remove old localStorage clear after a while localStorage.removeItem("metadata-cache") -type MetadataCache = Map +type MetadataCache = Map< + string, + { id: string; time: number; data: HexString; codeHash: HexString } +> const IDB_KEY = "metadata-cache" const MAX_CACHE_ENTRIES = 3 const addEntryToCache = ( codeHash: string, - entry: { id: string; time: number; data: HexString }, + entry: { id: string; time: number; data: HexString; codeHash: HexString }, ) => update(IDB_KEY, (cached) => { cached ??= new Map() @@ -191,7 +197,12 @@ const getMetadata = (codeHash: string | null) => const setMetadataFactory = (id: string) => (codeHash: string | null, data: Uint8Array) => { if (codeHash) - addEntryToCache(codeHash, { id, time: Date.now(), data: toHex(data) }) + addEntryToCache(codeHash, { + id, + time: Date.now(), + data: toHex(data), + codeHash, + }) } export const chainClient$ = state( @@ -249,6 +260,28 @@ const uncachedRuntimeCtx$ = chainClient$.pipeState( filter(Boolean), ) +const withTxDecoder: ( + input: Observable, +) => Observable }> = + map((ctx) => ({ + ...ctx, + txDecoder: getExtrinsicDecoder(ctx.metadataRaw), + })) + +export const runtimeCtxAt$ = state((atBlock: string) => + chainClient$.pipe( + take(1), + mergeMap((client) => { + const pinned = client.chainHead.pinnedBlocks$.state + return ( + pinned.runtimes[pinned.blocks.get(atBlock)?.runtime!]?.runtime || + client.chainHead.getRuntimeContext$(atBlock) + ) + }), + withTxDecoder, + ), +) + export const runtimeCtx$ = chainClient$.pipeState( switchMap(({ id }) => get(IDB_KEY).then((cache) => @@ -256,25 +289,20 @@ export const runtimeCtx$ = chainClient$.pipeState( ), ), switchMap((cached) => { - if (cached) { - const metadata = unifyMetadata(decAnyMetadata(cached[1].data)) - const lookup = getLookupFn(metadata) - const dynamicBuilder = getDynamicBuilder(lookup) + if (!cached) return uncachedRuntimeCtx$ + const metadata = unifyMetadata(decAnyMetadata(cached[1].data)) + const lookup = getLookupFn(metadata) + const dynamicBuilder = getDynamicBuilder(lookup) - return uncachedRuntimeCtx$.pipe( - startWith({ - metadataRaw: fromHex(cached[1].data), - lookup, - dynamicBuilder, - }), - ) - } - return uncachedRuntimeCtx$ + return uncachedRuntimeCtx$.pipe( + startWith({ + metadataRaw: fromHex(cached[1].data), + lookup, + dynamicBuilder, + }), + ) }), - map((ctx) => ({ - ...ctx, - txDecoder: getExtrinsicDecoder(ctx.metadataRaw), - })), + withTxDecoder, ) export const lookup$ = runtimeCtx$.pipeState(map((ctx) => ctx.lookup))