@parity/product-sdk 0.0.0-dev.312.0 → 0.0.0-dev.329.1

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 (57) hide show
  1. package/dist/address/index.d.ts +3 -1
  2. package/dist/{chunk-BRZNHTLQ.js → chunk-7GM3HUZF.js} +2 -2
  3. package/dist/chunk-7GM3HUZF.js.map +1 -0
  4. package/dist/chunk-TUXU2VPS.js +14 -0
  5. package/dist/chunk-TUXU2VPS.js.map +1 -0
  6. package/dist/{chunk-DHRCWMY5.js → chunk-VWD6HK2C.js} +29 -20
  7. package/dist/chunk-VWD6HK2C.js.map +1 -0
  8. package/dist/core/index.d.ts +50 -3
  9. package/dist/core/index.js +3 -2
  10. package/dist/{dotns-bswWAmR2.d.ts → dotns-CtmOwVQW.d.ts} +2 -2
  11. package/dist/identity/index.d.ts +118 -89
  12. package/dist/identity/index.js +200 -78
  13. package/dist/identity/index.js.map +1 -1
  14. package/dist/index.d.ts +3 -53
  15. package/dist/index.js +3 -2
  16. package/dist/nfts/index.d.ts +1 -0
  17. package/dist/nfts/index.js +3 -0
  18. package/dist/nfts/index.js.map +1 -0
  19. package/dist/react/index.d.ts +5 -5
  20. package/dist/react/index.js +4 -3
  21. package/dist/react/index.js.map +1 -1
  22. package/dist/renderer/index.d.ts +1 -0
  23. package/dist/renderer/index.js +3 -0
  24. package/dist/renderer/index.js.map +1 -0
  25. package/dist/testing/index.d.ts +2 -2
  26. package/dist/testing/index.js +3 -5
  27. package/dist/testing/index.js.map +1 -1
  28. package/dist/{types-B5Zx4RNq.d.ts → types-BpWw-OJl.d.ts} +19 -9
  29. package/package.json +23 -12
  30. package/src/core/createApp.test.ts +194 -0
  31. package/src/core/createApp.ts +35 -22
  32. package/src/core/local-storage-api.ts +74 -0
  33. package/src/core/types.ts +18 -7
  34. package/src/identity/dotns-abis.ts +44 -9
  35. package/src/identity/dotns-addresses.test.ts +536 -0
  36. package/src/identity/dotns-addresses.ts +320 -0
  37. package/src/identity/dotns-errors.ts +2 -0
  38. package/src/identity/dotns-namehash.ts +2 -2
  39. package/src/identity/dotns-registry.test.ts +158 -3
  40. package/src/identity/dotns-registry.ts +71 -73
  41. package/src/identity/dotns.ts +2 -2
  42. package/src/identity/index.test.ts +43 -0
  43. package/src/identity/index.ts +10 -6
  44. package/src/identity/types.ts +0 -19
  45. package/src/index.ts +3 -2
  46. package/src/individuality/contract.test.ts +426 -2
  47. package/src/nfts/contract.test.ts +58 -0
  48. package/src/nfts/index.ts +8 -0
  49. package/src/react/context.ts +1 -1
  50. package/src/react/index.ts +1 -1
  51. package/src/react/provider.tsx +3 -3
  52. package/src/renderer/index.ts +10 -0
  53. package/src/testing.ts +14 -11
  54. package/dist/chunk-BRZNHTLQ.js.map +0 -1
  55. package/dist/chunk-DHRCWMY5.js.map +0 -1
  56. package/src/identity/product-account.test.ts +0 -130
  57. package/src/identity/product-account.ts +0 -115
@@ -12,7 +12,7 @@
12
12
  *
13
13
  * **Names are rooted at the network's TLD, which is per deployment.** It is
14
14
  * fixed when `DotnsProtocolRegistry.initialize` runs, has no setter, and is
