@parity/product-sdk 0.1.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 (66) hide show
  1. package/LICENSE +201 -0
  2. package/dist/address/index.d.ts +8 -0
  3. package/dist/address/index.js +3 -0
  4. package/dist/address/index.js.map +1 -0
  5. package/dist/bulletin/index.d.ts +1 -0
  6. package/dist/bulletin/index.js +3 -0
  7. package/dist/bulletin/index.js.map +1 -0
  8. package/dist/chain/index.d.ts +1 -0
  9. package/dist/chain/index.js +3 -0
  10. package/dist/chain/index.js.map +1 -0
  11. package/dist/chunk-NVGSGXGH.js +177 -0
  12. package/dist/chunk-NVGSGXGH.js.map +1 -0
  13. package/dist/chunk-XSKBA5SR.js +8 -0
  14. package/dist/chunk-XSKBA5SR.js.map +1 -0
  15. package/dist/contracts/index.d.ts +1 -0
  16. package/dist/contracts/index.js +3 -0
  17. package/dist/contracts/index.js.map +1 -0
  18. package/dist/core/index.d.ts +9 -0
  19. package/dist/core/index.js +13 -0
  20. package/dist/core/index.js.map +1 -0
  21. package/dist/crypto/index.d.ts +1 -0
  22. package/dist/crypto/index.js +3 -0
  23. package/dist/crypto/index.js.map +1 -0
  24. package/dist/host/index.d.ts +1 -0
  25. package/dist/host/index.js +3 -0
  26. package/dist/host/index.js.map +1 -0
  27. package/dist/identity/index.d.ts +186 -0
  28. package/dist/identity/index.js +109 -0
  29. package/dist/identity/index.js.map +1 -0
  30. package/dist/index.d.ts +59 -0
  31. package/dist/index.js +27 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/react/index.d.ts +177 -0
  34. package/dist/react/index.js +213 -0
  35. package/dist/react/index.js.map +1 -0
  36. package/dist/storage/index.d.ts +1 -0
  37. package/dist/storage/index.js +3 -0
  38. package/dist/storage/index.js.map +1 -0
  39. package/dist/types-CZQDzQ53.d.ts +142 -0
  40. package/dist/wallet/index.d.ts +1 -0
  41. package/dist/wallet/index.js +3 -0
  42. package/dist/wallet/index.js.map +1 -0
  43. package/package.json +105 -0
  44. package/src/address/index.ts +6 -0
  45. package/src/bulletin/index.ts +6 -0
  46. package/src/chain/index.ts +6 -0
  47. package/src/contracts/index.ts +6 -0
  48. package/src/core/createApp.ts +284 -0
  49. package/src/core/index.ts +10 -0
  50. package/src/core/logger.ts +13 -0
  51. package/src/core/types.ts +155 -0
  52. package/src/crypto/index.ts +6 -0
  53. package/src/host/index.ts +6 -0
  54. package/src/identity/dotns.ts +96 -0
  55. package/src/identity/index.ts +34 -0
  56. package/src/identity/product-account.ts +158 -0
  57. package/src/identity/types.ts +75 -0
  58. package/src/index.ts +49 -0
  59. package/src/react/context.ts +33 -0
  60. package/src/react/index.ts +41 -0
  61. package/src/react/provider.tsx +76 -0
  62. package/src/react/useChain.ts +33 -0
  63. package/src/react/useStorage.ts +125 -0
  64. package/src/react/useWallet.ts +138 -0
  65. package/src/storage/index.ts +6 -0
  66. package/src/wallet/index.ts +9 -0
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Product account derivation
3
+ *
4
+ * Derives product-scoped accounts from a parent account
5
+ */
6
+
7
+ import { createLogger } from "@parity/product-sdk-logger";
8
+ import { blake2b256 } from "@parity/product-sdk-crypto";
9
+ import { ss58Encode, ss58Decode, deriveH160 } from "@parity/product-sdk-address";
10
+ import type { ProductAccountInfo, AnonymousAliasInfo, RingLocation } from "./types.js";
11
+
12
+ const log = createLogger("identity");
13
+
14
+ /**
15
+ * Derive a product-scoped account from a parent account
16
+ *
17
+ * The product account is deterministically derived using:
18
+ * productPublicKey = hash(parentPublicKey || productName)
19
+ *
20
+ * @param parentAddress - Parent account SS58 address
21
+ * @param productName - Product name for derivation
22
+ * @param ss58Prefix - SS58 prefix (default: 42)
23
+ * @returns Product account info
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * const productAccount = deriveProductAccount(
28
+ * '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY',
29
+ * 'my-app'
30
+ * );
31
+ * console.log('Product address:', productAccount.address);
32
+ * ```
33
+ */
34
+ export function deriveProductAccount(
35
+ parentAddress: string,
36
+ productName: string,
37
+ ss58Prefix = 42,
38
+ ): ProductAccountInfo {
39
+ const { publicKey: parentPublicKey } = ss58Decode(parentAddress);
40
+
41
+ // Derive product public key: blake2b-256(parentPublicKey || productName)
42
+ const productNameBytes = new TextEncoder().encode(productName);
43
+ const combined = new Uint8Array(parentPublicKey.length + productNameBytes.length);
44
+ combined.set(parentPublicKey, 0);
45
+ combined.set(productNameBytes, parentPublicKey.length);
46
+
47
+ const productPublicKey = blake2b256(combined);
48
+ const address = ss58Encode(productPublicKey, ss58Prefix);
49
+ const h160Address = deriveH160(productPublicKey);
50
+
51
+ log.debug("Derived product account", {
52
+ parentAddress,
53
+ productName,
54
+ address,
55
+ });
56
+
57
+ return {
58
+ address,
59
+ h160Address,
60
+ parentAddress,
61
+ productName,
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Verify that a product account was derived from a parent account
67
+ *
68
+ * @param productAddress - Product account address
69
+ * @param parentAddress - Claimed parent address
70
+ * @param productName - Product name
71
+ * @returns True if derivation is valid
72
+ */
73
+ export function verifyProductAccount(
74
+ productAddress: string,
75
+ parentAddress: string,
76
+ productName: string,
77
+ ): boolean {
78
+ try {
79
+ const derived = deriveProductAccount(parentAddress, productName);
80
+ const { publicKey: productKey } = ss58Decode(productAddress);
81
+ const { publicKey: derivedKey } = ss58Decode(derived.address);
82
+
83
+ // Compare public keys
84
+ if (productKey.length !== derivedKey.length) return false;
85
+ for (let i = 0; i < productKey.length; i++) {
86
+ if (productKey[i] !== derivedKey[i]) return false;
87
+ }
88
+ return true;
89
+ } catch {
90
+ return false;
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Derive an anonymous alias using Ring VRF
96
+ *
97
+ * This creates a context-specific alias that cannot be linked
98
+ * back to the original identity without the ring proof.
99
+ *
100
+ * @param context - Context for alias derivation (e.g., "voting-round-1")
101
+ * @param ringLocation - Ring location for proof generation
102
+ * @returns Anonymous alias info
103
+ */
104
+ export function deriveAnonymousAlias(
105
+ context: string,
106
+ ringLocation: RingLocation,
107
+ ): AnonymousAliasInfo {
108
+ log.debug("Deriving anonymous alias", { context, ringLocation });
109
+
110
+ // TODO: Implement Ring VRF alias derivation
111
+ // This requires the Ring VRF implementation from TruAPI
112
+ throw new Error(
113
+ "deriveAnonymousAlias() is not yet implemented. " +
114
+ "This requires container mode with Ring VRF support.",
115
+ );
116
+ }
117
+
118
+ /**
119
+ * Create a Ring VRF proof for a message
120
+ *
121
+ * @param message - Message to prove
122
+ * @param ringLocation - Ring location
123
+ * @returns Proof bytes
124
+ */
125
+ export async function createRingProof(
126
+ message: Uint8Array,
127
+ ringLocation: RingLocation,
128
+ ): Promise<Uint8Array> {
129
+ log.debug("Creating ring proof", { ringLocation });
130
+
131
+ // TODO: Implement Ring VRF proof creation via TruAPI
132
+ throw new Error(
133
+ "createRingProof() is not yet implemented. " +
134
+ "This requires container mode with Ring VRF support.",
135
+ );
136
+ }
137
+
138
+ /**
139
+ * Verify a Ring VRF proof
140
+ *
141
+ * @param message - Original message
142
+ * @param proof - Proof bytes
143
+ * @param alias - Expected alias
144
+ * @returns True if proof is valid
145
+ */
146
+ export async function verifyRingProof(
147
+ message: Uint8Array,
148
+ proof: Uint8Array,
149
+ alias: string,
150
+ ): Promise<boolean> {
151
+ log.debug("Verifying ring proof");
152
+
153
+ // TODO: Implement Ring VRF proof verification
154
+ throw new Error(
155
+ "verifyRingProof() is not yet implemented. " +
156
+ "This requires container mode with Ring VRF support.",
157
+ );
158
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Identity module types
3
+ *
4
+ * Types for DotNS name resolution and product account derivation
5
+ */
6
+
7
+ /** DotNS name resolution result */
8
+ export interface DotNsRecord {
9
+ /** Resolved SS58 address */
10
+ address: string;
11
+ /** Name that was resolved */
12
+ name: string;
13
+ /** Owner address */
14
+ owner: string;
15
+ /** Expiration timestamp (if applicable) */
16
+ expiresAt?: number;
17
+ }
18
+
19
+ /** Product account info */
20
+ export interface ProductAccountInfo {
21
+ /** Product-scoped SS58 address */
22
+ address: string;
23
+ /** H160 EVM address */
24
+ h160Address: `0x${string}`;
25
+ /** Parent account address */
26
+ parentAddress: string;
27
+ /** Product name used for derivation */
28
+ productName: string;
29
+ }
30
+
31
+ /** Ring VRF alias info */
32
+ export interface AnonymousAliasInfo {
33
+ /** Anonymous alias identifier */
34
+ alias: string;
35
+ /** Ring location for proof generation */
36
+ ringLocation: RingLocation;
37
+ /** Context used for alias derivation */
38
+ context: string;
39
+ }
40
+
41
+ /** Ring location for VRF proofs */
42
+ export interface RingLocation {
43
+ /** Ring index */
44
+ ringIndex: number;
45
+ /** Member index within ring */
46
+ memberIndex: number;
47
+ }
48
+
49
+ /** Identity verification result */
50
+ export interface VerificationResult {
51
+ /** Whether identity is verified */
52
+ verified: boolean;
53
+ /** Verification method used */
54
+ method: "on-chain" | "judgement" | "social";
55
+ /** Verification details */
56
+ details?: Record<string, unknown>;
57
+ }
58
+
59
+ /** On-chain identity info */
60
+ export interface OnChainIdentity {
61
+ /** Display name */
62
+ display?: string;
63
+ /** Legal name */
64
+ legal?: string;
65
+ /** Web URL */
66
+ web?: string;
67
+ /** Email */
68
+ email?: string;
69
+ /** Twitter handle */
70
+ twitter?: string;
71
+ /** Riot/Matrix handle */
72
+ riot?: string;
73
+ /** Additional fields */
74
+ additional: Array<[string, string]>;
75
+ }
package/src/index.ts ADDED
@@ -0,0 +1,49 @@
1
+ /**
2
+ * @parity/product-sdk
3
+ *
4
+ * Unified SDK for building products on the Polkadot ecosystem.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * import { createApp } from '@parity/product-sdk';
9
+ *
10
+ * const app = createApp({
11
+ * name: 'my-app',
12
+ * logLevel: 'info',
13
+ * });
14
+ *
15
+ * // Connect wallet
16
+ * const { accounts } = await app.wallet.connect();
17
+ *
18
+ * // Use storage
19
+ * await app.storage.set('key', 'value');
20
+ * ```
21
+ *
22
+ * @packageDocumentation
23
+ */
24
+
25
+ // Core exports
26
+ export { createApp } from "./core/createApp.js";
27
+ export { configure, createLogger } from "./core/logger.js";
28
+ export type {
29
+ App,
30
+ AppConfig,
31
+ LogLevel,
32
+ WalletApi,
33
+ StorageApi,
34
+ ChainApi,
35
+ BulletinApi,
36
+ Account,
37
+ ChainClient,
38
+ ChainDefinition,
39
+ TypedApi,
40
+ PolkadotClient,
41
+ } from "./core/types.js";
42
+ export type { LogEntry, LogHandler, LoggerConfig, Logger } from "./core/logger.js";
43
+
44
+ // Re-export common utilities from leaf packages
45
+ export { isInsideContainer, isInsideContainerSync } from "@parity/product-sdk-host";
46
+ export { createChainClient } from "@parity/product-sdk-chain-client";
47
+ export { SignerManager } from "@parity/product-sdk-signer";
48
+ export { createKvStore } from "@parity/product-sdk-storage";
49
+ export { BulletinClient, computeCid } from "@parity/product-sdk-bulletin";
@@ -0,0 +1,33 @@
1
+ /**
2
+ * React context for Product SDK
3
+ */
4
+
5
+ import { createContext, useContext } from "react";
6
+ import type { App } from "../core/types.js";
7
+
8
+ /** Context for the Product SDK app instance */
9
+ export const ProductSDKContext = createContext<App | null>(null);
10
+
11
+ /**
12
+ * Hook to access the Product SDK app instance
13
+ *
14
+ * @throws If used outside of ProductSDKProvider
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * function MyComponent() {
19
+ * const app = useProductSDK();
20
+ * // Use app.wallet, app.storage, etc.
21
+ * }
22
+ * ```
23
+ */
24
+ export function useProductSDK(): App {
25
+ const app = useContext(ProductSDKContext);
26
+ if (!app) {
27
+ throw new Error(
28
+ "useProductSDK must be used within a ProductSDKProvider. " +
29
+ 'Wrap your app with <ProductSDKProvider name="your-app">.',
30
+ );
31
+ }
32
+ return app;
33
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * @parity/product-sdk/react
3
+ *
4
+ * React bindings for the Product SDK.
5
+ * Provides hooks and components for wallet connection, storage, and chain interaction.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * import { ProductSDKProvider, useWallet, useStorage } from '@parity/product-sdk/react';
10
+ *
11
+ * function App() {
12
+ * return (
13
+ * <ProductSDKProvider name="my-app">
14
+ * <MyApp />
15
+ * </ProductSDKProvider>
16
+ * );
17
+ * }
18
+ *
19
+ * function MyApp() {
20
+ * const { isConnected, connect, accounts } = useWallet();
21
+ * const [theme, setTheme] = useStorage('theme', 'light');
22
+ *
23
+ * // ...
24
+ * }
25
+ * ```
26
+ */
27
+
28
+ // Provider
29
+ export { ProductSDKProvider } from "./provider.js";
30
+ export type { ProductSDKProviderProps } from "./provider.js";
31
+
32
+ // Context
33
+ export { ProductSDKContext, useProductSDK } from "./context.js";
34
+
35
+ // Hooks
36
+ export { useWallet } from "./useWallet.js";
37
+ export type { UseWalletState, UseWalletActions, UseWalletReturn } from "./useWallet.js";
38
+
39
+ export { useStorage, useStorageString } from "./useStorage.js";
40
+
41
+ export { useChain } from "./useChain.js";
@@ -0,0 +1,76 @@
1
+ /**
2
+ * ProductSDKProvider component
3
+ */
4
+
5
+ import React, { useEffect, useState, type ReactNode } from "react";
6
+ import { ProductSDKContext } from "./context.js";
7
+ import { createApp } from "../core/createApp.js";
8
+ import type { App, LogLevel } from "../core/types.js";
9
+
10
+ /** Props for ProductSDKProvider */
11
+ export interface ProductSDKProviderProps {
12
+ /** Application name - used for storage namespacing and product account derivation */
13
+ name: string;
14
+ /** Log level (default: 'info') */
15
+ logLevel?: LogLevel;
16
+ /** Child components */
17
+ children: ReactNode;
18
+ /** Fallback to show while loading */
19
+ fallback?: ReactNode;
20
+ }
21
+
22
+ /**
23
+ * Provider component that initializes the Product SDK
24
+ *
25
+ * @example
26
+ * ```tsx
27
+ * import { ProductSDKProvider } from '@parity/product-sdk/react';
28
+ *
29
+ * function App() {
30
+ * return (
31
+ * <ProductSDKProvider name="my-app" fallback={<Loading />}>
32
+ * <MyApp />
33
+ * </ProductSDKProvider>
34
+ * );
35
+ * }
36
+ * ```
37
+ */
38
+ export function ProductSDKProvider({
39
+ name,
40
+ logLevel = "info",
41
+ children,
42
+ fallback = null,
43
+ }: ProductSDKProviderProps) {
44
+ const [app, setApp] = useState<App | null>(null);
45
+ const [error, setError] = useState<Error | null>(null);
46
+
47
+ useEffect(() => {
48
+ let mounted = true;
49
+
50
+ createApp({ name, logLevel })
51
+ .then((createdApp) => {
52
+ if (mounted) {
53
+ setApp(createdApp);
54
+ }
55
+ })
56
+ .catch((e) => {
57
+ if (mounted) {
58
+ setError(e instanceof Error ? e : new Error(String(e)));
59
+ }
60
+ });
61
+
62
+ return () => {
63
+ mounted = false;
64
+ };
65
+ }, [name, logLevel]);
66
+
67
+ if (error) {
68
+ throw error;
69
+ }
70
+
71
+ if (!app) {
72
+ return <>{fallback}</>;
73
+ }
74
+
75
+ return <ProductSDKContext.Provider value={app}>{children}</ProductSDKContext.Provider>;
76
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * useChain hook
3
+ */
4
+
5
+ import { useMemo } from "react";
6
+ import { useProductSDK } from "./context.js";
7
+ import type { ChainDefinition, TypedApi } from "../core/types.js";
8
+
9
+ /**
10
+ * Hook to get a typed chain client
11
+ *
12
+ * @param chain - Chain descriptor (PAPI ChainDefinition)
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * import { paseo_asset_hub } from '@parity/product-sdk-descriptors/paseo-asset-hub';
17
+ * import { useChain } from '@parity/product-sdk/react';
18
+ *
19
+ * function AssetHubBalance() {
20
+ * const assetHub = useChain(paseo_asset_hub);
21
+ *
22
+ * // assetHub is typed for Asset Hub queries
23
+ * const balance = await assetHub.query.System.Account.getValue(address);
24
+ * }
25
+ * ```
26
+ */
27
+ export function useChain<T extends ChainDefinition>(chain: T): TypedApi<T> {
28
+ const app = useProductSDK();
29
+
30
+ return useMemo(() => {
31
+ return app.chain.getClient(chain);
32
+ }, [app, chain]);
33
+ }
@@ -0,0 +1,125 @@
1
+ /**
2
+ * useStorage hook
3
+ */
4
+
5
+ import { useState, useEffect, useCallback } from "react";
6
+ import { useProductSDK } from "./context.js";
7
+
8
+ /**
9
+ * Hook for key-value storage operations
10
+ *
11
+ * @param key - Storage key
12
+ * @param defaultValue - Default value if key doesn't exist
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * function ThemeToggle() {
17
+ * const [theme, setTheme, { loading }] = useStorage('theme', 'light');
18
+ *
19
+ * if (loading) return <div>Loading...</div>;
20
+ *
21
+ * return (
22
+ * <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
23
+ * Current: {theme}
24
+ * </button>
25
+ * );
26
+ * }
27
+ * ```
28
+ */
29
+ export function useStorage<T = string>(
30
+ key: string,
31
+ defaultValue?: T,
32
+ ): [T | null, (value: T) => Promise<void>, { loading: boolean; error: Error | null }] {
33
+ const app = useProductSDK();
34
+
35
+ const [value, setValue] = useState<T | null>(null);
36
+ const [loading, setLoading] = useState(true);
37
+ const [error, setError] = useState<Error | null>(null);
38
+
39
+ // Load initial value
40
+ useEffect(() => {
41
+ let mounted = true;
42
+
43
+ const loadValue = async () => {
44
+ try {
45
+ setLoading(true);
46
+ const stored = await app.storage.getJSON<T>(key);
47
+ if (mounted) {
48
+ setValue(stored ?? defaultValue ?? null);
49
+ setLoading(false);
50
+ }
51
+ } catch (e) {
52
+ if (mounted) {
53
+ setError(e instanceof Error ? e : new Error(String(e)));
54
+ setLoading(false);
55
+ }
56
+ }
57
+ };
58
+
59
+ loadValue();
60
+
61
+ return () => {
62
+ mounted = false;
63
+ };
64
+ }, [app, key, defaultValue]);
65
+
66
+ const setStoredValue = useCallback(
67
+ async (newValue: T) => {
68
+ try {
69
+ setError(null);
70
+ await app.storage.setJSON(key, newValue);
71
+ setValue(newValue);
72
+ } catch (e) {
73
+ setError(e instanceof Error ? e : new Error(String(e)));
74
+ throw e;
75
+ }
76
+ },
77
+ [app, key],
78
+ );
79
+
80
+ return [value, setStoredValue, { loading, error }];
81
+ }
82
+
83
+ /**
84
+ * Hook for string storage (simpler API)
85
+ *
86
+ * @param key - Storage key
87
+ * @param defaultValue - Default value
88
+ */
89
+ export function useStorageString(
90
+ key: string,
91
+ defaultValue?: string,
92
+ ): [string | null, (value: string) => Promise<void>, { loading: boolean }] {
93
+ const app = useProductSDK();
94
+
95
+ const [value, setValue] = useState<string | null>(null);
96
+ const [loading, setLoading] = useState(true);
97
+
98
+ useEffect(() => {
99
+ let mounted = true;
100
+
101
+ const loadValue = async () => {
102
+ const stored = await app.storage.get(key);
103
+ if (mounted) {
104
+ setValue(stored ?? defaultValue ?? null);
105
+ setLoading(false);
106
+ }
107
+ };
108
+
109
+ loadValue();
110
+
111
+ return () => {
112
+ mounted = false;
113
+ };
114
+ }, [app, key, defaultValue]);
115
+
116
+ const setStoredValue = useCallback(
117
+ async (newValue: string) => {
118
+ await app.storage.set(key, newValue);
119
+ setValue(newValue);
120
+ },
121
+ [app, key],
122
+ );
123
+
124
+ return [value, setStoredValue, { loading }];
125
+ }