@parity/product-sdk-statement-store
Publish/subscribe over the Polkadot Statement Store.
StatementStoreClient submits signed statements and subscribes to topics via
the Host API’s native binary protocol — the SDK runs only inside a host
container (Polkadot Browser / Desktop). ChannelStore layers a channel
abstraction on top for apps that want stable, two-party message streams
instead of raw topics.
npm install @parity/product-sdk-statement-storeExports
Classes
| Name | Summary |
|---|---|
ChannelStore | Higher-level abstraction providing last-write-wins channel semantics |
StatementConnectionError | Failed to connect to the statement store transport. |
StatementDataTooLargeError | The statement data payload exceeds the maximum allowed size. |
StatementEncodingError | A SCALE encoding or decoding operation failed. |
StatementStoreClient | High-level client for the Polkadot Statement Store. |
StatementStoreError | Base class for all statement store errors. |
StatementSubmitError | The statement store node rejected a submitted statement. |
StatementSubscriptionError | Failed to set up or maintain a statement subscription. |
Functions
| Name | Summary |
|---|---|
createChannel() | Create a 32-byte channel hash from a human-readable channel name. |
createTopic() | Create a 32-byte topic hash from a human-readable string. |
createTransport() | Create a statement store transport. |
decodeData() | Decode a JSON-serialized data payload from statement bytes. |
encodeData() | Encode a value as a JSON-serialized data payload for a statement. |
fromHex() | Convert a hex string (with or without 0x prefix) to bytes. |
serializeTopicFilter() | Serialize a TopicFilter into the JSON-RPC format expected by statement store nodes. |
toHex() | Convert bytes to a hex string with 0x prefix. |
topicsEqual() | Compare two topic or channel hashes for byte equality. |
topicToHex() | Convert a topic or channel hash to a hex string (with 0x prefix). |
Interfaces
| Name | Summary |
|---|---|
PublishOptions | Options for publishing a single statement. |
ReceivedStatement | A received statement with typed data and metadata. |
StatementSignerWithKey | An Sr25519 signer with its associated public key. |
StatementStoreConfig | Configuration for StatementStoreClient. |
StatementTransport | Low-level transport interface for statement store communication. |
Unsubscribable | Handle returned by subscription methods. Call unsubscribe() to stop receiving events. |
Type Aliases
| Name | Summary |
|---|---|
ChannelHash | A 32-byte blake2b-256 hash used as a statement channel identifier. |
ConnectionCredentials | Credentials for connecting to the statement store. |
Proof | — |
SdkTopicFilter | Filter for subscribing to statements with different topics. |
SignedStatement | — |
Statement | — |
StatementSigner | A function that signs a message with an Sr25519 key. |
SubmitResult | — |
TopicFilter | Filter for statement subscriptions and queries. |
TopicHash | A 32-byte blake2b-256 hash used as a statement topic. |
UnsignedStatement | — |
Variables
| Name | Summary |
|---|---|
DEFAULT_TTL_SECONDS | Default time-to-live for published statements in seconds. |
MAX_STATEMENT_SIZE | Maximum size of a single statement’s data payload in bytes. |
MAX_USER_TOTAL | Maximum total storage per user in bytes. |
Classes
class ChannelStore
Higher-level abstraction providing last-write-wins channel semantics over the statement store.
Each channel name maps to a single value. When a new value is written to a channel, it replaces the previous one if its timestamp is newer. This is ideal for presence announcements, signaling, and ephemeral state.
Examples
interface Presence {
type: "presence";
peerId: string;
timestamp: number;
}
const channels = new ChannelStore<Presence>(client, { topic2: "doc-123" });
// Write presence
await channels.write("presence/peer-abc", {
type: "presence",
peerId: "abc",
timestamp: Date.now(),
});
// Read all presences
for (const [name, value] of channels.readAll()) {
console.log(`${name}: ${value.peerId}`);
}
// React to changes
channels.onChange((name, value, previous) => {
console.log(`Channel ${name} updated`);
});
channels.destroy();Constructors
constructor
new ChannelStore(client: StatementStoreClient, options?: { topic2?: string }): ChannelStore<T>Parameters
client: The connectedStatementStoreClientto use.options: Optional secondary topic for scoping channels.
Accessors
size
numberGet the number of channels currently tracked.
Methods
destroy
Destroy the channel store and clean up subscriptions.
Does not destroy the underlying client.
destroy(): voidonChange
Subscribe to channel value changes.
The callback is invoked whenever a channel value is updated (either from the network or from a local write).
onChange(callback: (channelName: string, value: T, previous: T | undefined) => void): UnsubscribableParameters
callback: Called with the channel key (hex hash for network-received, hex hash for local writes), new value, and previous value.
Returns
A handle to unsubscribe.
read
Read the latest value for a channel by its human-readable name.
Looks up the channel hash from the name, then retrieves the value. Also checks the hash directly for values received from the network before any local write established the name mapping.
read(channelName: string): T | undefinedParameters
channelName: The channel name (e.g., “presence/peer-abc”).
Returns
The latest value, or undefined if no value has been received.
readAll
Read all channel values as a read-only map.
Keys are hex-encoded channel hashes. Use read for
lookup by human-readable name.
readAll(): ReadonlyMap<string, T>Returns
A map of channel hash to latest value.
write
Write a value to a named channel.
If the value doesn’t include a timestamp, one is added automatically
using Date.now(). The value is published to the statement store
with the channel name as the statement channel.
write(channelName: string, value: T): Promise<boolean>Parameters
channelName: The channel name (e.g., “presence/peer-abc”).value: The value to write.
Returns
true if the statement was accepted by the network.
class StatementConnectionError
Failed to connect to the statement store transport.
Thrown when the WebSocket connection cannot be established or the chain-client’s bulletin chain is not connected.
Extends: StatementStoreError
Constructors
constructor
new StatementConnectionError(message: string, options?: ErrorOptions): StatementConnectionErrorclass StatementDataTooLargeError
The statement data payload exceeds the maximum allowed size.
The statement store protocol limits individual statement data
to MAX_STATEMENT_SIZE bytes (512 bytes).
Extends: StatementStoreError
Constructors
constructor
new StatementDataTooLargeError(actualSize: number, maxSize: number = MAX_STATEMENT_SIZE): StatementDataTooLargeErrorProperties
actualSize
numberThe actual size of the data in bytes.
maxSize
numberThe maximum allowed size in bytes.
class StatementEncodingError
A SCALE encoding or decoding operation failed.
Thrown when statement bytes cannot be parsed (corrupt data, unknown field tags) or when encoding produces invalid output.
Extends: StatementStoreError
Constructors
constructor
new StatementEncodingError(message: string, options?: ErrorOptions): StatementEncodingErrorclass StatementStoreClient
High-level client for the Polkadot Statement Store.
Provides a simple publish/subscribe API over the ephemeral statement store, handling topic management and signing through the host API.
The SDK is designed to run exclusively inside a host container.
Examples
import { StatementStoreClient } from "@parity/product-sdk-statement-store";
// Inside a container (host mode)
const client = new StatementStoreClient({ appName: "my-app" });
await client.connect({ mode: "host", accountId: ["5Grw...", 42] });
// Publish
await client.publish({ type: "presence", peerId: "abc" }, {
channel: "presence/abc",
topic2: "room-123",
});
// Subscribe
client.subscribe<{ type: string }>(statement => {
console.log(statement.data.type);
});
// Cleanup
client.destroy();Constructors
constructor
new StatementStoreClient(config: StatementStoreConfig): StatementStoreClientMethods
connect
Connect to the statement store and start receiving statements.
Overload 1
connect(credentials: ConnectionCredentials): Promise<void>Connect to the statement store and start receiving statements.
Parameters
credentials: Connection credentials (host accountId or local signer).
Throws
- If the transport cannot be established.
Overload 2
connect(signer: StatementSignerWithKey): Promise<void>destroy
Destroy the client, unsubscribing and closing the transport.
Safe to call multiple times. After destruction, the client cannot be reused.
destroy(): voidgetPublicKeyHex
Get the signer’s public key as a hex string (with 0x prefix).
getPublicKeyHex(): stringReturns
The hex-encoded public key, or empty string if not connected or in host mode.
isConnected
Whether the client is connected and ready to publish/subscribe.
isConnected(): booleanpublish
Publish typed data to the statement store.
publish(data: T, options?: PublishOptions): Promise<boolean>Parameters
data: The value to publish (must be JSON-serializable, max 512 bytes).options: Optional channel, topic2, TTL, and decryption key overrides.
Returns
true if accepted, false if rejected or errored.
Throws
- If not connected.
- If the encoded data exceeds 512 bytes.
subscribe
Subscribe to incoming statements on this application’s topic.
subscribe(callback: (statement: ReceivedStatement<T>) => void, options?: { topic2?: string }): UnsubscribableParameters
callback: Called for each new statement.options: Optional secondary topic filter.
Returns
A handle to unsubscribe.
class StatementStoreError
Base class for all statement store errors.
Use instanceof StatementStoreError to catch any error originating
from the statement store package.
Extends: Error
Constructors
constructor
new StatementStoreError(message: string, options?: ErrorOptions): StatementStoreErrorclass StatementSubmitError
The statement store node rejected a submitted statement.
Carries the raw RPC response detail for programmatic inspection.
Extends: StatementStoreError
Constructors
constructor
new StatementSubmitError(detail: unknown): StatementSubmitErrorProperties
detail
unknownThe raw response from the RPC call.
class StatementSubscriptionError
Failed to set up or maintain a statement subscription.
This is a non-fatal error — the client falls back to polling when subscriptions are unavailable.
Extends: StatementStoreError
Constructors
constructor
new StatementSubscriptionError(message: string, options?: ErrorOptions): StatementSubscriptionErrorFunctions
createChannel()
Create a 32-byte channel hash from a human-readable channel name.
Channels enable last-write-wins semantics: for a given channel, only the most recent statement (by timestamp) is retained.
createChannel(name: string): ChannelHashParameters
name: A human-readable channel name (e.g., “presence/peer-abc”).
Returns
A branded 32-byte channel hash.
Examples
const channel = createChannel("presence/peer-abc123");createTopic()
Create a 32-byte topic hash from a human-readable string.
Uses blake2b-256 to hash the UTF-8 encoded string into a deterministic 32-byte topic identifier. Statements are tagged with topics so subscribers can filter efficiently on the network.
createTopic(name: string): TopicHashParameters
name: A human-readable topic name (e.g., “ss-webrtc”, “my-app”).
Returns
A branded 32-byte topic hash.
Examples
const topic = createTopic("ss-webrtc");
// Use in subscriptions and publish optionscreateTransport()
Create a statement store transport.
Uses the Host API via @parity/product-sdk-host — the container’s native
statement store protocol (binary, not JSON-RPC). This is the only supported path.
createTransport(): Promise<StatementTransport>Throws
- If the host statement store is unavailable.
decodeData()
Decode a JSON-serialized data payload from statement bytes.
decodeData(bytes: Uint8Array): TParameters
bytes: UTF-8 encoded JSON bytes.
Returns
The parsed value.
Throws
- If the bytes are not valid UTF-8 or valid JSON.
encodeData()
Encode a value as a JSON-serialized data payload for a statement.
Serializes the value as JSON and encodes to UTF-8 bytes.
Throws StatementDataTooLargeError if the result exceeds
MAX_STATEMENT_SIZE bytes.
encodeData(value: T): Uint8ArrayParameters
value: The value to serialize.
Returns
UTF-8 encoded JSON bytes.
Throws
- If the encoded data exceeds 512 bytes.
fromHex()
Convert a hex string (with or without 0x prefix) to bytes.
fromHex(hex: string): Uint8ArrayserializeTopicFilter()
Serialize a TopicFilter into the JSON-RPC format expected by statement store nodes.
"any"is passed through as the string"any".{ matchAll: [...] }becomes{ matchAll: ["0x...", ...] }with hex-encoded topics.{ matchAny: [...] }becomes{ matchAny: ["0x...", ...] }with hex-encoded topics.
serializeTopicFilter(filter: TopicFilter): TopicFilterParameters
filter: The topic filter to serialize.
Returns
A JSON-RPC compatible filter value.
toHex()
Convert bytes to a hex string with 0x prefix.
toHex(bytes: Uint8Array): stringtopicsEqual()
Compare two topic or channel hashes for byte equality.
topicsEqual(a: Uint8Array, b: Uint8Array): booleanParameters
a: First hash.b: Second hash.
Returns
True if the hashes are identical.
topicToHex()
Convert a topic or channel hash to a hex string (with 0x prefix).
topicToHex(hash: Uint8Array): stringParameters
hash: A 32-byte topic or channel hash.
Returns
Hex string with “0x” prefix.
Interfaces
interface PublishOptions
Options for publishing a single statement.
Properties
channel
stringChannel name for last-write-wins semantics.
When provided, the statement is tagged with blake2b(channel).
For a given channel, only the most recent statement is kept.
decryptionKey
Uint8Array<ArrayBufferLike>Decryption key hint (32 bytes).
Used by the statement_posted RPC method to filter statements.
Typically set to the blake2b hash of the room or document ID.
topic2
stringSecondary topic for additional filtering.
Hashed with blake2b and set as topic2. Useful for scoping statements to a specific room, document, or context.
ttlSeconds
numberTime-to-live in seconds. Overrides StatementStoreConfig.defaultTtlSeconds.
interface ReceivedStatement
A received statement with typed data and metadata.
Properties
channelHex
stringChannel hex, if present.
data
TParsed data payload.
expiry
bigintCombined expiry value (upper 32 bits = timestamp, lower 32 bits = sequence).
raw
StatementThe full statement from the transport for advanced inspection.
signerHex
stringSigner’s public key hex (from proof), if present.
topics
string[]Topics array (hex strings).
interface StatementSignerWithKey
An Sr25519 signer with its associated public key.
Used by StatementStoreClient.connect to sign published statements
when running outside a container (local mode).
Properties
publicKey
Uint8Array32-byte Sr25519 public key.
sign
StatementSignerSigning function.
interface StatementStoreConfig
Configuration for StatementStoreClient.
The client uses the Host API’s native statement store protocol. The SDK is designed to run exclusively inside a host container.
Properties
appName
stringApplication namespace used as the primary topic (topic1).
All statements published by this client are tagged with blake2b(appName).
Subscribers filter on this topic to receive only relevant statements.
defaultTtlSeconds
numberDefault time-to-live for published statements in seconds.
Statements automatically expire after this duration.
Can be overridden per-publish via PublishOptions.ttlSeconds.
transport
StatementTransportProvide a custom transport instead of auto-detection.
When set, the client uses this transport directly. Useful for testing or advanced BYOD setups.
interface StatementTransport
Low-level transport interface for statement store communication.
Uses HostTransport — the Host API’s native binary protocol.
Most consumers should use StatementStoreClient instead of this interface directly.
Methods
destroy
Destroy the transport and release all resources.
destroy(): voidquery
Query existing statements from the store.
Note: The host API subscription replays initial state, so this may not be needed.
query(filter: TopicFilter): Promise<Partial<{ channel: SizedHex<32>; data: Uint8Array; decryptionKey: SizedHex<32>; expiry: bigint; proof: Proof; topics: SizedHex<32>[] }>[]>signAndSubmit
Sign and submit a statement.
- Host mode: delegates to the host API’s
createProofthensubmit. - Local mode: signs locally using
getStatementSignerfrom sdk-statement, then submits.
signAndSubmit(statement: Statement, credentials: ConnectionCredentials): Promise<void>Parameters
statement: The unsigned statement to sign and submit.credentials: Signing credentials (host accountId or local signer).
subscribe
Subscribe to statements matching a topic filter.
subscribe(filter: TopicFilter, onStatements: (statements: Partial<{ channel: SizedHex<32>; data: Uint8Array; decryptionKey: SizedHex<32>; expiry: bigint; proof: Proof; topics: SizedHex<32>[] }>[]) => void, onError: (error: Error) => void): UnsubscribableParameters
filter: sdk-statement topic filter.onStatements: Called with batches of received statements.onError: Called when the subscription encounters an error.
Returns
A handle to unsubscribe.
interface Unsubscribable
Handle returned by subscription methods. Call unsubscribe() to stop receiving events.
Properties
unsubscribe
() => voidType Aliases
type ChannelHash
A 32-byte blake2b-256 hash used as a statement channel identifier.
Channels enable last-write-wins semantics: for a given channel,
only the most recent statement (by timestamp) is kept.
Create one with createChannel.
type ChannelHash = Uint8Array & { readonly __brand: "ChannelHash" }type ConnectionCredentials
Credentials for connecting to the statement store.
- Host mode: Inside a container, proof creation is delegated to the host API.
The
accountIdis a[ss58Address, chainPrefix]tuple from product-sdk. - Local mode: Outside a container, statements are signed locally using the provided Sr25519 signer.
type ConnectionCredentials = { accountId: [string, number]; mode: "host" } | { mode: "local"; signer: StatementSignerWithKey }type Proof
type Proof = Enum<{ ecdsa: { signature: SizedHex<65>; signer: SizedHex<33> }; ed25519: { signature: SizedHex<64>; signer: SizedHex<32> }; onChain: { blockHash: SizedHex<32>; event: bigint; who: SizedHex<32> }; sr25519: { signature: SizedHex<64>; signer: SizedHex<32> } }>type SdkTopicFilter
Filter for subscribing to statements with different topics.
type SdkTopicFilter = "any" | { matchAll: SizedHex<32>[] } | { matchAny: SizedHex<32>[] }type SignedStatement
type SignedStatement = UnsignedStatement & { proof: Proof }type Statement
type Statement = Partial<{ channel: SizedHex<32>; data: Uint8Array; decryptionKey: SizedHex<32>; expiry: bigint; proof: Proof; topics: SizedHex<32>[] }>type StatementSigner
A function that signs a message with an Sr25519 key.
Takes the signature material bytes and returns a 64-byte Sr25519 signature. May be synchronous or asynchronous (e.g., when signing via a hardware wallet).
type StatementSigner = (message: Uint8Array) => Uint8Array | Promise<Uint8Array>type SubmitResult
type SubmitResult = SubmitNew | SubmitKnown | SubmitKnownExpired | SubmitRejected | SubmitInvalid | SubmitInternalErrortype TopicFilter
Filter for statement subscriptions and queries.
"any"— matches all statements (no filtering).{ matchAll: [...] }— matches statements that have all listed topics.{ matchAny: [...] }— matches statements that have any listed topic.
type TopicFilter = "any" | { matchAll: TopicHash[] } | { matchAny: TopicHash[] }type TopicHash
A 32-byte blake2b-256 hash used as a statement topic.
Topics allow subscribers to filter statements efficiently on the network.
Create one with createTopic.
type TopicHash = Uint8Array & { readonly __brand: "TopicHash" }type UnsignedStatement
type UnsignedStatement = Omit<Statement, "proof">Variables
DEFAULT_TTL_SECONDS
Default time-to-live for published statements in seconds.
let DEFAULT_TTL_SECONDS: 30 = 30MAX_STATEMENT_SIZE
Maximum size of a single statement’s data payload in bytes.
let MAX_STATEMENT_SIZE: 512 = 512MAX_USER_TOTAL
Maximum total storage per user in bytes.
let MAX_USER_TOTAL: 1024 = 1024