refactor: break down chain.state into smaller files

This commit is contained in:
Victor Oliva
2024-11-11 04:06:31 +07:00
parent 20d72bea27
commit 23210a6c2f
11 changed files with 210 additions and 184 deletions

View File

@@ -17,8 +17,8 @@ import {
accountDetail$,
accounts$,
getPublicKey,
} from "@/extension-accounts.state"
import { identity$, isVerified } from "@/identity.state"
} from "@/state/extension-accounts.state"
import { identity$, isVerified } from "@/state/identity.state"
import { cn } from "@/utils/cn"
import { EditAccountId, NOTIN } from "@polkadot-api/react-builder"
import { getSs58AddressInfo } from "@polkadot-api/substrate-bindings"

View File

@@ -1,6 +1,6 @@
import { PolkadotIdenticon } from "@/components/PolkadotIdenticon"
import { accountDetail$, getPublicKey } from "@/extension-accounts.state"
import { identity$, isVerified } from "@/identity.state"
import { accountDetail$, getPublicKey } from "@/state/extension-accounts.state"
import { identity$, isVerified } from "@/state/identity.state"
import { useStateObservable } from "@react-rxjs/core"
import { CheckCircle } from "lucide-react"
import { SS58String } from "polkadot-api"

View File

@@ -13,11 +13,11 @@ import {
accountsByExtension$,
extensionAccounts$,
selectedExtensions$,
} from "@/extension-accounts.state"
} from "@/state/extension-accounts.state"
import {
walletConnectAccounts$,
walletConnectStatus$,
} from "@/walletconnect.state"
} from "@/state/walletconnect.state"
import { state, useStateObservable } from "@react-rxjs/core"
import { createSignal } from "@react-rxjs/utils"
import { InjectedExtension } from "polkadot-api/pjs-signer"

View File

