Skip to content

@parity/product-sdk-signer handles account discovery, selection, and signing, decoupled from where the keys actually live. Your Product talks to one class, SignerManager, and the same call sites work whether signing routes to the user's Polkadot App in production or to local dev accounts in a test.

Every fallible method returns a typed Result, so you check .ok before reading .value rather than wrapping calls in try/catch.

  • Whenever your Product needs to discover accounts, select one, and obtain a PolkadotSigner to sign transactions or raw bytes.
  • To manage the connection lifecycle: connect, disconnect, subscribe to state changes, and run once-per-session setup through the onConnect hook (for example, requesting permissions).
  • Use the host provider for real signing on Polkadot Desktop and the Polkadot App; use the dev provider for tests with well-known accounts such as Alice and Bob.
  • The product-account and Ring-VRF methods are Host-only; they return HostUnavailableError under the dev provider.
  • SignerManager: The central class. It wraps one or more providers behind a Result-typed API and holds a SignerState that it pushes to subscribers.
  • Result, ok, err: The return idiom across the package. Branch on res.ok, then read res.value or res.error; only unexpected internal failures throw.
  • SignerAccount: A signing-capable account. It exposes the SS58 address, the EVM-derived h160Address (for pallet-revive), the publicKey, an optional name, and getSigner().
  • Host vs dev providers: connect() defaults to the Host; connect('dev') loads well-known dev accounts locally, so no Host is needed for tests.
  • onConnect and subscribe: subscribe fires on every state change; onConnect fires exactly once per transition into the connected state (and again after an auto-reconnect), which is where you request resources up front.
  • Product accounts: getProductAccount(dotNsIdentifier, derivationIndex) returns a per-Product account the Host derives, so different Products get different addresses for the same user. This is a Host-only API.

Construct the manager once, connect, select an account, and sign. Each step returns a Result:

import { SignerManager } from '@parity/product-sdk-signer';
async function signHello() {
const manager = new SignerManager({ ss58Prefix: 0, dappName: 'my-product' });
const connectResult = await manager.connect();
if (!connectResult.ok) return; // HostUnavailableError outside a Host
const [account] = connectResult.value;
if (!account) return; // connected, but the Host returned no accounts
manager.selectAccount(account.address);
const signature = await manager.signRaw(new TextEncoder().encode('hello'));
if (signature.ok) {
console.log(signature.value); // Uint8Array
}
}

Use the onConnect hook to request resource allocations as soon as the connection is established, before any signing call. Unlike the rest of the package, requestResourceAllocation throws rather than returning a Result, so guard it:

import { SignerManager } from '@parity/product-sdk-signer';
const manager = new SignerManager({
ss58Prefix: 0,
dappName: 'my-product',
onConnect: async (_account, { requestResourceAllocation, signal }) => {
try {
const outcomes = await requestResourceAllocation([
{ tag: 'BulletinAllowance', value: undefined },
]);
if (signal.aborted) return; // user disconnected mid-request
if (outcomes.some((outcome) => outcome !== 'Allocated')) {
// Degrade gracefully: treat the capability as unavailable, not fatal.
}
} catch (cause) {
// Typed host error — the connection itself is unaffected.
}
},
});

Three things that example is doing deliberately:

  • try/catch: requestResourceAllocation adapts the Host's Result-returning call into a throwing one, so an unguarded await can throw inside onConnect. Errors thrown here are logged and do not break the connected state, but you lose the chance to react.
  • signal.aborted: The AbortSignal fires if the user disconnects or the manager is destroyed while the request is still in flight. Check it before acting on the outcomes.
  • Checking the outcomes: Each is 'Allocated', 'Rejected', or 'NotAvailable' — bare strings here, unlike the tagged objects the auth package returns. See Allowances and Permissions for what each resource authorizes.
  • Most methods return a Result; branch on .ok. Only terminal conditions such as calling a destroyed manager surface as thrown errors.
  • destroy() is terminal: later calls return DestroyedError. Use disconnect() for a reversible reset.
  • subscribe does not prime with the current state; call getState() for the initial read, and use onConnect for once-per-connect logic.
  • getProductAccount, getProductAccountAlias, createRingVRFProof, and getUserId are Host-only.
  • Sign and Submit Transactions


    The task-focused recipe: derive a product account, sign, and submit a transaction end to end.

    → Sign and Submit Transactions

  • Transactions


    Take the signer this package produces and submit and track a transaction to finality.

    → Transactions

  • API Reference


    The complete signer surface: SignerManager, SignerAccount, providers, and the error hierarchy.

    → Visit Site

Last updated: