@parity/product-sdk 0.0.0-dev.312.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/LICENSE +201 -0
  2. package/dist/address/index.d.ts +9 -0
  3. package/dist/address/index.js +3 -0
  4. package/dist/address/index.js.map +1 -0
  5. package/dist/chain/index.d.ts +1 -0
  6. package/dist/chain/index.js +3 -0
  7. package/dist/chain/index.js.map +1 -0
  8. package/dist/chunk-BRZNHTLQ.js +89 -0
  9. package/dist/chunk-BRZNHTLQ.js.map +1 -0
  10. package/dist/chunk-DHRCWMY5.js +240 -0
  11. package/dist/chunk-DHRCWMY5.js.map +1 -0
  12. package/dist/chunk-JVJ552RU.js +3 -0
  13. package/dist/chunk-JVJ552RU.js.map +1 -0
  14. package/dist/cloud-storage/index.d.ts +1 -0
  15. package/dist/cloud-storage/index.js +3 -0
  16. package/dist/cloud-storage/index.js.map +1 -0
  17. package/dist/contracts/index.d.ts +1 -0
  18. package/dist/contracts/index.js +3 -0
  19. package/dist/contracts/index.js.map +1 -0
  20. package/dist/core/index.d.ts +12 -0
  21. package/dist/core/index.js +5 -0
  22. package/dist/core/index.js.map +1 -0
  23. package/dist/crypto/index.d.ts +1 -0
  24. package/dist/crypto/index.js +3 -0
  25. package/dist/crypto/index.js.map +1 -0
  26. package/dist/dotns-bswWAmR2.d.ts +136 -0
  27. package/dist/host/index.d.ts +1 -0
  28. package/dist/host/index.js +3 -0
  29. package/dist/host/index.js.map +1 -0
  30. package/dist/identity/index.d.ts +491 -0
  31. package/dist/identity/index.js +691 -0
  32. package/dist/identity/index.js.map +1 -0
  33. package/dist/index.d.ts +62 -0
  34. package/dist/index.js +12 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/individuality/index.d.ts +1 -0
  37. package/dist/individuality/index.js +3 -0
  38. package/dist/individuality/index.js.map +1 -0
  39. package/dist/local-storage/index.d.ts +1 -0
  40. package/dist/local-storage/index.js +3 -0
  41. package/dist/local-storage/index.js.map +1 -0
  42. package/dist/react/index.d.ts +181 -0
  43. package/dist/react/index.js +198 -0
  44. package/dist/react/index.js.map +1 -0
  45. package/dist/testing/index.d.ts +61 -0
  46. package/dist/testing/index.js +149 -0
  47. package/dist/testing/index.js.map +1 -0
  48. package/dist/types-B5Zx4RNq.d.ts +211 -0
  49. package/dist/wallet/index.d.ts +1 -0
  50. package/dist/wallet/index.js +3 -0
  51. package/dist/wallet/index.js.map +1 -0
  52. package/package.json +118 -0
  53. package/src/address/index.ts +8 -0
  54. package/src/chain/index.ts +8 -0
  55. package/src/cloud-storage/index.ts +8 -0
  56. package/src/contracts/index.ts +8 -0
  57. package/src/core/createApp.ts +408 -0
  58. package/src/core/index.ts +12 -0
  59. package/src/core/logger.ts +15 -0
  60. package/src/core/types.ts +233 -0
  61. package/src/crypto/index.ts +8 -0
  62. package/src/host/index.ts +8 -0
  63. package/src/identity/dotns-abis.test.ts +43 -0
  64. package/src/identity/dotns-abis.ts +274 -0
  65. package/src/identity/dotns-errors.ts +34 -0
  66. package/src/identity/dotns-namehash.test.ts +155 -0
  67. package/src/identity/dotns-namehash.ts +149 -0
  68. package/src/identity/dotns-owner.test.ts +92 -0
  69. package/src/identity/dotns-registry.test.ts +1154 -0
  70. package/src/identity/dotns-registry.ts +870 -0
  71. package/src/identity/dotns.test.ts +128 -0
  72. package/src/identity/dotns.ts +221 -0
  73. package/src/identity/index.ts +61 -0
  74. package/src/identity/product-account.test.ts +130 -0
  75. package/src/identity/product-account.ts +115 -0
  76. package/src/identity/types.ts +87 -0
  77. package/src/index.ts +61 -0
  78. package/src/individuality/contract.test.ts +135 -0
  79. package/src/individuality/index.ts +8 -0
  80. package/src/local-storage/index.ts +8 -0
  81. package/src/react/context.ts +35 -0
  82. package/src/react/index.ts +43 -0
  83. package/src/react/provider.tsx +78 -0
  84. package/src/react/useChain.ts +35 -0
  85. package/src/react/useLocalStorage.ts +127 -0
  86. package/src/react/useWallet.ts +156 -0
  87. package/src/testing.ts +297 -0
  88. package/src/wallet/index.ts +11 -0