@@ -5,11 +5,11 @@ import {
availableExtensions$,
onToggleExtension,
selectedExtensions$,
} from "@/extension-accounts.state"
} from "@/state/extension-accounts.state"
import {
toggleWalletConnect,
walletConnectStatus$,
} from "@/walletconnect.state"
} from "@/state/walletconnect.state"
import { useStateObservable } from "@react-rxjs/core"
export const ExtensionProvider: React.FC = () => {

View File

@@ -1,13 +1,3 @@
import { Check, ChevronDown } from "lucide-react"
import { FC, useEffect, useRef, useState } from "react"
import {
Network,
networkCategories,
onChangeChain,
SelectedChain,
selectedChain$,
} from "@/state/chains/chain.state"
import {
Accordion,
AccordionContent,
@@ -33,8 +23,17 @@ import {
import { Label } from "@/components/ui/label"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
import { ScrollArea } from "@/components/ui/scroll-area"
import {
Network,
networkCategories,
onChangeChain,
SelectedChain,
selectedChain$,
} from "@/state/chains/chain.state"
import { useStateObservable } from "@react-rxjs/core"
import { useCommandState } from "cmdk"
import { Check, ChevronDown } from "lucide-react"
import { FC, useEffect, useRef, useState } from "react"
const isValidUri = (input: string): boolean => {
try {

View File

@@ -1,21 +1,11 @@
import { getDynamicBuilder, getLookupFn } from "@polkadot-api/metadata-builders"
import { getObservableClient } from "@polkadot-api/observable-client"
import { decAnyMetadata, HexString } from "@polkadot-api/substrate-bindings"
import {
createClient as createSubstrateClient,
JsonRpcProvider,
} from "@polkadot-api/substrate-client"
import { createClient as createSubstrateClient } from "@polkadot-api/substrate-client"
import { toHex } from "@polkadot-api/utils"
import { sinkSuspense, state, SUSPENSE } from "@react-rxjs/core"
import { createSignal } from "@react-rxjs/utils"
import { createClient } from "polkadot-api"
import { withLogsRecorder } from "polkadot-api/logs-provider"
import { withPolkadotSdkCompat } from "polkadot-api/polkadot-sdk-compat"
import { getSmProvider } from "polkadot-api/sm-provider"
import { Chain } from "polkadot-api/smoldot"
import { startFromWorker } from "polkadot-api/smoldot/from-worker"
import SmWorker from "polkadot-api/smoldot/worker?worker"
import { getWsProvider } from "polkadot-api/ws-provider/web"
import {
concat,
EMPTY,
@@ -23,155 +13,49 @@ import {
finalize,
map,
NEVER,
Observable,
of,
startWith,
switchMap,
tap,
} from "rxjs"
import ksmRawNetworks from "./networks/kusama.json"
import paseoRawNetworks from "./networks/paseo.json"
import polkadotRawNetworks from "./networks/polkadot.json"
import westendRawNetworks from "./networks/westend.json"
import { defaultNetwork, Network } from "./networks"
import {
createSmoldotSource,
getSmoldotProvider,
SmoldotSource,
} from "./smoldot"
import {
createWebsocketSource,
getWebsocketProvider,
WebsocketSource,
} from "./websocket"
const [dotChainSpec, ksmChainSpec, paseoChainSpec, westendChainSpec] = [
import("polkadot-api/chains/polkadot"),
import("polkadot-api/chains/ksmcc3"),
import("polkadot-api/chains/paseo"),
import("polkadot-api/chains/westend2"),
].map((x) => x.then((y) => y.chainSpec))
const smoldot = startFromWorker(new SmWorker(), {
logCallback: (level, target, message) => {
console.debug("smoldot[%s(%s)] %s", target, level, message)
},
})
const relayChains: Record<
string,
{ chainSpec: Promise<string>; chain: Promise<Chain> | null }
> = {
polkadot: { chainSpec: dotChainSpec, chain: null },
kusama: { chainSpec: ksmChainSpec, chain: null },
paseo: { chainSpec: paseoChainSpec, chain: null },
westend: { chainSpec: westendChainSpec, chain: null },
}
const getRelayChain = async (name: string) => {
if (!relayChains[name].chain) {
relayChains[name].chain = smoldot.addChain({
chainSpec: await relayChains[name].chainSpec,
})
}
return relayChains[name].chain
}
export type ChainSource = WebsocketSource | SmoldotSource
export type ChainSource = { id: string } & (
| {
type: "chainSpec"
value: {
chainSpec: string
relayChain?: string
}
}
| {
type: "websocket"
value: string
}
)
export type Network = {
id: string
display: string
endpoints: Record<string, string>
lightclient: boolean
relayChain?: string
}
export type NetworkCategory = {
name: string
networks: Network[]
}
export type SelectedChain = {
network: Network
endpoint: string
}
const [Polkadot, Kusama, Paseo, Westend] = (
[
polkadotRawNetworks,
ksmRawNetworks,
paseoRawNetworks,
westendRawNetworks,
] as const
).map((n): Network[] =>
n.map((x) => ({
endpoints: x.rpcs as any,
lightclient: x.hasChainSpecs,
id: x.id,
display: x.display,
relayChain: x.relayChainInfo?.id,
})),
)
const networks = {
Polkadot,
Kusama,
Paseo,
Westend,
Development: [
{
id: "localhost",
display: "Localhost",
lightclient: false,
endpoints: {
"Local (ws://127.0.0.1:9944)": "ws://127.0.0.1:9944",
},
} as Network,
],
}
export const networkCategories: NetworkCategory[] = Object.entries(
networks,
).map(([name, networks]) => ({ name, networks }))
export const getChainSource = ({
endpoint,
network: { id, relayChain },
}: SelectedChain) =>
endpoint === "light-client"
? createSmoldotSource(id, relayChain)
: createWebsocketSource(id, endpoint)
export const getProvider = (source: ChainSource) =>
source.type === "websocket"
? getWebsocketProvider(source)
: getSmoldotProvider(source)
export const [selectedChainChanged$, onChangeChain] =
createSignal<SelectedChain>()
export const selectedChain$ = state<SelectedChain>(selectedChainChanged$, {
network: Polkadot[0],
network: defaultNetwork,
endpoint: "light-client",
})
selectedChain$.subscribe()
const selectedSource$ = selectedChain$.pipe(
switchMap(
({ endpoint, network }): Observable<ChainSource> | Promise<ChainSource> => {
const { id } = network
if (endpoint !== "light-client")
return of({
id,
type: "websocket",
value: endpoint,
})
if (id in relayChains) {
return relayChains[id].chainSpec.then((chainSpec) => ({
id,
type: "chainSpec",
value: { chainSpec },
}))
}
return import(`./chainspecs/${id}.ts`).then(({ chainSpec }) => {
const parsed = JSON.parse(chainSpec)
return {
id,
type: "chainSpec",
value: {
chainSpec,
relayChain:
network.relayChain || parsed.relayChain || parsed.relay_chain,
},
}
})
},
),
)
const selectedSource$ = selectedChain$.pipe(switchMap(getChainSource))
export const chainClient$ = state(
selectedSource$.pipe(
@@ -260,24 +144,4 @@ export const dynamicBuilder$ = runtimeCtx$.pipeState(
map((ctx) => ctx.dynamicBuilder),
)
export function getProvider(source: ChainSource): JsonRpcProvider {
if (source.type === "websocket") {
return withPolkadotSdkCompat(getWsProvider(source.value))
}
const chain = source.value.relayChain
? getRelayChain(source.value.relayChain).then((chain) =>
smoldot.addChain({
chainSpec: source.value.chainSpec,
potentialRelayChains: [chain],
}),
)
: smoldot.addChain({
chainSpec: source.value.chainSpec,
})
return withLogsRecorder(
(v) => v.includes("initialized") && console.debug(v),
getSmProvider(chain),
)
}
export { networkCategories, type Network } from "./networks"

View File

@@ -0,0 +1,57 @@
import ksmRawNetworks from "./kusama.json"
import paseoRawNetworks from "./paseo.json"
import polkadotRawNetworks from "./polkadot.json"
import westendRawNetworks from "./westend.json"
export type Network = {
id: string
display: string
endpoints: Record<string, string>
lightclient: boolean
relayChain?: string
}
export type NetworkCategory = {
name: string
networks: Network[]
}
const [Polkadot, Kusama, Paseo, Westend] = (
[
polkadotRawNetworks,
ksmRawNetworks,
paseoRawNetworks,
westendRawNetworks,
] as const
).map((n): Network[] =>
n.map((x) => ({
endpoints: x.rpcs as any,
lightclient: x.hasChainSpecs,
id: x.id,
display: x.display,
relayChain: x.relayChainInfo?.id,
})),
)
const networks = {
Polkadot,
Kusama,
Paseo,
Westend,
Development: [
{
id: "localhost",
display: "Localhost",
lightclient: false,
endpoints: {
"Local (ws://127.0.0.1:9944)": "ws://127.0.0.1:9944",
},
} as Network,
],
}
export const networkCategories: NetworkCategory[] = Object.entries(
networks,
).map(([name, networks]) => ({ name, networks }))
export const defaultNetwork = Polkadot[0]

View File

@@ -0,0 +1,86 @@
import { JsonRpcProvider } from "@polkadot-api/substrate-client"
import { withLogsRecorder } from "polkadot-api/logs-provider"
import { getSmProvider } from "polkadot-api/sm-provider"
import { Chain } from "polkadot-api/smoldot"
import { startFromWorker } from "polkadot-api/smoldot/from-worker"
import SmWorker from "polkadot-api/smoldot/worker?worker"
const [dotChainSpec, ksmChainSpec, paseoChainSpec, westendChainSpec] = [
import("polkadot-api/chains/polkadot"),
import("polkadot-api/chains/ksmcc3"),
import("polkadot-api/chains/paseo"),
import("polkadot-api/chains/westend2"),
].map((x) => x.then((y) => y.chainSpec))
const smoldot = startFromWorker(new SmWorker(), {
logCallback: (level, target, message) => {
console.debug("smoldot[%s(%s)] %s", target, level, message)
},
})
const relayChains: Record<
string,
{ chainSpec: Promise<string>; chain: Promise<Chain> | null }
> = {
polkadot: { chainSpec: dotChainSpec, chain: null },
kusama: { chainSpec: ksmChainSpec, chain: null },
paseo: { chainSpec: paseoChainSpec, chain: null },
westend: { chainSpec: westendChainSpec, chain: null },
}
const getRelayChain = async (name: string) => {
if (!relayChains[name].chain) {
relayChains[name].chain = smoldot.addChain({
chainSpec: await relayChains[name].chainSpec,
})
}
return relayChains[name].chain
}
export interface SmoldotSource {
type: "chainSpec"
id: string
value: {
chainSpec: string
relayChain?: string
}
}
export function createSmoldotSource(
id: string,
relayChain?: string,
): Promise<SmoldotSource> {
if (id in relayChains) {
return relayChains[id].chainSpec.then((chainSpec) => ({
id,
type: "chainSpec",
value: { chainSpec },
}))
}
return import(`./chainspecs/${id}.ts`).then(({ chainSpec }) => {
const parsed = JSON.parse(chainSpec)
return {
id,
type: "chainSpec",
value: {
chainSpec,
relayChain: relayChain || parsed.relayChain || parsed.relay_chain,
},
}
})
}
export function getSmoldotProvider(source: SmoldotSource): JsonRpcProvider {
const chain = source.value.relayChain
? getRelayChain(source.value.relayChain).then((chain) =>
smoldot.addChain({
chainSpec: source.value.chainSpec,
potentialRelayChains: [chain],
}),
)
: smoldot.addChain({
chainSpec: source.value.chainSpec,
})
return withLogsRecorder(
(v) => v.includes("initialized") && console.debug(v),
getSmProvider(chain),
)
}

View File

@@ -0,0 +1,20 @@
import { JsonRpcProvider } from "@polkadot-api/substrate-client"
import { withPolkadotSdkCompat } from "polkadot-api/polkadot-sdk-compat"
import { getWsProvider } from "polkadot-api/ws-provider/web"
export interface WebsocketSource {
type: "websocket"
id: string
endpoint: string
}
export async function createWebsocketSource(
id: string,
endpoint: string,
): Promise<WebsocketSource> {
return { type: "websocket", id, endpoint }
}
export function getWebsocketProvider(source: WebsocketSource): JsonRpcProvider {
return withPolkadotSdkCompat(getWsProvider(source.endpoint))
}

View File

@@ -1,3 +1,4 @@
import { localStorageSubject } from "@/utils/localStorageSubject"
import {
IdentityData,
IdentityJudgement,
@@ -5,10 +6,9 @@ import {
} from "@polkadot-api/descriptors"
import { state } from "@react-rxjs/core"
import { Binary, createClient, SS58String } from "polkadot-api"
import { chainSpec } from "./chains/chainspecs/polkadot_people"
import { catchError, map, of, tap } from "rxjs"
import { getProvider } from "./chains/chain.state"
import { localStorageSubject } from "@/utils/localStorageSubject"
import { chainSpec } from "./chains/chainspecs/polkadot_people"
export interface Identity {
displayName: string

View File

@@ -1,3 +1,4 @@
import { localStorageSubject } from "@/utils/localStorageSubject"
import { state, withDefault } from "@react-rxjs/core"
import { createSignal } from "@react-rxjs/utils"
import { WalletConnectModal } from "@walletconnect/modal"
@@ -28,7 +29,6 @@ import {
takeUntil,
tap,
} from "rxjs"
import { localStorageSubject } from "./utils/localStorageSubject"
// https://docs.reown.com/advanced/multichain/polkadot/dapp-integration-guide
const chains = [