15
- * published as `tld()`. `.paseo` on Paseo Asset Hub Next V2, `.dot` on
15
+ * published as `tld()`. `.paseo` on Paseo Asset Hub Next V2, `.test` on
16
16
  * Previewnet, and operators can choose others. Every entry point that hashes a
17
17
  * name therefore asks the chain first, through {@link resolveTld}, and callers
18
18
  * can supply `opts.tld` to skip the read. Hashing under the wrong root returns a
@@ -41,13 +41,7 @@
41
41
  * expiry getter), so `DotNsRecord.expiresAt` is always omitted.
42
42
  */
43
43
  import { err, ok, type Result } from "@parity/result";
44
- import {
45
- type AbiEntry,
46
- type BatchableCall,
47
- createContract,
48
- QUERY_FALLBACK_ORIGIN,
49
- } from "@parity/product-sdk-contracts";
50
- import type { ContractRuntime } from "@parity/product-sdk-contracts";
44
+ import type { AbiEntry, BatchableCall, ContractRuntime } from "@parity/product-sdk-contracts";
51
45
  import { bytesToHex, randomBytes } from "@parity/product-sdk-crypto";
52
46
  import { ss58ToH160 } from "@parity/product-sdk-address";
53
47
  import { createLogger } from "@parity/product-sdk-logger";
@@ -64,6 +58,16 @@ import {
64
58
  POP_STATUS,
65
59
  } from "./dotns-abis.js";
66
60
  import { DotNsError } from "./dotns-errors.js";
61
+ import {
62
+ type DotNsGatewayQueryApi,
63
+ type RuntimeCache,
64
+ cachedPerRuntime,
65
+ contractOf,
66
+ isZero,
67
+ readOrigin,
68
+ resolveDotNsAddresses,
69
+ sameAddress,
70
+ } from "./dotns-addresses.js";
67
71
  import { isResolvableDotNsName, isValidDotNsName, normalizeDotNsName } from "./dotns.js";
