@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.
- package/LICENSE +201 -0
- package/dist/address/index.d.ts +9 -0
- package/dist/address/index.js +3 -0
- package/dist/address/index.js.map +1 -0
- package/dist/chain/index.d.ts +1 -0
- package/dist/chain/index.js +3 -0
- package/dist/chain/index.js.map +1 -0
- package/dist/chunk-BRZNHTLQ.js +89 -0
- package/dist/chunk-BRZNHTLQ.js.map +1 -0
- package/dist/chunk-DHRCWMY5.js +240 -0
- package/dist/chunk-DHRCWMY5.js.map +1 -0
- package/dist/chunk-JVJ552RU.js +3 -0
- package/dist/chunk-JVJ552RU.js.map +1 -0
- package/dist/cloud-storage/index.d.ts +1 -0
- package/dist/cloud-storage/index.js +3 -0
- package/dist/cloud-storage/index.js.map +1 -0
- package/dist/contracts/index.d.ts +1 -0
- package/dist/contracts/index.js +3 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/core/index.d.ts +12 -0
- package/dist/core/index.js +5 -0
- package/dist/core/index.js.map +1 -0
- package/dist/crypto/index.d.ts +1 -0
- package/dist/crypto/index.js +3 -0
- package/dist/crypto/index.js.map +1 -0
- package/dist/dotns-bswWAmR2.d.ts +136 -0
- package/dist/host/index.d.ts +1 -0
- package/dist/host/index.js +3 -0
- package/dist/host/index.js.map +1 -0
- package/dist/identity/index.d.ts +491 -0
- package/dist/identity/index.js +691 -0
- package/dist/identity/index.js.map +1 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/individuality/index.d.ts +1 -0
- package/dist/individuality/index.js +3 -0
- package/dist/individuality/index.js.map +1 -0
- package/dist/local-storage/index.d.ts +1 -0
- package/dist/local-storage/index.js +3 -0
- package/dist/local-storage/index.js.map +1 -0
- package/dist/react/index.d.ts +181 -0
- package/dist/react/index.js +198 -0
- package/dist/react/index.js.map +1 -0
- package/dist/testing/index.d.ts +61 -0
- package/dist/testing/index.js +149 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/types-B5Zx4RNq.d.ts +211 -0
- package/dist/wallet/index.d.ts +1 -0
- package/dist/wallet/index.js +3 -0
- package/dist/wallet/index.js.map +1 -0
- package/package.json +118 -0
- package/src/address/index.ts +8 -0
- package/src/chain/index.ts +8 -0
- package/src/cloud-storage/index.ts +8 -0
- package/src/contracts/index.ts +8 -0
- package/src/core/createApp.ts +408 -0
- package/src/core/index.ts +12 -0
- package/src/core/logger.ts +15 -0
- package/src/core/types.ts +233 -0
- package/src/crypto/index.ts +8 -0
- package/src/host/index.ts +8 -0
- package/src/identity/dotns-abis.test.ts +43 -0
- package/src/identity/dotns-abis.ts +274 -0
- package/src/identity/dotns-errors.ts +34 -0
- package/src/identity/dotns-namehash.test.ts +155 -0
- package/src/identity/dotns-namehash.ts +149 -0
- package/src/identity/dotns-owner.test.ts +92 -0
- package/src/identity/dotns-registry.test.ts +1154 -0
- package/src/identity/dotns-registry.ts +870 -0
- package/src/identity/dotns.test.ts +128 -0
- package/src/identity/dotns.ts +221 -0
- package/src/identity/index.ts +61 -0
- package/src/identity/product-account.test.ts +130 -0
- package/src/identity/product-account.ts +115 -0
- package/src/identity/types.ts +87 -0
- package/src/index.ts +61 -0
- package/src/individuality/contract.test.ts +135 -0
- package/src/individuality/index.ts +8 -0
- package/src/local-storage/index.ts +8 -0
- package/src/react/context.ts +35 -0
- package/src/react/index.ts +43 -0
- package/src/react/provider.tsx +78 -0
- package/src/react/useChain.ts +35 -0
- package/src/react/useLocalStorage.ts +127 -0
- package/src/react/useWallet.ts +156 -0
- package/src/testing.ts +297 -0
- 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,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
|
+
}
|