refactor: break down chain.state into smaller files
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 = () => {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
|
||||
57
src/state/chains/networks/index.ts
Normal file
57
src/state/chains/networks/index.ts
Normal 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]
|
||||
86
src/state/chains/smoldot.ts
Normal file
86
src/state/chains/smoldot.ts
Normal 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),
|
||||
)
|
||||
}
|
||||
20
src/state/chains/websocket.ts
Normal file
20
src/state/chains/websocket.ts
Normal 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))
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
Reference in New Issue
Block a user