68
72
  import {
69
73
  DOT_TLD,
@@ -79,8 +83,6 @@ const log = createLogger("identity:dotns");
79
83
 
80
84
  type HexString = `0x${string}`;
81
85
 
82
- const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
83
-
84
86
  /** Shared inputs for a DotNS registry call. */
85
87
  export interface DotNsClientOptions {
86
88
  /** A contract runtime for the chain hosting DotNS (Asset Hub). */
@@ -112,6 +114,36 @@ export interface DotNsClientOptions {
112
114
  * the whole set is CREATE3-deterministic.
113
115
  */
114
116
  protocolRegistryAddress?: HexString;
117
+ /**
118
+ * Where the addresses come from. `"pinned"` (the default) uses the
119
+ * {@link DOTNS_ADDRESSES} table; `"discovered"` walks the gateway on the
120
+ * chain `runtime` points at, so nothing is trusted from this bundle.
121
+ *
122
+ * Pinned is the default because the table is currently correct and a walk
123
+ * costs four round trips on the first call. Discovery is the honest choice
124
+ * once a deployment has moved, and its trust root is
125
+ * `DotnsGateway.DispatcherAddress` — a governance-set value on the chain the
126
+ * caller already trusts for every name read. Per-field address overrides win
127
+ * over either source.
128
+ *
129
+ * Discovery also selects the address `setDotNsRecord` and
130
+ * `prepareDotNsRegistration` build calls against, so it decides where a
131
+ * signed transaction goes, not only where a read comes from. A pinned
132
+ * address constrains that destination even against a hostile RPC; a
133
+ * discovered one does not. Use {@link verifyDotNsAddresses} at startup if
134
+ * that matters.
135
+ */
136
+ addressSource?: "pinned" | "discovered";
137
+ /**
138
+ * Typed API for the chain hosting the `DotnsGateway` pallet, needed only by
139
+ * `"discovered"`.
140
+ *
141
+ * Optional because `runtime.api` is usually the same chain and is used when
142
+ * it carries the pallet. `ContractRuntime` does not declare that storage, so
143
+ * it is probed rather than assumed; on a chain without the pallet, discovery
144
+ * fails with `AddressDiscovery` instead of throwing.
145
+ */
146
+ gatewayApi?: DotNsGatewayQueryApi;
115
147
  /**
116
148
  * The deployment's TLD, when you already know it.
117
149
  *
@@ -159,29 +191,6 @@ export interface SetRecordArgs {
159
191
  address: string;
160
192
  }
161
193
 
162
- function contractOf(runtime: ContractRuntime, address: HexString, abi: AbiEntry[]) {
163
- // createContract is generic over a typed ABI def; these minimal literal ABIs
164
- // are called by name via .query(), so the handle is untyped by construction.
165
- return createContract(runtime, address, abi as any) as any;
166
- }
167
-
168
- function isZero(addr: unknown): boolean {
169
- return typeof addr === "string" && addr.toLowerCase() === ZERO_ADDRESS;
170
- }
171
-
172
- /**
173
- * Origin for a read. Passing the fallback explicitly rather than letting the
174
- * contracts layer substitute it keeps each query from logging a warning.
175
- */
176
- function readOrigin(opts: DotNsClientOptions): SS58String {
177
- return opts.origin ?? QUERY_FALLBACK_ORIGIN;
178
- }
179
-
180
- /** Case-insensitive H160 compare: chain reads are lowercase, our table is EIP-55. */
181
- function sameAddress(a: unknown, b: unknown): boolean {
182
- return typeof a === "string" && typeof b === "string" && a.toLowerCase() === b.toLowerCase();
183
- }
184
-
185
194
  // ── The deployment's TLD ─────────────────────────────────────────────
186
195
  //
187
196
  // Every node hash is rooted at the network's TLD, which is fixed when
@@ -189,14 +198,11 @@ function sameAddress(a: unknown, b: unknown): boolean {
189
198
  // (runtime, protocol registry) and cache it: it cannot change under us.
190
199
 
191
200
  /**
192
- * Cached TLD per runtime, then per protocol-registry address.
193
- *
194
- * Keyed on the address as well as the runtime because the address is
195
- * overridable, and one runtime pointed at two registries has two TLDs. The
196
- * value is the in-flight promise rather than the resolved TLD, so concurrent
197
- * first calls share one read instead of racing.
201
+ * Cached TLD per runtime, then per protocol-registry address. Keyed on the
202
+ * address as well because it is overridable, and one runtime pointed at two
203
+ * registries has two TLDs.
198
204
  */
199
- const tldCache = new WeakMap<ContractRuntime, Map<string, Promise<Result<DotNsTld, DotNsError>>>>();
205
+ const tldCache: RuntimeCache<DotNsTld> = new WeakMap();
200
206
 
201
207
  /** An empty-payload revert: the contract ran and refused, telling us nothing. */
202
208
  function isEmptyRevert(value: unknown): boolean {
@@ -225,8 +231,8 @@ function isEmptyRevert(value: unknown): boolean {
225
231
  * via the protocol registry"). Before that commit the TLD was a
226
232
  * compile-time constant and `.dot` was the only value it could hold, so
227
233
  * this is the one case where the fallback is provably right rather than
228
- * merely plausible. Verified against Paseo Asset Hub Previewnet, where both
229
- * getters revert empty and `dim2` is owned under the `.dot` root;
234
+ * merely plausible. No live deployment is known to still take this
235
+ * branch: Previewnet was cited for it, but reports `.test`;
230
236
  * - anything else → `err`. A dispatch failure, a revert carrying a reason, an
231
237
  * undecodable answer, or a TLD we cannot use.
232
238
  *
@@ -253,24 +259,10 @@ export async function resolveTld(opts: DotNsClientOptions): Promise<Result<DotNs
253
259
  );
254
260
  }
255
261
 
256
- const address = (opts.protocolRegistryAddress ?? DOTNS_ADDRESSES.protocolRegistry) as HexString;
257
- let perAddress = tldCache.get(opts.runtime);
258
- if (!perAddress) {
259
- perAddress = new Map();
260
- tldCache.set(opts.runtime, perAddress);
261
- }
262
- const cached = perAddress.get(address);
263
- if (cached) return cached;
264
-
265
- const pending = readTld(opts, address);
266
- perAddress.set(address, pending);
267
- // Only a successful read is worth keeping: a failure is usually transient
268
- // (RPC, not deployment), and caching it would strand the client for the
269
- // life of the runtime.
270
- pending.then((result) => {
271
- if (!result.ok) perAddress?.delete(address);
272
- });
273
- return pending;
262
+ const addresses = await resolveDotNsAddresses(opts);
263
+ if (!addresses.ok) return addresses;
264
+ const address = addresses.value.protocolRegistry;
265
+ return cachedPerRuntime(tldCache, opts.runtime, address, () => readTld(opts, address));
274
266
  }
275
267
 
276
268
  async function readTld(
@@ -390,7 +382,9 @@ export async function resolveDotNs(
390
382
  return err(nameError(name, normalized, tld));
391
383
  }
392
384
  const node = namehash(normalized, tld);
393
- const registryAddr = opts.registryAddress ?? DOTNS_ADDRESSES.registry;
385
+ const addrs = await resolveDotNsAddresses(opts);
386
+ if (!addrs.ok) return addrs;
387
+ const registryAddr = addrs.value.registry;
394
388
  const origin = readOrigin(opts);
395
389
  log.debug("resolveDotNs", { name: normalized, node, registry: registryAddr });
396
390
 
@@ -435,7 +429,7 @@ export async function resolveDotNs(
435
429
  // way; before this, the read and write paths disagreed about what a
436
430
  // pointer meant.
437
431
  const resolverAddr = resolverRes.value as HexString;
438
- const forwardAddr = opts.resolverAddress ?? DOTNS_ADDRESSES.resolver;
432
+ const forwardAddr = addrs.value.resolver;
439
433
  if (!sameAddress(resolverAddr, forwardAddr)) {
440
434
  return ok({ name: normalized, owner });
441
435
  }
@@ -471,7 +465,9 @@ export async function reverseDotNs(
471
465
  address: string,
472
466
  opts: DotNsClientOptions,
473
467
  ): Promise<Result<string | null, DotNsError>> {
474
- const reverseAddr = opts.reverseResolverAddress ?? DOTNS_ADDRESSES.reverseResolver;
468
+ const addrs = await resolveDotNsAddresses(opts);
469
+ if (!addrs.ok) return addrs;
470
+ const reverseAddr = addrs.value.reverseResolver;
475
471
  log.debug("reverseDotNs", { address, reverseResolver: reverseAddr });
476
472
  try {
477
473
  const reverse = contractOf(
@@ -529,7 +525,9 @@ export async function isDotNsAvailable(
529
525
  }
530
526
  // The registrar takes the bare label, without the TLD suffix.
531
527
  const label = stripSuffix(normalized, tld.suffix);
532
- const controllerAddr = opts.registrarControllerAddress ?? DOTNS_ADDRESSES.registrarController;
528
+ const addrs = await resolveDotNsAddresses(opts);
529
+ if (!addrs.ok) return addrs;
530
+ const controllerAddr = addrs.value.registrarController;
533
531
  log.debug("isDotNsAvailable", { name: normalized, label, controller: controllerAddr });
534
532
 
535
533
  try {
@@ -538,11 +536,7 @@ export async function isDotNsAvailable(
538
536
  controllerAddr as HexString,
539
537
  DOTNS_REGISTRAR_CONTROLLER_ABI,
540
538
  );
541
- const popRules = contractOf(
542
- opts.runtime,
543
- (opts.popRulesAddress ?? DOTNS_ADDRESSES.popRules) as HexString,
544
- DOTNS_POP_RULES_ABI,
545
- );
539
+ const popRules = contractOf(opts.runtime, addrs.value.popRules, DOTNS_POP_RULES_ABI);
546
540
  const origin = readOrigin(opts);
547
541
  const [res, classified] = await Promise.all([
548
542
  controller.available.query(label, { origin }),
@@ -610,8 +604,10 @@ export async function setDotNsRecord(
610
604
  return err(new DotNsError("MissingOrigin", "setDotNsRecord needs opts.origin (SS58)"));
611
605
  }
612
606
  const node = namehash(normalized, tld);
613
- const registryAddr = opts.registryAddress ?? DOTNS_ADDRESSES.registry;
614
- const resolverAddr = opts.resolverAddress ?? DOTNS_ADDRESSES.resolver;
607
+ const addrs = await resolveDotNsAddresses(opts);
608
+ if (!addrs.ok) return addrs;
609
+ const registryAddr = addrs.value.registry;
610
+ const resolverAddr = addrs.value.resolver;
615
611
  try {
616
612
  const registry = contractOf(opts.runtime, registryAddr as HexString, DOTNS_REGISTRY_ABI);
617
613
  const currentRes = await registry.resolver.query(node, { origin });
@@ -766,8 +762,10 @@ export async function prepareDotNsRegistration(
766
762
  }
767
763
  // The registrar takes the bare label, without the TLD suffix.
768
764
  const label = stripSuffix(normalized, tld.suffix);
769
- const controllerAddr = opts.registrarControllerAddress ?? DOTNS_ADDRESSES.registrarController;
770
- const popRulesAddr = opts.popRulesAddress ?? DOTNS_ADDRESSES.popRules;
765
+ const addrs = await resolveDotNsAddresses(opts);
766
+ if (!addrs.ok) return addrs;
767
+ const controllerAddr = addrs.value.registrarController;
768
+ const popRulesAddr = addrs.value.popRules;
771
769
  const secret = `0x${bytesToHex(randomBytes(32))}` as HexString;
772
770
  const registration = { label, owner: args.owner, secret, reserved: args.reserved ?? false };
773
771
  // register compares msg.sender against the owner; msg.sender is derived
@@ -75,8 +75,8 @@ export type PeopleUsernameQueryApi = {
75
75
  * Whether `name` is a single registrable label under `suffix`.
76
76
  *
77
77
  * `suffix` is the deployment's own TLD, including its leading dot, as
78
- * `resolveTld` reports it — `".dot"` on Paseo Asset Hub Previewnet, `".paseo"`
79
- * on Next V2. It is required rather than defaulted, because a name is only
78
+ * `resolveTld` reports it — `".paseo"` on Paseo Asset Hub Next V2, `".test"`
79
+ * on Previewnet. It is required rather than defaulted, because a name is only
80
80
  * valid *relative to a deployment*: `alice.dot` is registrable on one network
81
81
  * and meaningless on the other, and a default would silently pick one.
82
82
  *
@@ -0,0 +1,43 @@
1
+ // Copyright 2026 Parity Technologies (UK) Ltd.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * Guards the identity export surface: what must be there, and what must stay gone.
5
+ */
6
+ import { describe, expect, it } from "vitest";
7
+ import * as identity from "./index.js";
8
+
9
+ // The check below is a runtime one, so it cannot see a removed type. If this
10
+ // type comes back, the suppression goes unused and typecheck fails.
11
+ // @ts-expect-error must stay removed
12
+ import type { ContextAliasInfo } from "./types.js";
13
+
14
+ describe("the identity export surface", () => {
15
+ // Both sets threw or misled on every call, so no working consumer could
16
+ // exist. Deleting turns a runtime problem into a compile error.
17
+ it("no longer exports the ring stubs or the context-alias helpers", () => {
18
+ for (const name of [
19
+ "deriveAnonymousAlias",
20
+ "createRingProof",
21
+ "verifyRingProof",
22
+ "deriveContextAlias",
23
+ "verifyContextAlias",
24
+ ]) {
25
+ expect(name in identity).toBe(false);
26
+ }
27
+ });
28
+
29
+ it("still exports the DotNS helpers", () => {
30
+ for (const name of [
31
+ "resolveDotNs",
32
+ "reverseDotNs",
33
+ "isDotNsAvailable",
34
+ "resolvePeopleUsernameOwner",
35
+ "isValidDotNsName",
36
+ "normalizeDotNsName",
37
+ "accountIdHexToBytes",
38
+ "accountIdToHex",
39
+ ]) {
40
+ expect(name in identity).toBe(true);
41
+ }
42
+ });
43
+ });
@@ -3,10 +3,9 @@
3
3
  /**
4
4
  * @parity/product-sdk/identity
5
5
  *
6
- * Identity utilities: DotNS name resolution, plus the deprecated context-alias
7
- * helpers.
6
+ * Identity utilities: DotNS name resolution and registry access.
8
7
  *
9
- * For real Ring VRF aliases and proofs, use `SignerManager`'s
8
+ * For Ring VRF aliases and proofs, use `SignerManager`'s
10
9
  * `getProductAccountAlias` and `createRingVRFProof` from
11
10
  * `@parity/product-sdk-signer`.
12
11
  */
@@ -49,13 +48,18 @@ export {
49
48
  export type { DotNsTld } from "./dotns-namehash.js";
50
49
  export { POP_STATUS, DOTNS_ADDRESSES } from "./dotns-abis.js";
51
50
 
52
- // Context alias utilities (deprecated)
53
- export { deriveContextAlias, verifyContextAlias } from "./product-account.js";
51
+ // Where those addresses come from: the pinned table, or the gateway walk.
52
+ export {
53
+ DOTNS_REGISTRY_KEYS,
54
+ discoverDotNsAddresses,
55
+ resolveDotNsAddresses,
56
+ verifyDotNsAddresses,
57
+ } from "./dotns-addresses.js";
58
+ export type { DotNsAddresses, DotNsGatewayQueryApi } from "./dotns-addresses.js";
54
59
 
55
60
  // Types
56
61
  export type {
57
62
  DotNsRecord,
58
- ContextAliasInfo,
59
63
  VerificationResult,
60
64
  OnChainIdentity,
61
65
  } from "./types.js";
@@ -2,8 +2,6 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  /**
4
4
  * Identity module types
5
- *
6
- * Types for DotNS name resolution and context-alias derivation
7
5
  */
8
6
 
9
7
  /** DotNS name resolution result */
@@ -41,23 +39,6 @@ export interface DotNsRecord {
41
39
  expiresAt?: number;
42
40
  }
43
41
 
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
42
  /** Identity verification result */
62
43
  export interface VerificationResult {
63
44
  /** Whether identity is verified */
package/src/index.ts CHANGED
@@ -4,13 +4,13 @@
4
4
  * @parity/product-sdk
5
5
  *
6
6
  * Unified SDK for building products on the Polkadot ecosystem.
7
+ * App identity comes from the host and scopes wallet accounts and local storage.
7
8
  *
8
9
  * @example
9
10
  * ```ts
10
11
  * import { createApp } from '@parity/product-sdk';
11
12
  *
12
- * const app = createApp({
13
- * name: 'my-app',
13
+ * const app = await createApp({
14
14
  * logLevel: 'info',
15
15
  * });
16
16
  *
@@ -30,6 +30,7 @@ export { configure, createLogger } from "./core/logger.js";
30
30
  export type {
31
31
  App,
32
32
  AppConfig,
33
+ AppInfo,
33
34
  LogLevel,
34
35
  WalletApi,
35
36
  LocalStorageApi,