@parity/product-sdk-signer 0.4.0 → 0.6.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/dist/index.d.ts CHANGED
@@ -272,6 +272,21 @@ interface HostProviderOptions {
272
272
  requestChainSubmitPermission?: boolean;
273
273
  /** @deprecated Renamed to `requestChainSubmitPermission`. */
274
274
  requestTransactionSubmitPermission?: boolean;
275
+ /**
276
+ * If set, `connect()` returns a single product account for the given
277
+ * `dotNsIdentifier`, skipping the legacy fetch entirely. For apps
278
+ * that sign exclusively with a per-dapp derived account.
279
+ *
280
+ * `SignerAccount.name` is populated best-effort from
281
+ * `accounts.getUserId().primaryUsername`; failures leave it null.
282
+ * Signing is pinned to `createTransaction` (see PR #96).
283
+ */
284
+ productAccount?: {
285
+ /** App identifier (e.g., `"playground.dot"`). */
286
+ dotNsIdentifier: string;
287
+ /** Derivation index within the app scope. Default: 0. */
288
+ derivationIndex?: number;
289
+ };
275
290
  }
276
291
  /**
277
292
  * A product account — an app-scoped derived account managed by the host wallet.
@@ -324,6 +339,9 @@ interface AccountsProvider {
324
339
  getProductAccount: (dotNsIdentifier: string, derivationIndex?: number) => NeverthrowResultAsync<RawAccount, unknown>;
325
340
  getProductAccountSigner: (account: ProductAccount, signerType?: "signPayload" | "createTransaction") => polkadot_api.PolkadotSigner;
326
341
  getProductAccountAlias: (dotNsIdentifier: string, derivationIndex?: number) => NeverthrowResultAsync<ContextualAlias, unknown>;
342
+ getUserId: () => NeverthrowResultAsync<{
343
+ primaryUsername: string;
344
+ }, unknown>;
327
345
  createRingVRFProof: (dotNsIdentifier: string, derivationIndex: number, location: unknown, message: Uint8Array) => NeverthrowResultAsync<Uint8Array, unknown>;
328
346
  subscribeAccountConnectionStatus: (callback: (status: string) => void) => {
329
347
  unsubscribe: () => void;
@@ -369,6 +387,7 @@ declare class HostProvider implements SignerProvider {
369
387
  private readonly loadSdk;
370
388
  private readonly loadHostApiEnum;
371
389
  private readonly requestChainSubmitPermission;
390
+ private readonly productAccount;
372
391
  private accountsProvider;
373
392
  private statusCleanup;
374
393
  private statusListeners;
@@ -420,6 +439,7 @@ declare class HostProvider implements SignerProvider {
420
439
  */
421
440
  createRingVRFProof(dotNsIdentifier: string, derivationIndex: number, location: RingLocation, message: Uint8Array): Promise<Result<Uint8Array, SignerError>>;
422
441
  private tryConnect;
442
+ private fetchProductSignerAccount;
423
443
  private mapAccounts;
424
444
  }
425
445
 
