Signer¶
Introduction¶
@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.
When to Use It¶
- Whenever your Product needs to discover accounts, select one, and obtain a
PolkadotSignerto sign transactions or raw bytes. - To manage the connection lifecycle: connect, disconnect, subscribe to state changes, and run once-per-session setup through the
onConnecthook (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
HostUnavailableErrorunder the dev provider.
Core Concepts¶
SignerManager: The central class. It wraps one or more providers behind aResult-typed API and holds aSignerStatethat it pushes to subscribers.Result,ok,err: The return idiom across the package. Branch onres.ok, then readres.valueorres.error; only unexpected internal failures throw.SignerAccount: A signing-capable account. It exposes the SS58address, the EVM-derivedh160Address(forpallet-revive), thepublicKey, an optionalname, andgetSigner().- Host vs dev providers:
connect()defaults to the Host;connect('dev')loads well-known dev accounts locally, so no Host is needed for tests. onConnectandsubscribe:subscribefires on every state change;onConnectfires 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.
Connect and Sign Raw Bytes¶
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
}
}
A successful connect can still yield zero accounts
connect() resolves with ok([]) — not an error — when dappName is unset or the Host rejects the derivation, typically because the .dot identifier is not registered for that user. Destructure and check before indexing, or value[0].address throws on a path the SDK documents as normal. A Product in that state can still drive the explicit signing paths, such as getLegacyAccountSigner.
Request Permissions Once Per Session¶
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:requestResourceAllocationadapts the Host'sResult-returning call into a throwing one, so an unguardedawaitcan throw insideonConnect. Errors thrown here are logged and do not break the connected state, but you lose the chance to react.signal.aborted: TheAbortSignalfires 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 theauthpackage returns. See Allowances and Permissions for what each resource authorizes.
Why not AutoSigning in this example
AutoSigning is the most interesting resource to request and the one you cannot rely on: it returns NotAvailable on both the Android and iOS wallets today. Request it if you want, but treat per-transaction signing as the real path and do not build a flow that depends on the grant landing.
Limitations¶
- 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 returnDestroyedError. Usedisconnect()for a reversible reset.subscribedoes not prime with the current state; callgetState()for the initial read, and useonConnectfor once-per-connect logic.getProductAccount,getProductAccountAlias,createRingVRFProof, andgetUserIdare Host-only.
Where to Go Next¶
-
Guide Sign and Submit Transactions
The task-focused recipe: derive a product account, sign, and submit a transaction end to end.
-
Learn Transactions
Take the signer this package produces and submit and track a transaction to finality.
-
External API Reference
The complete
signersurface:SignerManager,SignerAccount, providers, and the error hierarchy.
| Created: September 2, 2026