@@ -0,0 +1,87 @@
1
+ // Copyright 2026 Parity Technologies (UK) Ltd.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * Identity module types
5
+ *
6
+ * Types for DotNS name resolution and context-alias derivation
7
+ */
8
+
9
+ /** DotNS name resolution result */
10
+ export interface DotNsRecord {
11
+ /**
12
+ * H160 the name resolves to, `0x` and 20 bytes. Not SS58: DotNS is a set of
13
+ * Revive contracts and both address fields come back as EVM addresses.
14
+ * Convert with `h160ToSs58` from `@parity/product-sdk-address` if a
15
+ * Substrate-shaped address is needed.
16
+ *
17
+ * Absent when the name is registered but has no forward record yet, the
18
+ * state of every name just after registration. An unregistered name is
19
+ * `null` from `resolveDotNs`, not a record.
20
+ */
21
+ address?: `0x${string}`;
22
+ /**
23
+ * Name that was resolved, normalized: lowercased, and carrying the
24
+ * deployment's own TLD suffix — `.paseo` on Paseo Asset Hub Next V2, `.dot`
25
+ * on Previewnet. A bare label passed in comes back suffixed.
26
+ *
27
+ * Note for a **lite-person** registrant: the registry stores their label
28
+ * flattened, so `alice.42` is registered as `alice42` and this field reads
29
+ * `alice42.paseo`. Pass the flattened spelling when resolving; the dotted
30
+ * one derives a different node and finds nothing.
31
+ */
32
+ name: string;
33
+ /** H160 of the node's owner. Not SS58, same as {@link DotNsRecord.address}. */
34
+ owner: `0x${string}`;
35
+ /**
36
+ * Expiration timestamp, if the deployment has one.
37
+ *
38
+ * Always absent today: the Paseo Asset Hub registrar exposes no expiry
39
+ * getter, so nothing populates this.
40
+ */
41
+ expiresAt?: number;
42
+ }
43
+
44
+ /**
45
+ * Context alias info: a deterministic, context-bound alias derived from a parent account
46
+ *
47
+ * @deprecated Returned by `deriveContextAlias`, whose addresses no key can spend.
48
+ * Removal: `@parity/product-sdk` 0.23.0.
49
+ */
50
+ export interface ContextAliasInfo {
51
+ /** Alias SS58 address */
52
+ address: string;
53
+ /** H160 EVM address */
54
+ h160Address: `0x${string}`;
55
+ /** Parent account address */
56
+ parentAddress: string;
57
+ /** Context string used for derivation */
58
+ context: string;
59
+ }
60
+
61
+ /** Identity verification result */
62
+ export interface VerificationResult {
63
+ /** Whether identity is verified */
64
+ verified: boolean;
65
+ /** Verification method used */
66
+ method: "on-chain" | "judgement" | "social";
67
+ /** Verification details */
68
+ details?: Record<string, unknown>;
69
+ }
70
+
71
+ /** On-chain identity info */
72
+ export interface OnChainIdentity {
73
+ /** Display name */
74
+ display?: string;
75
+ /** Legal name */
76
+ legal?: string;
77
+ /** Web URL */
78
+ web?: string;
79
+ /** Email */
80
+ email?: string;
81
+ /** Twitter handle */
82
+ twitter?: string;
83
+ /** Riot/Matrix handle */
84
+ riot?: string;
85
+ /** Additional fields */
86
+ additional: Array<[string, string]>;
87
+ }
package/src/index.ts ADDED
@@ -0,0 +1,61 @@
1
+ // Copyright 2026 Parity Technologies (UK) Ltd.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * @parity/product-sdk
5
+ *
6
+ * Unified SDK for building products on the Polkadot ecosystem.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { createApp } from '@parity/product-sdk';
11
+ *
12
+ * const app = createApp({
13
+ * name: 'my-app',
14
+ * logLevel: 'info',
15
+ * });
16
+ *
17
+ * // Connect wallet
18
+ * const { accounts } = await app.wallet.connect();
19
+ *
20
+ * // Use storage
21
+ * await app.localStorage.set('key', 'value');
22
+ * ```
23
+ *
24
+ * @packageDocumentation
25
+ */
26
+
27
+ // Core exports
28
+ export { createApp } from "./core/createApp.js";
29
+ export { configure, createLogger } from "./core/logger.js";
30
+ export type {
31
+ App,
32
+ AppConfig,
33
+ LogLevel,
34
+ WalletApi,
35
+ LocalStorageApi,
36
+ ChainApi,
37
+ CloudStorageApi,
38
+ Account,
39
+ DotNsIdentitySignature,
40
+ SignMessageWithDotNsIdentityArgs,
41
+ ChainClient,
42
+ ChainDefinition,
43
+ TypedApi,
44
+ PolkadotClient,
45
+ } from "./core/types.js";
46
+ export type { LogEntry, LogHandler, LoggerConfig, Logger } from "./core/logger.js";
47
+
48
+ // The throw→Result model. Every fallible SDK operation returns `Result<T, E>`.
49
+ // `Result`/`ok`/`err`/`isErrorOf` come from the generic `@parity/result`; the
50
+ // cross-package `SdkError` marker (`isSdkError` recognizes any error raised by
51
+ // an `@parity/product-sdk-*` package) lives in `@parity/product-sdk-errors`.
52
+ export { ok, err, isErrorOf } from "@parity/result";
53
+ export type { Result } from "@parity/result";
54
+ export { type SdkError, isSdkError } from "@parity/product-sdk-errors";
55
+
56
+ // Re-export common utilities from leaf packages
57
+ export { isInsideContainer, isInsideContainerSync } from "@parity/product-sdk-host";
58
+ export { createChainClient } from "@parity/product-sdk-chain-client";
59
+ export { SignerManager } from "@parity/product-sdk-signer";
60
+ export { createLocalKvStore } from "@parity/product-sdk-local-storage";
61
+ export { CloudStorageClient, calculateCid } from "@parity/product-sdk-cloud-storage";
@@ -0,0 +1,135 @@
1
+ // Copyright 2026 Parity Technologies (UK) Ltd.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * Compile-time guard for the chain contract in `@parity/product-sdk-individuality`.
5
+ *
6
+ * That package types its chain parameter structurally, listing the storage
7
+ * entries it needs rather than naming a descriptor. That keeps it free of a
8
+ * chain-client dependency and lets its tests use a plain double, at the cost that
9
+ * the contract can drift from the real chain silently, since the double satisfies
10
+ * it by construction.
11
+ *
12
+ * This file closes that gap: if a descriptor regeneration changes a key or a
13
+ * value type, `pnpm typecheck` fails here. It guards both of that package's
14
+ * chain contracts, `IndividualityChain` for the personhood batch and
15
+ * `ConsumersChain` for the account to username read.
16
+ *
17
+ * **It checks the built `dist`, not the sibling's `src`.** The contracts resolve
18
+ * through `@parity/product-sdk-individuality`'s `types` field, so editing a
19
+ * contract and running this typecheck without rebuilding that package asserts
20
+ * against the previous build. Run `pnpm -r build` first, or the guard silently
21
+ * checks stale types. Verified 2026-08-19 by mutating a contract and watching
22
+ * the typecheck pass until the sibling was rebuilt.
23
+ *
24
+ * **It lives in the umbrella package deliberately.** Inside the individuality
25
+ * package the same assertion is vacuous, passing even against a contract that
26
+ * demands a pallet the chain does not have, because the descriptor types do not
27
+ * fully resolve through that package's dependency graph. Here, where both
28
+ * `chain-client` and the individuality package are direct dependencies, it
29
+ * correctly rejects a bogus contract. The negative control below keeps that
30
+ * property honest: if `IndividualityChain` ever stopped constraining, the
31
+ * `@ts-expect-error` would report as unused and this file would fail.
32
+ */
33
+ import type { getChainAPI } from "@parity/product-sdk-chain-client";
34
+ import type { RingVRFProof as HostRingVRFProof } from "@parity/product-sdk-host";
35
+ import type {
36
+ ConsumersChain,
37
+ IndividualityChain,
38
+ RingVRFProof as AsPersonRingVRFProof,
39
+ } from "@parity/product-sdk-individuality";
40
+ import { expect, test } from "vitest";
41
+
42
+ // The false branch must be `false`, not `never`. `never` is assignable to
43
+ // `true`, so a `never` branch would make the assertion unfalsifiable.
44
+ type Assert<T extends true> = T;
45
+
46
+ type PaseoClient = Awaited<ReturnType<typeof getChainAPI<"paseo">>>;
47
+ type DevnetClient = Awaited<ReturnType<typeof getChainAPI<"devnet">>>;
48
+
49
+ // These four aliases are the test. Each fails to typecheck if its condition
50
+ // breaks, so they need no export and no runtime reference.
51
+ type PaseoSatisfiesContract = Assert<PaseoClient extends IndividualityChain ? true : false>;
52
+ type DevnetSatisfiesContract = Assert<DevnetClient extends IndividualityChain ? true : false>;
53
+
54
+ // `ConsumersChain` needs this more than the batch contract does: its value shape
55
+ // was derived from the pallet and the codegen rather than read off the emitted
56
+ // descriptor, so these two lines are what make that derivation a checked fact.
57
+ type PaseoSatisfiesConsumers = Assert<PaseoClient extends ConsumersChain ? true : false>;
58
+ type DevnetSatisfiesConsumers = Assert<DevnetClient extends ConsumersChain ? true : false>;
59
+
60
+ // Negative control, kept type-only so it emits no runtime code. If
61
+ // `IndividualityChain` ever stopped constraining, this flips to `false` and the
62
+ // file fails to typecheck, which is what keeps the assertions above honest.
63
+ type ClientWithoutIndividuality = {
64
+ assetHub: unknown;
65
+ raw: { assetHub: unknown };
66
+ destroy(): void;
67
+ };
68
+ type RejectsBogusClient = Assert<
69
+ ClientWithoutIndividuality extends IndividualityChain ? false : true
70
+ >;
71
+ type RejectsBogusConsumersClient = Assert<
72
+ ClientWithoutIndividuality extends ConsumersChain ? false : true
73
+ >;
74
+
75
+ // The write half declares its own `RingVRFProof` rather than importing the
76
+ // host's, for the same reason `IndividualityChain` is structural: it keeps the
77
+ // individuality package free of a dependency on `@parity/product-sdk-host`. The
78
+ // cost is that a field rename on the host side would go unnoticed there, since
79
+ // nothing in that package resolves the host type. Here both are direct
80
+ // dependencies, so this is where the two can be tied together.
81
+ type HostProofSatisfiesLocal = Assert<HostRingVRFProof extends AsPersonRingVRFProof ? true : false>;
82
+
83
+ // Negative control for the same assertion. If `AsPersonRingVRFProof` ever
84
+ // stopped constraining, this flips to `false` and the file fails to typecheck.
85
+ type ProofWithoutRingIndex = {
86
+ proof: Uint8Array;
87
+ contextualAlias: { context: Uint8Array; alias: Uint8Array };
88
+ ringRevision: number;
89
+ };
90
+ type RejectsProofMissingRingIndex = Assert<
91
+ ProofWithoutRingIndex extends AsPersonRingVRFProof ? false : true
92
+ >;
93
+
94
+ // `AliasWithProof` is only ever accepted for `People.set_alias_account`, so the
95
+ // call has to exist on the real chain with the two arguments the extension's
96
+ // validation reads. A descriptor regeneration that renames either fails here.
97
+ type PeopleTx = PaseoClient["individuality"]["tx"]["People"];
98
+ type SetAliasAccountExists = Assert<"set_alias_account" extends keyof PeopleTx ? true : false>;
99
+ type SetAliasAccountArgs = Parameters<PeopleTx["set_alias_account"]>[0];
100
+ type SetAliasAccountTakesAccountAndValidAt = Assert<
101
+ "account" extends keyof SetAliasAccountArgs
102
+ ? "call_valid_at" extends keyof SetAliasAccountArgs
103
+ ? true
104
+ : false
105
+ : false
106
+ >;
107
+
108
+ // The JSDoc examples in the individuality package submit
109
+ // `Game.sign_up_with_alias` under a person origin. A person origin supplies only
110
+ // the origin, never the call arguments, so those snippets go stale the moment
111
+ // this call's parameters change — and nothing else in the repo compiles them.
112
+ type GameTx = PaseoClient["individuality"]["tx"]["Game"];
113
+ type SignUpWithAliasExists = Assert<"sign_up_with_alias" extends keyof GameTx ? true : false>;
114
+ type SignUpWithAliasArgs = Parameters<GameTx["sign_up_with_alias"]>[0];
115
+ type SignUpWithAliasTakesTheDocumentedArgs = Assert<
116
+ "identifier_key" extends keyof SignUpWithAliasArgs
117
+ ? "statement_account" extends keyof SignUpWithAliasArgs
118
+ ? "sig" extends keyof SignUpWithAliasArgs
119
+ ? true
120
+ : false
121
+ : false
122
+ : false
123
+ >;
124
+
125
+ // Negative control for the assertion above. The `extends keyof` chain would pass
126
+ // vacuously if `SignUpWithAliasArgs` ever resolved to something permissive such
127
+ // as `any`, so this pins a shape that is missing `sig` and requires it to fail.
128
+ type SignUpArgsWithoutSig = Pick<SignUpWithAliasArgs, "identifier_key" | "statement_account">;
129
+ type RejectsSignUpArgsMissingSig = Assert<"sig" extends keyof SignUpArgsWithoutSig ? false : true>;
130
+
131
+ test("the individuality chain contract is asserted at compile time", () => {
132
+ // The type assertions above are the test. This keeps vitest from reporting
133
+ // the file as an empty suite.
134
+ expect(true).toBe(true);
135
+ });
@@ -0,0 +1,8 @@
1
+ // Copyright 2026 Parity Technologies (UK) Ltd.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * @parity/product-sdk/individuality
5
+ *
6
+ * Re-exports from @parity/product-sdk-individuality.
7
+ */
8
+ export * from "@parity/product-sdk-individuality";
@@ -0,0 +1,8 @@
1
+ // Copyright 2026 Parity Technologies (UK) Ltd.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * @parity/product-sdk/local-storage
5
+ *
6
+ * Re-exports from @parity/product-sdk-local-storage.
7
+ */
8
+ export * from "@parity/product-sdk-local-storage";
@@ -0,0 +1,35 @@
1
+ // Copyright 2026 Parity Technologies (UK) Ltd.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * React context for Product SDK
5
+ */
6
+
7
+ import { createContext, useContext } from "react";
8
+ import type { App } from "../core/types.js";
9
+
10
+ /** Context for the Product SDK app instance */
11
+ export const ProductSDKContext = createContext<App | null>(null);
12
+
13
+ /**
14
+ * Hook to access the Product SDK app instance
15
+ *
16
+ * @throws If used outside of ProductSDKProvider
17
+ *
18
+ * @example
19
+ * ```tsx
20
+ * function MyComponent() {
21
+ * const app = useProductSDK();
22
+ * // Use app.wallet, app.localStorage, etc.
23
+ * }
24
+ * ```
25
+ */
26
+ export function useProductSDK(): App {
27
+ const app = useContext(ProductSDKContext);
28
+ if (!app) {
29
+ throw new Error(
30
+ "useProductSDK must be used within a ProductSDKProvider. " +
31
+ 'Wrap your app with <ProductSDKProvider name="your-app">.',
32
+ );
33
+ }
34
+ return app;
35
+ }
@@ -0,0 +1,43 @@
1
+ // Copyright 2026 Parity Technologies (UK) Ltd.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * @parity/product-sdk/react
5
+ *
6
+ * React bindings for the Product SDK.
7
+ * Provides hooks and components for wallet connection, storage, and chain interaction.
8
+ *
9
+ * @example
10
+ * ```tsx
11
+ * import { ProductSDKProvider, useWallet, useLocalStorage } from '@parity/product-sdk/react';
12
+ *
13
+ * function App() {
14
+ * return (
15
+ * <ProductSDKProvider name="my-app">
16
+ * <MyApp />
17
+ * </ProductSDKProvider>
18
+ * );
19
+ * }
20
+ *
21
+ * function MyApp() {
22
+ * const { isConnected, connect, accounts } = useWallet();
23
+ * const [theme, setTheme] = useLocalStorage('theme', 'light');
24
+ *
25
+ * // ...
26
+ * }
27
+ * ```
28
+ */
29
+
30
+ // Provider
31
+ export { ProductSDKProvider } from "./provider.js";
32
+ export type { ProductSDKProviderProps } from "./provider.js";
33
+
34
+ // Context
35
+ export { ProductSDKContext, useProductSDK } from "./context.js";
36
+
37
+ // Hooks
38
+ export { useWallet } from "./useWallet.js";
39
+ export type { UseWalletState, UseWalletActions, UseWalletReturn } from "./useWallet.js";
40
+
41
+ export { useLocalStorage, useLocalStorageString } from "./useLocalStorage.js";
42
+
43
+ export { useChain } from "./useChain.js";
@@ -0,0 +1,78 @@
1
+ // Copyright 2026 Parity Technologies (UK) Ltd.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * ProductSDKProvider component
5
+ */
6
+
7
+ import React, { useEffect, useState, type ReactNode } from "react";
8
+ import { ProductSDKContext } from "./context.js";
9
+ import { createApp } from "../core/createApp.js";
10
+ import type { App, LogLevel } from "../core/types.js";
11
+
12
+ /** Props for ProductSDKProvider */
13
+ export interface ProductSDKProviderProps {
14
+ /** Application name - used for storage namespacing and product account derivation */
15
+ name: string;
16
+ /** Log level (default: 'info') */
17
+ logLevel?: LogLevel;
18
+ /** Child components */
19
+ children: ReactNode;
20
+ /** Fallback to show while loading */
21
+ fallback?: ReactNode;
22
+ }
23
+
24
+ /**
25
+ * Provider component that initializes the Product SDK
26
+ *
27
+ * @example
28
+ * ```tsx
29
+ * import { ProductSDKProvider } from '@parity/product-sdk/react';
30
+ *
31
+ * function App() {
32
+ * return (
33
+ * <ProductSDKProvider name="my-app" fallback={<Loading />}>
34
+ * <MyApp />
35
+ * </ProductSDKProvider>
36
+ * );
37
+ * }
38
+ * ```
39
+ */
40
+ export function ProductSDKProvider({
41
+ name,
42
+ logLevel = "info",
43
+ children,
44
+ fallback = null,
45
+ }: ProductSDKProviderProps) {
46
+ const [app, setApp] = useState<App | null>(null);
47
+ const [error, setError] = useState<Error | null>(null);
48
+
49
+ useEffect(() => {
50
+ let mounted = true;
51
+
52
+ createApp({ name, logLevel })
53
+ .then((createdApp) => {
54
+ if (mounted) {
55
+ setApp(createdApp);
56
+ }
57
+ })
58
+ .catch((e) => {
59
+ if (mounted) {
60
+ setError(e instanceof Error ? e : new Error(String(e)));
61
+ }
62
+ });
63
+
64
+ return () => {
65
+ mounted = false;
66
+ };
67
+ }, [name, logLevel]);
68
+
69
+ if (error) {
70
+ throw error;
71
+ }
72
+
73
+ if (!app) {
74
+ return <>{fallback}</>;
75
+ }
76
+
77
+ return <ProductSDKContext.Provider value={app}>{children}</ProductSDKContext.Provider>;
78
+ }
@@ -0,0 +1,35 @@
1
+ // Copyright 2026 Parity Technologies (UK) Ltd.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * useChain hook
5
+ */
6
+
7
+ import { useMemo } from "react";
8
+ import { useProductSDK } from "./context.js";
9
+ import type { ChainDefinition, TypedApi } from "../core/types.js";
10
+
11
+ /**
12
+ * Hook to get a typed chain client
13
+ *
14
+ * @param chain - Chain descriptor (PAPI ChainDefinition)
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * import { paseo_asset_hub } from '@parity/product-sdk-descriptors/paseo-asset-hub';
19
+ * import { useChain } from '@parity/product-sdk/react';
20
+ *
21
+ * function AssetHubBalance() {
22
+ * const assetHub = useChain(paseo_asset_hub);
23
+ *
24
+ * // assetHub is typed for Asset Hub queries
25
+ * const balance = await assetHub.query.System.Account.getValue(address);
26
+ * }
27
+ * ```
28
+ */
29
+ export function useChain<T extends ChainDefinition>(chain: T): TypedApi<T> {
30
+ const app = useProductSDK();
31
+
32
+ return useMemo(() => {
33
+ return app.chain.getClient(chain);
34
+ }, [app, chain]);
35
+ }
@@ -0,0 +1,127 @@
1
+ // Copyright 2026 Parity Technologies (UK) Ltd.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * useLocalStorage hook
5
+ */
6
+
7
+ import { useState, useEffect, useCallback } from "react";
8
+ import { useProductSDK } from "./context.js";
9
+
10
+ /**
11
+ * Hook for key-value storage operations
12
+ *
13
+ * @param key - Storage key
14
+ * @param defaultValue - Default value if key doesn't exist
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * function ThemeToggle() {
19
+ * const [theme, setTheme, { loading }] = useLocalStorage('theme', 'light');
20
+ *
21
+ * if (loading) return <div>Loading...</div>;
22
+ *
23
+ * return (
24
+ * <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
25
+ * Current: {theme}
26
+ * </button>
27
+ * );
28
+ * }
29
+ * ```
30
+ */
31
+ export function useLocalStorage<T = string>(
32
+ key: string,
33
+ defaultValue?: T,
34
+ ): [T | null, (value: T) => Promise<void>, { loading: boolean; error: Error | null }] {
35
+ const app = useProductSDK();
36
+
37
+ const [value, setValue] = useState<T | null>(null);
38
+ const [loading, setLoading] = useState(true);
39
+ const [error, setError] = useState<Error | null>(null);
40
+
41
+ // Load initial value
42
+ useEffect(() => {
43
+ let mounted = true;
44
+
45
+ const loadValue = async () => {
46
+ try {
47
+ setLoading(true);
48
+ const stored = await app.localStorage.getJSON<T>(key);
49
+ if (mounted) {
50
+ setValue(stored ?? defaultValue ?? null);
51
+ setLoading(false);
52
+ }
53
+ } catch (e) {
54
+ if (mounted) {
55
+ setError(e instanceof Error ? e : new Error(String(e)));
56
+ setLoading(false);
57
+ }
58
+ }
59
+ };
60
+
61
+ loadValue();
62
+
63
+ return () => {
64
+ mounted = false;
65
+ };
66
+ }, [app, key, defaultValue]);
67
+
68
+ const setStoredValue = useCallback(
69
+ async (newValue: T) => {
70
+ try {
71
+ setError(null);
72
+ await app.localStorage.setJSON(key, newValue);
73
+ setValue(newValue);
74
+ } catch (e) {
75
+ setError(e instanceof Error ? e : new Error(String(e)));
76
+ throw e;
77
+ }
78
+ },
79
+ [app, key],
80
+ );
81
+
82
+ return [value, setStoredValue, { loading, error }];
83
+ }
84
+
85
+ /**
86
+ * Hook for string storage (simpler API)
87
+ *
88
+ * @param key - Storage key
89
+ * @param defaultValue - Default value
90
+ */
91
+ export function useLocalStorageString(
92
+ key: string,
93
+ defaultValue?: string,
94
+ ): [string | null, (value: string) => Promise<void>, { loading: boolean }] {
95
+ const app = useProductSDK();
96
+
97
+ const [value, setValue] = useState<string | null>(null);
98
+ const [loading, setLoading] = useState(true);
99
+
100
+ useEffect(() => {
101
+ let mounted = true;
102
+
103
+ const loadValue = async () => {
104
+ const stored = await app.localStorage.get(key);
105
+ if (mounted) {
106
+ setValue(stored ?? defaultValue ?? null);
107
+ setLoading(false);
108
+ }
109
+ };
110
+
111
+ loadValue();
112
+
113
+ return () => {
114
+ mounted = false;
115
+ };
116
+ }, [app, key, defaultValue]);
117
+
118
+ const setStoredValue = useCallback(
119
+ async (newValue: string) => {
120
+ await app.localStorage.set(key, newValue);
121
+ setValue(newValue);
122
+ },
123
+ [app, key],
124
+ );
125
+
126
+ return [value, setStoredValue, { loading }];
127
+ }