package/dist/index.js CHANGED
@@ -195,6 +195,7 @@ var HostProvider = class {
195
195
  loadSdk;
196
196
  loadHostApiEnum;
197
197
  requestChainSubmitPermission;
198
+ productAccount;
198
199
  accountsProvider = null;
199
200
  statusCleanup = null;
200
201
  statusListeners = /* @__PURE__ */ new Set();
@@ -206,6 +207,7 @@ var HostProvider = class {
206
207
  this.loadSdk = options?.loadSdk ?? defaultLoadSdk;
207
208
  this.loadHostApiEnum = options?.loadHostApiEnum ?? defaultLoadHostApiEnum;
208
209
  this.requestChainSubmitPermission = options?.requestChainSubmitPermission ?? options?.requestTransactionSubmitPermission ?? true;
210
+ this.productAccount = options?.productAccount;
209
211
  }
210
212
  async connect(signal) {
211
213
  log2.debug("attempting Host API connection");
@@ -392,25 +394,37 @@ var HostProvider = class {
392
394
  }
393
395
  const provider = sdk.createAccountsProvider();
394
396
  this.accountsProvider = provider;
395
- let rawAccounts;
396
- try {
397
- rawAccounts = await provider.getLegacyAccounts().match(
398
- (accounts2) => accounts2,
399
- (error) => {
400
- throw new Error(`Host rejected account request: ${formatError(error)}`);
401
- }
402
- );
403
- } catch (cause) {
404
- log2.error("failed to get accounts from host", { cause });
405
- return err(
406
- new HostRejectedError(
407
- cause instanceof Error ? cause.message : "Failed to get accounts from host"
408
- )
397
+ let signerAccounts;
398
+ if (this.productAccount) {
399
+ const accountResult = await this.fetchProductSignerAccount(
400
+ provider,
401
+ this.productAccount.dotNsIdentifier,
402
+ this.productAccount.derivationIndex ?? 0
409
403
  );
410
- }
411
- if (rawAccounts.length === 0) {
412
- log2.warn("host returned no accounts");
413
- return err(new NoAccountsError("host"));
404
+ if (!accountResult.ok) return accountResult;
405
+ signerAccounts = [accountResult.value];
406
+ } else {
407
+ let rawAccounts;
408
+ try {
409
+ rawAccounts = await provider.getLegacyAccounts().match(
410
+ (accounts) => accounts,
411
+ (error) => {
412
+ throw new Error(`Host rejected account request: ${formatError(error)}`);
413
+ }
414
+ );
415
+ } catch (cause) {
416
+ log2.error("failed to get accounts from host", { cause });
417
+ return err(
418
+ new HostRejectedError(
419
+ cause instanceof Error ? cause.message : "Failed to get accounts from host"
420
+ )
421
+ );
422
+ }
423
+ if (rawAccounts.length === 0) {
424
+ log2.warn("host returned no accounts");
425
+ return err(new NoAccountsError("host"));
426
+ }
427
+ signerAccounts = this.mapAccounts(rawAccounts);
414
428
  }
415
429
  if (this.requestChainSubmitPermission && sdk.hostApi) {
416
430
  try {
@@ -433,8 +447,7 @@ var HostProvider = class {
433
447
  log2.warn("failed to request ChainSubmit permission", { cause });
434
448
  }
435
449
  }
436
- const accounts = this.mapAccounts(rawAccounts);
437
- log2.info("host connected", { accounts: accounts.length });
450
+ log2.info("host connected", { accounts: signerAccounts.length });
438
451
  const sub = provider.subscribeAccountConnectionStatus((status) => {
439
452
  const mapped = status === "connected" ? "connected" : "disconnected";
440
453
  log2.debug("host status changed", { status: mapped });
@@ -443,7 +456,32 @@ var HostProvider = class {
443
456
  }
444
457
  });
445
458
  this.statusCleanup = typeof sub === "function" ? sub : () => sub.unsubscribe();
446
- return ok(accounts);
459
+ return ok(signerAccounts);
460
+ }
461
+ async fetchProductSignerAccount(provider, dotNsIdentifier, derivationIndex) {
462
+ const fetchUsername = async () => {
463
+ try {
464
+ return await provider.getUserId().match(
465
+ (result) => result.primaryUsername,
466
+ (error) => {
467
+ log2.debug("getUserId failed; product account name stays null", {
468
+ error: formatError(error)
469
+ });
470
+ return null;
471
+ }
472
+ );
473
+ } catch (cause) {
474
+ log2.debug("getUserId threw; product account name stays null", { cause });
475
+ return null;
476
+ }
477
+ };
478
+ const [accountResult, primaryUsername] = await Promise.all([
479
+ this.getProductAccount(dotNsIdentifier, derivationIndex),
480
+ fetchUsername()
481
+ ]);
482
+ if (!accountResult.ok) return accountResult;
483
+ const account = accountResult.value;
484
+ return ok({ ...account, name: account.name ?? primaryUsername });
447
485
  }
448
486
  mapAccounts(rawAccounts) {
449
487
  return rawAccounts.map((raw) => {