Product SDK¶
Introduction¶
The Product SDK is the TypeScript SDK for building Polkadot Products. It gives your Product typed access to everything the platform provides: chain reads, transaction signing, decentralized storage, off-chain messaging, smart contracts, and identity, all routed through the Host your Product runs inside.
The SDK never dials an RPC endpoint itself. Every sensitive operation (signing, chain access, storage) goes through the Host, which selects the network, holds the user's keys, and prompts for approval on the user's phone. Your Product calls a typed method; the Host mediates the rest.
Fallible operations in the individual packages return a typed Result instead of throwing, so you check .ok before reading .value. That pattern runs through every capability package and is what each Build guide teaches. The createApp facade below is thinner and does not follow it uniformly — see What createApp Returns.
Two Ways to Use the SDK¶
The SDK ships as one umbrella package that re-exports most capabilities, plus individual per-capability packages you can install on their own:
- Umbrella package:
npm install @parity/product-sdk. One dependency that provides thecreateAppentry point and re-exports most capabilities through subpaths such as@parity/product-sdk/cloud-storage. Convenient when your Product uses several capabilities and bundle size is not a concern. A few packages, notablystatement-store, are not re-exported and are always installed on their own. - Individual packages:
npm install @parity/product-sdk-chain-client @parity/product-sdk-signer(and so on). Install only what you use to keep your bundle smaller and your dependencies explicit.
The import specifiers differ between the two: the umbrella exposes subpaths like @parity/product-sdk/cloud-storage, while the standalone package is @parity/product-sdk-cloud-storage. Switching styles means updating your imports.
The umbrella's subpaths are a fixed set: address, chain, cloud-storage, contracts, core, crypto, host, identity, individuality, local-storage, react, testing, and wallet. Two of those names do not match their leaf package — @parity/product-sdk/chain re-exports chain-client, and @parity/product-sdk/wallet re-exports signer, kept under the older name for compatibility.
Note what is not there: tx, keys, statement-store, terminal, and auth have no umbrella subpath and are not re-exported from the root, so install those from their own packages even when you are otherwise on the umbrella. The root entry point does re-export the most common handful directly — createApp, SignerManager, createChainClient, createLocalKvStore, CloudStorageClient, isInsideContainer, and the Result trio (ok, err, isErrorOf).
A Minimal Product¶
createApp is the fastest way in. It wires the signer, local storage, chain client, and cloud storage behind one object — the signer is exposed as app.wallet, the facade's older name for it:
import { createApp } from '@parity/product-sdk';
async function start() {
const app = await createApp({
name: 'my-product.dot', // also your dotNS identifier — see the warning below
logLevel: 'info',
});
// wallet.connect() throws rather than returning a Result.
try {
const { accounts } = await app.wallet.connect();
if (accounts.length === 0) {
// Connected, but the Host could not derive an account for this name.
} else {
console.log('Connected accounts:', accounts);
}
} catch (cause) {
// No Host, or the Host refused the connection.
}
// Per-Product storage, namespaced by `name`. No Result: a miss reads as null.
await app.localStorage.set('lastVisit', new Date().toISOString());
const lastVisit = await app.localStorage.get('lastVisit'); // string | null
console.log('Last visit:', lastVisit);
return app;
}
name is also your dotNS identifier
createApp passes name straight through as the signer's dappName, and the Host treats that as the product identifier it derives the user's account from, appending .dot to non-local names. If it is not a registered .dot name, the Host rejects the derivation and wallet.connect() resolves with zero accounts instead of failing — so the only symptom is an empty list, with no error to catch. name also namespaces your local storage, so changing it later moves both the derived account and every stored key.
What createApp Returns¶
An App exposing wallet, localStorage, chain, and cloudStorage, plus getAppInfo. The four do not share one error convention, so check which one you are calling before writing the guard:
| Member | Convention |
|---|---|
wallet | Throws. connect() rethrows the signer's error as a plain Error, so the typed variant is lost — you cannot tell HostUnavailableError from a rejection. |
localStorage | Neither. get resolves to string \| null, set to void; a failed read is indistinguishable from a missing key. |
chain | Throws. getClient and getRawClient throw if the chain is not connected. |
cloudStorage | Returns a Result. upload and fetch resolve to ok/err, matching the rest of the SDK. Also null entirely when cloud storage is disabled via cloudStorage: false. |
If you want the Result convention throughout, use the individual packages instead: signer in place of app.wallet, chain-client in place of app.chain, and local-storage in place of app.localStorage. That is the path every Build guide takes.
createApp requires a Host
createApp must run inside a compatible Host (Polkadot Desktop, the Polkadot App, or Polkadot Web). Called outside one, it throws Host storage unavailable. For local development and tests, use the SDK's fake Host; see Testing Without a Host.
The Package Family¶
Each capability is its own package. The umbrella re-exports most of them; a few (such as statement-store) are always installed on their own. Each capability package below has its own overview page in this section covering what it is, when to use it, its core concepts, and typical journeys. The API reference links point to the generated reference for the complete surface.
| Package | What it does | API reference |
|---|---|---|
Chain Client (chain-client) | Typed, host-routed client for reading on-chain storage, constants, and account state across chains | API |
Signer (signer) | Derives product-scoped accounts and requests signatures, routing every approval to the phone | API |
Transactions (tx) | Builds, submits, and follows transactions through to finality | API |
Cloud Storage (cloud-storage) | Uploads and retrieves content-addressed data by CID, backed by the Bulletin Chain | API |
Statement Store (statement-store) | Publish/subscribe client for signed, short-lived statements gossiped off-chain | API |
Local Storage (local-storage) | Per-Product, per-device key-value store backed by the Host | API |
Contracts (contracts) | Typed calls to pallet-revive (PolkaVM) contracts on Asset Hub, resolved from a cdm.json | API |
Keys (keys) | Derives application and session keys from the user's accounts | API |
Individuality (individuality) | Reads personhood standing and usernames on the Individuality chain, and dispatches under a person origin | Source |
Host (host) | Detects the Host container and exposes its lower-level API surface directly | API |
Command-Line Packages¶
Two packages are for tools you run next to a Product — a deploy script, a migration job, a CI step — rather than inside one. A Product runs in a Host that already owns pairing and signing, so it uses Signer instead. Both require Node 21 or later.
| Package | What it does | API reference |
|---|---|---|
Terminal (terminal) | QR-code pairing, session signing, and allowance signers for a Node CLI | API |
Auth (auth) | The runtime-agnostic login, logout, and allocation flow built on terminal | API |
Supporting Packages¶
Lower-level primitives the capability packages build on. Each has its own generated API reference:
address: Encodes, decodes, and converts SS58 and H160 addresses.crypto: Encryption, hashing, and encoding primitives.utils: Byte encoding, 32-byte hashes (blake2b256,sha256,keccak256), planck token formatting, and typed balance queries.logger: Structured, namespace-filtered logging.errorsandresult: The sharedSdkErrormarker and the genericResulttype the whole SDK returns.descriptors: Typed chain metadata consumed by the chain client. Imported per chain (for example,@parity/product-sdk-descriptors/paseo-asset-hub).
result breaks the package-name pattern
Every other package installs as @parity/product-sdk-<name>, but the result type ships as @parity/result, with no product-sdk- prefix. Most Products never install it directly, since the capability packages re-export Result, ok, and err; if you do need it standalone, use the unprefixed name.
The full surface, every package, class, and method, is documented in the Product SDK API reference.
Capabilities That Live on the Host¶
A few things the platform offers are reached through the host package rather than a dedicated capability package, so there is no focused API to learn yet:
- Payments:
getPaymentManager()— request a payment, top up, and track status. - Chat:
getChatManager()— rooms, bots, and interactive action buttons. - Notifications:
getNotificationManager()— push notifications to the user's phone. - Navigation:
navigateTo()— deep links between Products.
These are Host getters that return null outside a container, and their surfaces are still settling. Treat them as lower-level than the rest of the SDK, and check the host API reference for the current shape before building on them.
React Bindings¶
The umbrella exposes a React entry point at @parity/product-sdk/react. Wrap your app in ProductSDKProvider, then reach the SDK from any component through hooks:
useProductSDK: TheAppinstance and connection state.useWallet: The connected account and signing helpers.useLocalStorage: Reactive per-Product key-value storage.useChain: The host-routed chain client.
The Shared Todo App tutorial uses these bindings end to end.
Testing Without a Host¶
Because createApp and the host-only methods require a Host, the SDK ships fakes so automated tests can exercise Product logic in plain Node or a browser test runner. These are a test tool, not a development environment: to develop against a real Host, run your Product from localhost inside Polkadot Desktop, per Set Up Your Project.
@parity/product-sdk/testing exports createFakeApp, which returns a fake App you can use directly in a logic test or hand to ProductSDKContext.Provider for a React component test:
import { createFakeApp } from '@parity/product-sdk/testing';
// Synchronous, unlike the real createApp, which returns a Promise.
const app = createFakeApp({ wallet: { accounts: [alice, bob], selected: alice } });
await app.wallet.connect();
It fakes wallet, localStorage, and cloudStorage. Each is overridable through the options, along with name.
There is no chain fake, and app.chain throws
createFakeApp leaves chain unconfigured, so getClient and getRawClient throw — deliberately, because the Host owns RPC selection and a fake would not exercise the real wiring. The SDK's own guidance is to put chain-reading logic behind an interface you control, unit-test against that, and cover the wiring in end-to-end tests. Pass a chain override to createFakeApp if you would rather supply your own double.
This matters most for Read On-Chain Data, the first Build recipe, which is entirely chain reads.
The subpath also re-exports the per-package fakes for signer, local-storage, contracts, and host — createFakeSignerProvider, createFakeHostLocalStorage, and the rest — so one import covers them.
Statement store fakes are imported separately
They are deliberately not re-exported here, for the same reason statement-store has no umbrella subpath: adding it would pull in a dependency the umbrella does not otherwise have, and could pin a different version than the one your Product installs. Import them from @parity/product-sdk-statement-store/testing instead.
Individual packages also expose a dev path where it makes sense; for example, SignerManager.connect('dev') loads the standard Substrate dev accounts. See Sign and Submit Transactions.
Requirements¶
- Node.js: version 20 or later — except
terminalandauth, which need 21 or later. Those two open a WebSocket through the globalWebSocketthat Node 21 was the first to expose; on Node 18 or 20 they fail at connect time withWebSocket is not defined, not at install time. - Module format: ESM only. The SDK does not ship CommonJS builds.
- TypeScript: version 5.0 or later, if you consume the types.
- Runtime Host: The umbrella package and host-only methods require a compatible Host at runtime. Use the SDK's testing fakes in automated tests.
Where to Go Next¶
-
Guide Build Guides
Task-focused recipes, one per capability, that take you from an empty project to working Product code.
-
External Product SDK API Reference
The complete SDK surface: installation, quickstart, testing, and per-package API docs for every class and method.
-
Learn App Development Reference
How the Product, SDK, Host, and on-chain infrastructure fit together.
| Created: September 2, 2026