From c02a0f85c864f0b24d8687e75d2ce34dbcaab363 Mon Sep 17 00:00:00 2001 From: Victor Oliva Date: Fri, 20 Mar 2026 10:34:49 +0100 Subject: [PATCH 1/5] feat: subscription based statement store --- packages/sdk-statement/CHANGELOG.md | 10 + packages/sdk-statement/package.json | 5 +- packages/sdk-statement/src/api.ts | 72 +++-- .../sdk-statement/src/codec/codec.spec.ts | 35 ++- packages/sdk-statement/src/codec/codec.ts | 38 ++- packages/sdk-statement/src/index.ts | 2 +- packages/sdk-statement/src/statement-sdk.ts | 129 ++++++--- packages/sdk-statement/src/types.ts | 73 ++++- packages/sdk-statement/src/utils.ts | 19 +- pnpm-lock.yaml | 270 ++++++++++-------- 10 files changed, 420 insertions(+), 233 deletions(-) diff --git a/packages/sdk-statement/CHANGELOG.md b/packages/sdk-statement/CHANGELOG.md index 19dbe87..bb97b78 100644 --- a/packages/sdk-statement/CHANGELOG.md +++ b/packages/sdk-statement/CHANGELOG.md @@ -2,6 +2,16 @@ ## Unreleased +### Changed + +- Change API to new subscription-based statement store + - `createStatementSdk` takes an endpoint URL rather than a `req` function. + - Update `Statement` to new spec: replaces `priority` for `expiry`. + - Removes `dump()` + - `getStatements` requires the `topics` parameter + - Adds `getBroadcasts` and `getPosted` for filtering based on the `dest` parameter + - Adds `subscribeStatements(topics)` for subscribing + ## 0.4.1 2026-03-16 ### Fixed diff --git a/packages/sdk-statement/package.json b/packages/sdk-statement/package.json index f0a4325..2589343 100644 --- a/packages/sdk-statement/package.json +++ b/packages/sdk-statement/package.json @@ -42,7 +42,10 @@ "license": "MIT", "dependencies": { "@polkadot-api/substrate-bindings": "0.19.0", + "@polkadot-api/substrate-client": "^0.6.0", "@polkadot-api/utils": "^0.3.0", - "polkadot-api": "^2.0.0-rc.5" + "@polkadot-api/ws-provider": "^0.8.1", + "polkadot-api": "^2.0.0-rc.5", + "rxjs": "^7.8.2" } } diff --git a/packages/sdk-statement/src/api.ts b/packages/sdk-statement/src/api.ts index e8d9823..f99cc6e 100644 --- a/packages/sdk-statement/src/api.ts +++ b/packages/sdk-statement/src/api.ts @@ -1,26 +1,58 @@ import { HexString } from "@polkadot-api/substrate-bindings" -import { fromHex } from "@polkadot-api/utils" -import { SubmitResult } from "./types" +import { createClient, UnsubscribeFn } from "@polkadot-api/substrate-client" +import { getWsProvider } from "@polkadot-api/ws-provider" +import { Observable } from "rxjs" +import { StatementEvent, SubmitResult, TopicFilter } from "./types" -export type RequestFn = = any[]>( - method: string, - params: Params, -) => Promise +export const getApi = (endpoint: string) => { + const client = createClient(getWsProvider(endpoint)) -export const getApi = (req: RequestFn) => ({ - submit: (stmt: HexString) => - req("statement_submit", [stmt]), + const subscribe = ( + method: string, + unsubscribeMethod: string, + params: any[], + ) => + new Observable((obs) => { + let unsubInner: UnsubscribeFn | null = null + let subId: string | null = null - dump: () => req("statement_dump", []), + const sendUnsubscribe = () => { + // Fire-and-forget + subId != null && client.request(unsubscribeMethod, [subId]) + } - broadcasts: (matchAllTopics: HexString[]) => - req("statement_broadcastsStatement", [ - matchAllTopics.map((v) => [...fromHex(v)]), - ]), + client._request(method, params, { + onSuccess: (res: string, follow) => { + subId = res + if (obs.closed) { + sendUnsubscribe() + return + } + unsubInner = follow(subId, { + next: (data: any) => obs.next(data), + error: (e) => obs.error(e), + }) + }, + onError(e) { + obs.error(e) + }, + }) - posted: (matchAllTopics: HexString[], dest: HexString) => - req("statement_postedStatement", [ - matchAllTopics.map((v) => [...fromHex(v)]), - [...fromHex(dest)], - ]), -}) + return () => { + sendUnsubscribe() + unsubInner?.() + } + }) + + return { + submit: (stmt: HexString) => + client.request("statement_submit", [stmt]), + + subscribeStatement: (topicFilter: TopicFilter) => + subscribe( + "statement_subscribeStatement", + "statement_unsubscribeStatement", + [topicFilter], + ), + } +} diff --git a/packages/sdk-statement/src/codec/codec.spec.ts b/packages/sdk-statement/src/codec/codec.spec.ts index afa37e1..83ef435 100644 --- a/packages/sdk-statement/src/codec/codec.spec.ts +++ b/packages/sdk-statement/src/codec/codec.spec.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest" -import { statementCodec } from "./codec" -import { fromHex } from "@polkadot-api/utils" +import { Statement, statementCodec } from "./codec" +import { fromHex, toHex } from "@polkadot-api/utils" describe("statement codec", () => { it("encodes and decodes empty statement", () => { @@ -13,15 +13,15 @@ describe("statement codec", () => { it("throws on repeated and/or unordered keys", () => { expect(() => { - // [{ priority: 1 }, { priority: 3 }] - statementCodec.dec(fromHex("0x0802010000000203000000")) + // [{ expiry: 1 }, { expiry: 3 }] + statementCodec.dec(fromHex("0x0802010000000000000002030000000000000000")) }).toThrow("entries order") expect(() => { statementCodec.dec( fromHex( - // [{ channel: [0; 32] }, { priority: 1 }] - "0x080300000000000000000000000000000000000000000000000000000000000000000201000000", + // [{ channel: [0; 32] }, { expiry: 1 }] + "0x0803000000000000000000000000000000000000000000000000000000000000000002010000000000000000", ), ) }).toThrow("entries order") @@ -37,4 +37,27 @@ describe("statement codec", () => { ) }).toThrow("Unexpected topic") }) + + it("encodes and decodes statement with expiry", () => { + const expiry: Statement["expiry"] = { + sequence: 0x12345678, + timestampSecs: 0x76543231, + } + const stmt: Statement = { expiry } + const encoded = statementCodec.enc(stmt) + const decoded = statementCodec.dec(encoded) + expect(decoded.expiry).toEqual(expiry) + }) + + it("encodes expiry as u64 little-endian", () => { + const stmt: Statement = { + expiry: { + sequence: 1, + timestampSecs: 0, + }, + } + const encoded = statementCodec.enc(stmt) + // [compact length=1 (0x04)][variant=0x02][u64 LE = 01 00 00 00 00 00 00 00] + expect(toHex(encoded)).toBe("0x04020100000000000000") + }) }) diff --git a/packages/sdk-statement/src/codec/codec.ts b/packages/sdk-statement/src/codec/codec.ts index fade1a4..1a41c38 100644 --- a/packages/sdk-statement/src/codec/codec.ts +++ b/packages/sdk-statement/src/codec/codec.ts @@ -7,7 +7,6 @@ import { SizedBytes, SizedHex, Struct, - u32, u64, Variant, Vector, @@ -26,8 +25,15 @@ export type Proof = Enum<{ export type Statement = Partial<{ proof: Proof + /** + * @deprecated Experimental feature, may be removed/changed in future + * releases. + */ decryptionKey: SizedHex<32> - priority: number + expiry: { + timestampSecs: number + sequence: number + } channel: SizedHex<32> topics: Array> data: Uint8Array @@ -36,7 +42,7 @@ export type Statement = Partial<{ const sortIdxs = { proof: 0, decryptionKey: 1, - priority: 2, + expiry: 2, channel: 3, topics: 4, topic1: 4, @@ -60,7 +66,7 @@ const field = Variant({ onChain: Struct({ who: bin32, blockHash: bin32, event: u64 }), }), decryptionKey: bin32, - priority: u32, + expiry: u64, channel: bin32, topic1: bin32, topic2: bin32, @@ -88,6 +94,13 @@ export const statementCodec = enhanceCodec< stmt[k]!.forEach((v, i) => { statement.push(Enum(`topic${i + 1}` as `topic${1 | 2 | 3 | 4}`, v)) }) + } else if (k === "expiry") { + statement.push( + Enum( + "expiry", + createExpiry(stmt.expiry!.timestampSecs, stmt.expiry!.sequence), + ), + ) } else { statement.push(Enum(k, stmt[k]!)) } @@ -104,7 +117,9 @@ export const statementCodec = enhanceCodec< if (idx <= maxIdx) throw new Error("Unexpected entries order") maxIdx = idx - if (!v.type.startsWith("topic")) { + if (v.type === "expiry") { + statement.expiry = parseExpiry(v.value) + } else if (!v.type.startsWith("topic")) { ;(statement as any)[v.type] = v.value } else if (v.type !== `topic${++maxTopicChecked}`) { throw new Error(`Unexpected ${v.type}`) @@ -116,3 +131,16 @@ export const statementCodec = enhanceCodec< return statement }, ) + +const MAX_SEQ_NUMBER = 0xffffffff +const createExpiry = (timestamp: number, sequenceNumber: number = 0) => { + if (sequenceNumber < 0 || sequenceNumber > MAX_SEQ_NUMBER) { + throw new RangeError(`sequenceNumber must be 0-${MAX_SEQ_NUMBER}`) + } + return (BigInt(timestamp) << 32n) | BigInt(sequenceNumber) +} + +const parseExpiry = (expiry: bigint) => ({ + timestampSecs: Number(expiry >> 32n), + sequence: Number(expiry & 0xffffffffn), +}) diff --git a/packages/sdk-statement/src/index.ts b/packages/sdk-statement/src/index.ts index 7348fc2..2be3c1a 100644 --- a/packages/sdk-statement/src/index.ts +++ b/packages/sdk-statement/src/index.ts @@ -1,5 +1,5 @@ export * from "./codec" export * from "./statement-sdk" export * from "./signer" -export type { SubmitResult } from "./types" +export type { SubmitResult, TopicFilter, StatementEvent } from "./types" export { stringToTopic } from "./utils" diff --git a/packages/sdk-statement/src/statement-sdk.ts b/packages/sdk-statement/src/statement-sdk.ts index 6e673c3..18984cd 100644 --- a/packages/sdk-statement/src/statement-sdk.ts +++ b/packages/sdk-statement/src/statement-sdk.ts @@ -1,64 +1,103 @@ -import { SizedHex, HexString } from "@polkadot-api/substrate-bindings" -import { Statement, statementCodec } from "./codec" +import { SizedHex } from "@polkadot-api/substrate-bindings" import { toHex } from "@polkadot-api/utils" -import { getApi, RequestFn } from "./api" -import { filterDecKey, filterTopics } from "./utils" -import { SubmitResult } from "./types" +import { + firstValueFrom, + map, + mergeAll, + Observable, + filter as rxjsFilter, +} from "rxjs" +import { getApi } from "./api" +import { Statement, statementCodec } from "./codec" +import { SubmitResult, TopicFilter } from "./types" + +const ANY_FILTER: TopicFilter = "any" /** * Create statement sdk. * - * @param {RequestFn} req Takes a req-res function, which accepts Statement RPC - * calls. This can be `client._request` (from - * `polkadot-api`) - * client, `client.request` (from - * `@polkadot-api/substrate-client`) - * or any other crafted by the consumer. + * @param {string} endpoint WebSocket endpoint to connect to, starting either + * with `ws://` or `wss://` */ -export const createStatementSdk = (req: RequestFn) => { - const api = getApi(req) +export const createStatementSdk = (endpoint: string) => { + const api = getApi(endpoint) + + const getStatements$ = (filter: TopicFilter): Observable => + api.subscribeStatement(filter).pipe( + map((evt) => { + if (evt.event === "newStatements") { + return evt.data.statements.map(statementCodec.dec) + } + return null + }), + rxjsFilter((v) => v !== null), + ) + + /** + * Get statements from store matching the given filter. + * + * This method subscribes to the statement store, collects all existing + * statements matching the filter, then unsubscribes and returns them. + * + * @param filter Topic filter for statements. Defaults to matching all. + */ + const getStatements = ( + filter: TopicFilter = ANY_FILTER, + ): Promise => firstValueFrom(getStatements$(filter)) + return { /** * Submit a Statement to the store. * It must be signed to be accepted. */ submit: (stmt: Statement): Promise => - api - .submit(toHex(statementCodec.enc(stmt))) - // TODO: remove in due time - // prior to https://github.com/paritytech/polkadot-sdk/pull/10421 - // everything that was not `"new"` yielded an error, and `"new"` returned undefined - // catching the errors is not an option since we can't feasibly know what is the actual error - .then((v) => v ?? { status: "new" }), + api.submit(toHex(statementCodec.enc(stmt))), + + getStatements, /** - * Get statements from store. - * dest: `Binary` means to get all statements with that specific - * `decryptionKey` set. - * `null` means to get all statements with no `decryptionKey` set. - * `undefined` (or unset) means to get all statements disregarding - * `decryptionKey`. + * Subscribe to statements matching the given filter. + * + * Unlike `getStatements`, this maintains an active subscription and + * continues to receive new statements as they are added to the store. + * + * @param filter Topic filter for statements. + * @param onStatement Callback for each decoded statement. + * @param onError Callback for errors. + * @returns Unsubscribe function. */ - getStatements: async ({ - dest, - topics, - }: Partial<{ - topics: Array> - dest: SizedHex<32> | null - }> = {}): Promise => { - if (dest === null) - return (await api.broadcasts(topics ?? [])).map(statementCodec.dec) - if (topics && dest) - return (await api.posted(topics, dest)).map(statementCodec.dec) - return (await api.dump()) - .map(statementCodec.dec) - .filter(filterDecKey(dest)) - .filter(filterTopics(topics)) + subscribeStatements: (filter: TopicFilter): Observable => + getStatements$(filter).pipe(mergeAll()), + + /** + * Get broadcasts (statements with no decryptionKey) matching topics. + * + * @param topics Topics to match (all must be present). + */ + getBroadcasts: async ( + topics: Array> = [], + ): Promise => { + const filter: TopicFilter = + topics.length > 0 ? { matchAll: topics } : ANY_FILTER + const statements = await getStatements(filter) + return statements.filter((stmt) => stmt.decryptionKey === undefined) }, - dump: (): Promise => - req("statement_dump", []).then((res) => - res.map(statementCodec.dec), - ), + /** + * Get posted statements (with decryptionKey) matching topics and + * destination. + * + * @param topics Topics to match (all must be present). + * @param dest Destination decryption key. + */ + getPosted: async ( + topics: Array>, + dest: SizedHex<32>, + ): Promise => { + const filter: TopicFilter = + topics.length > 0 ? { matchAll: topics } : ANY_FILTER + const statements = await getStatements(filter) + return statements.filter((stmt) => stmt.decryptionKey === dest) + }, } } diff --git a/packages/sdk-statement/src/types.ts b/packages/sdk-statement/src/types.ts index fed5cc6..79e0635 100644 --- a/packages/sdk-statement/src/types.ts +++ b/packages/sdk-statement/src/types.ts @@ -1,3 +1,24 @@ +import { HexString, SizedHex } from "@polkadot-api/substrate-bindings" + +/** + * Filter for subscribing to statements with different topics. + */ +export type TopicFilter = + | "any" + | { matchAll: Array> } + | { matchAny: Array> } + +/** + * An item returned by the statement subscription stream. + */ +export type StatementEvent = { + event: "newStatements" + data: { + statements: HexString[] + remaining?: number + } +} + type SubmitNew = { /** * Statement was accepted as new. @@ -10,9 +31,15 @@ type SubmitKnown = { */ status: "known" } +type SubmitKnownExpired = { + /** + * Statement was already known but has expired. + */ + status: "knownExpired" +} type SubmitRejected = { /** - * Statement was rejected because the store is full or priority is too low. + * Statement was rejected because the store is full or expiry is too low. */ status: "rejected" } & ( @@ -32,32 +59,32 @@ type SubmitRejected = { } | { /** - * Attempting to replace a channel message with lower or equal priority. + * Attempting to replace a channel message with lower or equal expiry. */ reason: "channelPriorityTooLow" /** - * The priority of the submitted statement. + * The expiry of the submitted statement. */ - submitted_priority: number + submitted_expiry: bigint /** - * The minimum priority of the existing channel message. + * The minimum expiry of the existing channel message. */ - min_priority: number + min_expiry: bigint } | { /** - * Account reached its statement limit and submitted priority is too low + * Account reached its statement limit and submitted expiry is too low * to evict existing. */ reason: "accountFull" /** - * The priority of the submitted statement. + * The expiry of the submitted statement. */ - submitted_priority: number + submitted_expiry: bigint /** - * The minimum priority of the existing statement. + * The minimum expiry of the existing statement. */ - min_priority: number + min_expiry: bigint } | { /** @@ -65,6 +92,12 @@ type SubmitRejected = { */ reason: "storeFull" } + | { + /** + * Account has no allowance set. + */ + reason: "noAllowance" + } ) type SubmitInvalid = { /** @@ -98,10 +131,28 @@ type SubmitInvalid = { */ max_size: number } + | { + /** + * Statement has already expired. The expiry field is in the past. + */ + reason: "alreadyExpired" + } ) +type SubmitInternalError = { + /** + * Internal store error. + */ + status: "internalError" + /** + * Error message. + */ + error: string +} export type SubmitResult = | SubmitNew | SubmitKnown + | SubmitKnownExpired | SubmitRejected | SubmitInvalid + | SubmitInternalError diff --git a/packages/sdk-statement/src/utils.ts b/packages/sdk-statement/src/utils.ts index 7f92345..a062b68 100644 --- a/packages/sdk-statement/src/utils.ts +++ b/packages/sdk-statement/src/utils.ts @@ -1,21 +1,6 @@ import { Binary, Blake2256, SizedHex } from "@polkadot-api/substrate-bindings" -import { Statement } from "./codec" export const stringToTopic = (str: string): SizedHex<32> => { - const enc = Binary.fromText(str) // Returns Uint8Array directly - return Binary.toHex(Blake2256(enc)) as SizedHex<32> // SizedHex is now SizedHex (string) -} - -export const filterDecKey = (key?: SizedHex<32>) => { - if (!key) return () => true - const hexKey = key // Already a hex string (SizedHex) - return (v: Statement) => v.decryptionKey === hexKey -} - -export const filterTopics = (topics?: Array>) => { - if (!topics) return () => true - const hexTopics = topics // Already hex strings (SizedHex) - return (v: Statement) => - (v.topics?.length ?? 0) >= hexTopics.length && - hexTopics.every((top, idx) => top === v.topics![idx]) + const enc = Binary.fromText(str) + return Binary.toHex(Blake2256(enc)) } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6458c38..aabaecc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -34,10 +34,10 @@ importers: version: 5.9.3 vite-tsconfig-paths: specifier: ^6.1.1 - version: 6.1.1(typescript@5.9.3)(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)) + version: 6.1.1(typescript@5.9.3)(vite@8.0.1(@types/node@25.5.0)(esbuild@0.27.3)) vitest: specifier: ^4.1.0 - version: 4.1.0(@types/node@25.5.0)(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)) + version: 4.1.0(@types/node@25.5.0)(vite@8.0.1(@types/node@25.5.0)(esbuild@0.27.3)) examples/governance: dependencies: @@ -59,7 +59,7 @@ importers: devDependencies: '@types/bun': specifier: ^1.3.10 - version: 1.3.10 + version: 1.3.11 examples/ink-playground: dependencies: @@ -87,7 +87,7 @@ importers: devDependencies: '@types/bun': specifier: ^1.3.10 - version: 1.3.10 + version: 1.3.11 examples/remote-proxy: dependencies: @@ -115,7 +115,7 @@ importers: version: 5.9.3 vite: specifier: ^8.0.0 - version: 8.0.0(@types/node@25.5.0)(esbuild@0.27.3) + version: 8.0.1(@types/node@25.5.0)(esbuild@0.27.3) examples/staking: dependencies: @@ -137,7 +137,7 @@ importers: devDependencies: '@types/bun': specifier: ^1.3.10 - version: 1.3.10 + version: 1.3.11 examples/statement-playground: dependencies: @@ -162,7 +162,7 @@ importers: devDependencies: '@types/bun': specifier: latest - version: 1.3.10 + version: 1.3.9 packages/common-utils: devDependencies: @@ -233,7 +233,7 @@ importers: version: 2.0.0-rc.5(postcss@8.5.8)(rxjs@7.8.2) viem: specifier: ^2.47.4 - version: 2.47.4(typescript@5.9.3) + version: 2.47.5(typescript@5.9.3) devDependencies: '@polkadot-api/ink-contracts': specifier: ^0.5.1 @@ -295,12 +295,21 @@ importers: '@polkadot-api/substrate-bindings': specifier: 0.19.0 version: 0.19.0 + '@polkadot-api/substrate-client': + specifier: ^0.6.0 + version: 0.6.0 '@polkadot-api/utils': specifier: ^0.3.0 version: 0.3.0 + '@polkadot-api/ws-provider': + specifier: ^0.8.1 + version: 0.8.1(rxjs@7.8.2) polkadot-api: specifier: ^2.0.0-rc.5 version: 2.0.0-rc.5(postcss@8.5.8)(rxjs@7.8.2) + rxjs: + specifier: ^7.8.2 + version: 7.8.2 packages: @@ -324,11 +333,11 @@ packages: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} - '@emnapi/core@1.9.0': - resolution: {integrity: sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==} + '@emnapi/core@1.9.1': + resolution: {integrity: sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==} - '@emnapi/runtime@1.9.0': - resolution: {integrity: sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==} + '@emnapi/runtime@1.9.1': + resolution: {integrity: sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==} '@emnapi/wasi-threads@1.2.0': resolution: {integrity: sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==} @@ -698,12 +707,8 @@ packages: resolution: {integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==} engines: {node: '>= 20.19.0'} - '@oxc-project/runtime@0.115.0': - resolution: {integrity: sha512-Rg8Wlt5dCbXhQnsXPrkOjL1DTSvXLgb2R/KYfnf1/K+R0k6UMLEmbQXPM+kwrWqSmWA2t0B1EtHy2/3zikQpvQ==} - engines: {node: ^20.19.0 || >=22.12.0} - - '@oxc-project/types@0.115.0': - resolution: {integrity: sha512-4n91DKnebUS4yjUHl2g3/b2T+IUdCfmoZGhmwsovZCDaJSs+QkVAM+0AqqTxHSsHfeiMuueT75cZaZcT/m0pSw==} + '@oxc-project/types@0.120.0': + resolution: {integrity: sha512-k1YNu55DuvAip/MGE1FTsIuU3FUCn6v/ujG9V7Nq5Df/kX2CWb13hhwD0lmJGMGqE+bE1MXvv9SZVnMzEXlWcg==} '@polkadot-api/cli@0.19.4': resolution: {integrity: sha512-i8eECLhVYanfK+CWXE6/h5gK57inBNzitn/jiMYirkrJLtj4E7V+g9PoOlAa8VqNivgM3kuPK2w30wyUuE3DOA==} @@ -831,103 +836,103 @@ packages: react: '>=16.8.0' rxjs: '>=6' - '@rolldown/binding-android-arm64@1.0.0-rc.9': - resolution: {integrity: sha512-lcJL0bN5hpgJfSIz/8PIf02irmyL43P+j1pTCfbD1DbLkmGRuFIA4DD3B3ZOvGqG0XiVvRznbKtN0COQVaKUTg==} + '@rolldown/binding-android-arm64@1.0.0-rc.10': + resolution: {integrity: sha512-jOHxwXhxmFKuXztiu1ORieJeTbx5vrTkcOkkkn2d35726+iwhrY1w/+nYY/AGgF12thg33qC3R1LMBF5tHTZHg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.0.0-rc.9': - resolution: {integrity: sha512-J7Zk3kLYFsLtuH6U+F4pS2sYVzac0qkjcO5QxHS7OS7yZu2LRs+IXo+uvJ/mvpyUljDJ3LROZPoQfgBIpCMhdQ==} + '@rolldown/binding-darwin-arm64@1.0.0-rc.10': + resolution: {integrity: sha512-gED05Teg/vtTZbIJBc4VNMAxAFDUPkuO/rAIyyxZjTj1a1/s6z5TII/5yMGZ0uLRCifEtwUQn8OlYzuYc0m70w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.0-rc.9': - resolution: {integrity: sha512-iwtmmghy8nhfRGeNAIltcNXzD0QMNaaA5U/NyZc1Ia4bxrzFByNMDoppoC+hl7cDiUq5/1CnFthpT9n+UtfFyg==} + '@rolldown/binding-darwin-x64@1.0.0-rc.10': + resolution: {integrity: sha512-rI15NcM1mA48lqrIxVkHfAqcyFLcQwyXWThy+BQ5+mkKKPvSO26ir+ZDp36AgYoYVkqvMcdS8zOE6SeBsR9e8A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.0.0-rc.9': - resolution: {integrity: sha512-DLFYI78SCiZr5VvdEplsVC2Vx53lnA4/Ga5C65iyldMVaErr86aiqCoNBLl92PXPfDtUYjUh+xFFor40ueNs4Q==} + '@rolldown/binding-freebsd-x64@1.0.0-rc.10': + resolution: {integrity: sha512-XZRXHdTa+4ME1MuDVp021+doQ+z6Ei4CCFmNc5/sKbqb8YmkiJdj8QKlV3rCI0AJtAeSB5n0WGPuJWNL9p/L2w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.9': - resolution: {integrity: sha512-CsjTmTwd0Hri6iTw/DRMK7kOZ7FwAkrO4h8YWKoX/kcj833e4coqo2wzIFywtch/8Eb5enQ/lwLM7w6JX1W5RQ==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.10': + resolution: {integrity: sha512-R0SQMRluISSLzFE20sPWYHVmJdDQnRyc/FzSCN72BqQmh2SOZUFG+N3/vBZpR4C6WpEUVYJLrYUXaj43sJsNLA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.9': - resolution: {integrity: sha512-2x9O2JbSPxpxMDhP9Z74mahAStibTlrBMW0520+epJH5sac7/LwZW5Bmg/E6CXuEF53JJFW509uP+lSedaUNxg==} + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.10': + resolution: {integrity: sha512-Y1reMrV/o+cwpduYhJuOE3OMKx32RMYCidf14y+HssARRmhDuWXJ4yVguDg2R/8SyyGNo+auzz64LnPK9Hq6jg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-arm64-musl@1.0.0-rc.9': - resolution: {integrity: sha512-JA1QRW31ogheAIRhIg9tjMfsYbglXXYGNPLdPEYrwFxdbkQCAzvpSCSHCDWNl4hTtrol8WeboCSEpjdZK8qrCg==} + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.10': + resolution: {integrity: sha512-vELN+HNb2IzuzSBUOD4NHmP9yrGwl1DVM29wlQvx1OLSclL0NgVWnVDKl/8tEks79EFek/kebQKnNJkIAA4W2g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.9': - resolution: {integrity: sha512-aOKU9dJheda8Kj8Y3w9gnt9QFOO+qKPAl8SWd7JPHP+Cu0EuDAE5wokQubLzIDQWg2myXq2XhTpOVS07qqvT+w==} + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.10': + resolution: {integrity: sha512-ZqrufYTgzxbHwpqOjzSsb0UV/aV2TFIY5rP8HdsiPTv/CuAgCRjM6s9cYFwQ4CNH+hf9Y4erHW1GjZuZ7WoI7w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.9': - resolution: {integrity: sha512-OalO94fqj7IWRn3VdXWty75jC5dk4C197AWEuMhIpvVv2lw9fiPhud0+bW2ctCxb3YoBZor71QHbY+9/WToadA==} + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.10': + resolution: {integrity: sha512-gSlmVS1FZJSRicA6IyjoRoKAFK7IIHBs7xJuHRSmjImqk3mPPWbR7RhbnfH2G6bcmMEllCt2vQ/7u9e6bBnByg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-gnu@1.0.0-rc.9': - resolution: {integrity: sha512-cVEl1vZtBsBZna3YMjGXNvnYYrOJ7RzuWvZU0ffvJUexWkukMaDuGhUXn0rjnV0ptzGVkvc+vW9Yqy6h8YX4pg==} + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.10': + resolution: {integrity: sha512-eOCKUpluKgfObT2pHjztnaWEIbUabWzk3qPZ5PuacuPmr4+JtQG4k2vGTY0H15edaTnicgU428XW/IH6AimcQw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-musl@1.0.0-rc.9': - resolution: {integrity: sha512-UzYnKCIIc4heAKgI4PZ3dfBGUZefGCJ1TPDuLHoCzgrMYPb5Rv6TLFuYtyM4rWyHM7hymNdsg5ik2C+UD9VDbA==} + '@rolldown/binding-linux-x64-musl@1.0.0-rc.10': + resolution: {integrity: sha512-Xdf2jQbfQowJnLcgYfD/m0Uu0Qj5OdxKallD78/IPPfzaiaI4KRAwZzHcKQ4ig1gtg1SuzC7jovNiM2TzQsBXA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@rolldown/binding-openharmony-arm64@1.0.0-rc.9': - resolution: {integrity: sha512-+6zoiF+RRyf5cdlFQP7nm58mq7+/2PFaY2DNQeD4B87N36JzfF/l9mdBkkmTvSYcYPE8tMh/o3cRlsx1ldLfog==} + '@rolldown/binding-openharmony-arm64@1.0.0-rc.10': + resolution: {integrity: sha512-o1hYe8hLi1EY6jgPFyxQgQ1wcycX+qz8eEbVmot2hFkgUzPxy9+kF0u0NIQBeDq+Mko47AkaFFaChcvZa9UX9Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-wasm32-wasi@1.0.0-rc.9': - resolution: {integrity: sha512-rgFN6sA/dyebil3YTlL2evvi/M+ivhfnyxec7AccTpRPccno/rPoNlqybEZQBkcbZu8Hy+eqNJCqfBR8P7Pg8g==} + '@rolldown/binding-wasm32-wasi@1.0.0-rc.10': + resolution: {integrity: sha512-Ugv9o7qYJudqQO5Y5y2N2SOo6S4WiqiNOpuQyoPInnhVzCY+wi/GHltcLHypG9DEUYMB0iTB/huJrpadiAcNcA==} engines: {node: '>=14.0.0'} cpu: [wasm32] - '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.9': - resolution: {integrity: sha512-lHVNUG/8nlF1IQk1C0Ci574qKYyty2goMiPlRqkC5R+3LkXDkL5Dhx8ytbxq35m+pkHVIvIxviD+TWLdfeuadA==} + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.10': + resolution: {integrity: sha512-7UODQb4fQUNT/vmgDZBl3XOBAIOutP5R3O/rkxg0aLfEGQ4opbCgU5vOw/scPe4xOqBwL9fw7/RP1vAMZ6QlAQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.0.0-rc.9': - resolution: {integrity: sha512-G0oA4+w1iY5AGi5HcDTxWsoxF509hrFIPB2rduV5aDqS9FtDg1CAfa7V34qImbjfhIcA8C+RekocJZA96EarwQ==} + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.10': + resolution: {integrity: sha512-PYxKHMVHOb5NJuDL53vBUl1VwUjymDcYI6rzpIni0C9+9mTiJedvUxSk7/RPp7OOAm3v+EjgMu9bIy3N6b408w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@rolldown/pluginutils@1.0.0-rc.9': - resolution: {integrity: sha512-w6oiRWgEBl04QkFZgmW+jnU1EC9b57Oihi2ot3HNWIQRqgHp5PnYDia5iZ5FF7rpa4EQdiqMDXjlqKGXBhsoXw==} + '@rolldown/pluginutils@1.0.0-rc.10': + resolution: {integrity: sha512-UkVDEFk1w3mveXeKgaTuYfKWtPbvgck1dT8TUG3bnccrH0XtLTuAyfCoks4Q/M5ZGToSVJTIQYCzy2g/atAOeg==} '@rollup/plugin-alias@6.0.0': resolution: {integrity: sha512-tPCzJOtS7uuVZd+xPhoy5W4vThe6KWXNmsFCNktaAh5RTqcLiSfT4huPQIXkgJ6YCOjJHvecOAzQxLFhPxKr+g==} @@ -1140,8 +1145,11 @@ packages: '@tybys/wasm-util@0.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} - '@types/bun@1.3.10': - resolution: {integrity: sha512-0+rlrUrOrTSskibryHbvQkDOWRJwJZqZlxrUs1u4oOoTln8+WIXBPmAuCF35SWB2z4Zl3E84Nl/D0P7803nigQ==} + '@types/bun@1.3.11': + resolution: {integrity: sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg==} + + '@types/bun@1.3.9': + resolution: {integrity: sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw==} '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} @@ -1224,8 +1232,11 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - bun-types@1.3.10: - resolution: {integrity: sha512-tcpfCCl6XWo6nCVnpcVrxQ+9AYN1iqMIzgrSKYMB/fjLtV2eyAVEg7AxQJuCq/26R6HpKWykQXuSOq/21RYcbg==} + bun-types@1.3.11: + resolution: {integrity: sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg==} + + bun-types@1.3.9: + resolution: {integrity: sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg==} bundle-require@5.1.0: resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} @@ -1721,8 +1732,8 @@ packages: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} - rolldown@1.0.0-rc.9: - resolution: {integrity: sha512-9EbgWge7ZH+yqb4d2EnELAntgPTWbfL8ajiTW+SyhJEC4qhBbkCKbqFV4Ge4zmu5ziQuVbWxb/XwLZ+RIO7E8Q==} + rolldown@1.0.0-rc.10: + resolution: {integrity: sha512-q7j6vvarRFmKpgJUT8HCAUljkgzEp4LAhPlJUvQhA5LA1SUL36s5QCysMutErzL3EbNOZOkoziSx9iZC4FddKA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -1847,16 +1858,16 @@ packages: tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - tinyexec@1.0.4: - resolution: {integrity: sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==} + tinyexec@1.0.2: + resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} engines: {node: '>=18'} tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} - tinyrainbow@3.1.0: - resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + tinyrainbow@3.0.3: + resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} engines: {node: '>=14.0.0'} tr46@1.0.1: @@ -1967,8 +1978,8 @@ packages: validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - viem@2.47.4: - resolution: {integrity: sha512-h0Wp/SYmJO/HB4B/em1OZ3W1LaKrmr7jzaN7talSlZpo0LCn0V6rZ5g923j6sf4VUSrqp/gUuWuHFc7UcoIp8A==} + viem@2.47.5: + resolution: {integrity: sha512-nVrJEQ8GL4JoVIrMBF3wwpTUZun0cpojfnOZ+96GtDWhqxZkVdy6vOEgu+jwfXqfTA/+wrR+YsN9TBQmhDUk0g==} peerDependencies: typescript: '>=5.0.4' peerDependenciesMeta: @@ -1980,13 +1991,13 @@ packages: peerDependencies: vite: '*' - vite@8.0.0: - resolution: {integrity: sha512-fPGaRNj9Zytaf8LEiBhY7Z6ijnFKdzU/+mL8EFBaKr7Vw1/FWcTBAMW0wLPJAGMPX38ZPVCVgLceWiEqeoqL2Q==} + vite@8.0.1: + resolution: {integrity: sha512-wt+Z2qIhfFt85uiyRt5LPU4oVEJBXj8hZNWKeqFG4gRG/0RaRGJ7njQCwzFVjO+v4+Ipmf5CY7VdmZRAYYBPHw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: '@types/node': ^20.19.0 || >=22.12.0 - '@vitejs/devtools': ^0.0.0-alpha.31 + '@vitejs/devtools': ^0.1.0 esbuild: ^0.27.0 jiti: '>=1.21.0' less: ^4.0.0 @@ -2138,13 +2149,13 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 - '@emnapi/core@1.9.0': + '@emnapi/core@1.9.1': dependencies: '@emnapi/wasi-threads': 1.2.0 tslib: 2.8.1 optional: true - '@emnapi/runtime@1.9.0': + '@emnapi/runtime@1.9.1': dependencies: tslib: 2.8.1 optional: true @@ -2344,8 +2355,8 @@ snapshots: '@napi-rs/wasm-runtime@1.1.1': dependencies: - '@emnapi/core': 1.9.0 - '@emnapi/runtime': 1.9.0 + '@emnapi/core': 1.9.1 + '@emnapi/runtime': 1.9.1 '@tybys/wasm-util': 0.10.1 optional: true @@ -2363,9 +2374,7 @@ snapshots: '@noble/hashes@2.0.1': {} - '@oxc-project/runtime@0.115.0': {} - - '@oxc-project/types@0.115.0': {} + '@oxc-project/types@0.120.0': {} '@polkadot-api/cli@0.19.4(postcss@8.5.8)': dependencies: @@ -2588,54 +2597,54 @@ snapshots: react: 19.0.0 rxjs: 7.8.2 - '@rolldown/binding-android-arm64@1.0.0-rc.9': + '@rolldown/binding-android-arm64@1.0.0-rc.10': optional: true - '@rolldown/binding-darwin-arm64@1.0.0-rc.9': + '@rolldown/binding-darwin-arm64@1.0.0-rc.10': optional: true - '@rolldown/binding-darwin-x64@1.0.0-rc.9': + '@rolldown/binding-darwin-x64@1.0.0-rc.10': optional: true - '@rolldown/binding-freebsd-x64@1.0.0-rc.9': + '@rolldown/binding-freebsd-x64@1.0.0-rc.10': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.9': + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.10': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.9': + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.10': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.0-rc.9': + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.10': optional: true - '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.9': + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.10': optional: true - '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.9': + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.10': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.0-rc.9': + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.10': optional: true - '@rolldown/binding-linux-x64-musl@1.0.0-rc.9': + '@rolldown/binding-linux-x64-musl@1.0.0-rc.10': optional: true - '@rolldown/binding-openharmony-arm64@1.0.0-rc.9': + '@rolldown/binding-openharmony-arm64@1.0.0-rc.10': optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-rc.9': + '@rolldown/binding-wasm32-wasi@1.0.0-rc.10': dependencies: '@napi-rs/wasm-runtime': 1.1.1 optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.9': + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.10': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.0-rc.9': + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.10': optional: true - '@rolldown/pluginutils@1.0.0-rc.9': {} + '@rolldown/pluginutils@1.0.0-rc.10': {} '@rollup/plugin-alias@6.0.0(rollup@4.59.0)': optionalDependencies: @@ -2777,9 +2786,13 @@ snapshots: tslib: 2.8.1 optional: true - '@types/bun@1.3.10': + '@types/bun@1.3.11': + dependencies: + bun-types: 1.3.11 + + '@types/bun@1.3.9': dependencies: - bun-types: 1.3.10 + bun-types: 1.3.9 '@types/chai@5.2.3': dependencies: @@ -2805,19 +2818,19 @@ snapshots: '@vitest/spy': 4.1.0 '@vitest/utils': 4.1.0 chai: 6.2.2 - tinyrainbow: 3.1.0 + tinyrainbow: 3.0.3 - '@vitest/mocker@4.1.0(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3))': + '@vitest/mocker@4.1.0(vite@8.0.1(@types/node@25.5.0)(esbuild@0.27.3))': dependencies: '@vitest/spy': 4.1.0 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 8.0.0(@types/node@25.5.0)(esbuild@0.27.3) + vite: 8.0.1(@types/node@25.5.0)(esbuild@0.27.3) '@vitest/pretty-format@4.1.0': dependencies: - tinyrainbow: 3.1.0 + tinyrainbow: 3.0.3 '@vitest/runner@4.1.0': dependencies: @@ -2837,7 +2850,7 @@ snapshots: dependencies: '@vitest/pretty-format': 4.1.0 convert-source-map: 2.0.0 - tinyrainbow: 3.1.0 + tinyrainbow: 3.0.3 abitype@1.2.3(typescript@5.9.3): optionalDependencies: @@ -2857,7 +2870,11 @@ snapshots: assertion-error@2.0.1: {} - bun-types@1.3.10: + bun-types@1.3.11: + dependencies: + '@types/node': 25.5.0 + + bun-types@1.3.9: dependencies: '@types/node': 25.5.0 @@ -3339,26 +3356,26 @@ snapshots: onetime: 7.0.0 signal-exit: 4.1.0 - rolldown@1.0.0-rc.9: + rolldown@1.0.0-rc.10: dependencies: - '@oxc-project/types': 0.115.0 - '@rolldown/pluginutils': 1.0.0-rc.9 + '@oxc-project/types': 0.120.0 + '@rolldown/pluginutils': 1.0.0-rc.10 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.0-rc.9 - '@rolldown/binding-darwin-arm64': 1.0.0-rc.9 - '@rolldown/binding-darwin-x64': 1.0.0-rc.9 - '@rolldown/binding-freebsd-x64': 1.0.0-rc.9 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.9 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.9 - '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.9 - '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.9 - '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.9 - '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.9 - '@rolldown/binding-linux-x64-musl': 1.0.0-rc.9 - '@rolldown/binding-openharmony-arm64': 1.0.0-rc.9 - '@rolldown/binding-wasm32-wasi': 1.0.0-rc.9 - '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.9 - '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.9 + '@rolldown/binding-android-arm64': 1.0.0-rc.10 + '@rolldown/binding-darwin-arm64': 1.0.0-rc.10 + '@rolldown/binding-darwin-x64': 1.0.0-rc.10 + '@rolldown/binding-freebsd-x64': 1.0.0-rc.10 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.10 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.10 + '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.10 + '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.10 + '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.10 + '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.10 + '@rolldown/binding-linux-x64-musl': 1.0.0-rc.10 + '@rolldown/binding-openharmony-arm64': 1.0.0-rc.10 + '@rolldown/binding-wasm32-wasi': 1.0.0-rc.10 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.10 + '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.10 rollup-plugin-dts@6.4.0(rollup@4.59.0)(typescript@5.9.3): dependencies: @@ -3505,14 +3522,14 @@ snapshots: tinyexec@0.3.2: {} - tinyexec@1.0.4: {} + tinyexec@1.0.2: {} tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 - tinyrainbow@3.1.0: {} + tinyrainbow@3.0.3: {} tr46@1.0.1: dependencies: @@ -3612,7 +3629,7 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - viem@2.47.4(typescript@5.9.3): + viem@2.47.5(typescript@5.9.3): dependencies: '@noble/curves': 1.9.1 '@noble/hashes': 1.8.0 @@ -3629,33 +3646,32 @@ snapshots: - utf-8-validate - zod - vite-tsconfig-paths@6.1.1(typescript@5.9.3)(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)): + vite-tsconfig-paths@6.1.1(typescript@5.9.3)(vite@8.0.1(@types/node@25.5.0)(esbuild@0.27.3)): dependencies: debug: 4.4.3 globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.9.3) - vite: 8.0.0(@types/node@25.5.0)(esbuild@0.27.3) + vite: 8.0.1(@types/node@25.5.0)(esbuild@0.27.3) transitivePeerDependencies: - supports-color - typescript - vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3): + vite@8.0.1(@types/node@25.5.0)(esbuild@0.27.3): dependencies: - '@oxc-project/runtime': 0.115.0 lightningcss: 1.32.0 picomatch: 4.0.3 postcss: 8.5.8 - rolldown: 1.0.0-rc.9 + rolldown: 1.0.0-rc.10 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 25.5.0 esbuild: 0.27.3 fsevents: 2.3.3 - vitest@4.1.0(@types/node@25.5.0)(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)): + vitest@4.1.0(@types/node@25.5.0)(vite@8.0.1(@types/node@25.5.0)(esbuild@0.27.3)): dependencies: '@vitest/expect': 4.1.0 - '@vitest/mocker': 4.1.0(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)) + '@vitest/mocker': 4.1.0(vite@8.0.1(@types/node@25.5.0)(esbuild@0.27.3)) '@vitest/pretty-format': 4.1.0 '@vitest/runner': 4.1.0 '@vitest/snapshot': 4.1.0 @@ -3669,10 +3685,10 @@ snapshots: picomatch: 4.0.3 std-env: 4.0.0 tinybench: 2.9.0 - tinyexec: 1.0.4 + tinyexec: 1.0.2 tinyglobby: 0.2.15 - tinyrainbow: 3.1.0 - vite: 8.0.0(@types/node@25.5.0)(esbuild@0.27.3) + tinyrainbow: 3.0.3 + vite: 8.0.1(@types/node@25.5.0)(esbuild@0.27.3) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 25.5.0 From df1d6ea169837d09d56a06454fb4ec0c94d53d86 Mon Sep 17 00:00:00 2001 From: Victor Oliva Date: Fri, 20 Mar 2026 10:39:08 +0100 Subject: [PATCH 2/5] fix: getStatements should return all statements in store --- packages/sdk-statement/src/statement-sdk.ts | 39 ++++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/packages/sdk-statement/src/statement-sdk.ts b/packages/sdk-statement/src/statement-sdk.ts index 18984cd..834d01c 100644 --- a/packages/sdk-statement/src/statement-sdk.ts +++ b/packages/sdk-statement/src/statement-sdk.ts @@ -1,11 +1,13 @@ import { SizedHex } from "@polkadot-api/substrate-bindings" import { toHex } from "@polkadot-api/utils" import { - firstValueFrom, + lastValueFrom, map, mergeAll, Observable, filter as rxjsFilter, + scan, + takeWhile, } from "rxjs" import { getApi } from "./api" import { Statement, statementCodec } from "./codec" @@ -22,11 +24,19 @@ const ANY_FILTER: TopicFilter = "any" export const createStatementSdk = (endpoint: string) => { const api = getApi(endpoint) - const getStatements$ = (filter: TopicFilter): Observable => + const getStatements$ = ( + filter: TopicFilter, + ): Observable<{ + statements: Statement[] + remaining?: number + }> => api.subscribeStatement(filter).pipe( map((evt) => { if (evt.event === "newStatements") { - return evt.data.statements.map(statementCodec.dec) + return { + statements: evt.data.statements.map(statementCodec.dec), + remaining: evt.data.remaining, + } } return null }), @@ -43,7 +53,23 @@ export const createStatementSdk = (endpoint: string) => { */ const getStatements = ( filter: TopicFilter = ANY_FILTER, - ): Promise => firstValueFrom(getStatements$(filter)) + ): Promise => + lastValueFrom( + getStatements$(filter).pipe( + scan( + (acc: { statements: Statement[]; remaining: number }, evt) => ({ + statements: [...acc.statements, ...evt.statements], + remaining: evt.remaining ?? 0, + }), + { + statements: [], + remaining: 0, + }, + ), + takeWhile((v) => v.remaining > 0, true), + map((v) => v.statements), + ), + ) return { /** @@ -67,7 +93,10 @@ export const createStatementSdk = (endpoint: string) => { * @returns Unsubscribe function. */ subscribeStatements: (filter: TopicFilter): Observable => - getStatements$(filter).pipe(mergeAll()), + getStatements$(filter).pipe( + map((v) => v.statements), + mergeAll(), + ), /** * Get broadcasts (statements with no decryptionKey) matching topics. From 2c3354fae6b5054de02f806b1b60b47ea05c1019 Mon Sep 17 00:00:00 2001 From: Victor Oliva Date: Fri, 20 Mar 2026 10:41:32 +0100 Subject: [PATCH 3/5] update statement-playground --- examples/statement-playground/index.ts | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/examples/statement-playground/index.ts b/examples/statement-playground/index.ts index 53527ad..3effc2d 100644 --- a/examples/statement-playground/index.ts +++ b/examples/statement-playground/index.ts @@ -1,13 +1,11 @@ -import { createClient } from "@polkadot-api/substrate-client" import { createStatementSdk, getStatementSigner, stringToTopic, type Statement, } from "@polkadot-api/sdk-statement" -import { getWsProvider } from "polkadot-api/ws" -import { sign, getPublicKey } from "@scure/sr25519" import { Binary, Blake2256 } from "@polkadot-api/substrate-bindings" +import { getPublicKey, sign } from "@scure/sr25519" import { jsonSerialize, mergeUint8 } from "polkadot-api/utils" // use any key @@ -16,14 +14,15 @@ const alice = getStatementSigner(getPublicKey(ALICE_SK), "sr25519", (p) => sign(ALICE_SK, p, Blake2256(mergeUint8([ALICE_SK, p]))), ) -const client = createClient(getWsProvider("ws://127.0.0.1:9936")) - -const sdk = createStatementSdk(client.request) +const sdk = createStatementSdk("ws://127.0.0.1:9936") // BUILD STATEMENT const stmt1: Statement = { decryptionKey: stringToTopic("key"), - priority: 1, + expiry: { + timestampSecs: (Date.now() + 3600000) / 1000, + sequence: 0, + }, topics: [stringToTopic("1"), stringToTopic("2")], data: Binary.fromText("TEST 1"), } @@ -34,15 +33,13 @@ console.log(await sdk.submit(signed1)) console.log( JSON.stringify( - await sdk.getStatements({ - dest: stringToTopic("key"), - topics: [stringToTopic("1"), stringToTopic("2")], - }), + await sdk.getPosted( + [stringToTopic("1"), stringToTopic("2")], + stringToTopic("key"), + ), jsonSerialize, ), ) // GET ALL STATEMENTS (i.e. `dump`) console.log(JSON.stringify(await sdk.getStatements(), jsonSerialize)) - -client.destroy() From 017359ab12e036b2ec867e8ce67fd2cbee20e863 Mon Sep 17 00:00:00 2001 From: Victor Oliva Date: Fri, 20 Mar 2026 11:38:09 +0100 Subject: [PATCH 4/5] remove top-level filtering functions --- packages/sdk-statement/CHANGELOG.md | 6 +-- packages/sdk-statement/src/filters.ts | 12 ++++++ packages/sdk-statement/src/index.ts | 5 ++- packages/sdk-statement/src/statement-sdk.ts | 44 +++------------------ packages/sdk-statement/src/types.ts | 4 +- 5 files changed, 25 insertions(+), 46 deletions(-) create mode 100644 packages/sdk-statement/src/filters.ts diff --git a/packages/sdk-statement/CHANGELOG.md b/packages/sdk-statement/CHANGELOG.md index bb97b78..1c47e3a 100644 --- a/packages/sdk-statement/CHANGELOG.md +++ b/packages/sdk-statement/CHANGELOG.md @@ -8,9 +8,9 @@ - `createStatementSdk` takes an endpoint URL rather than a `req` function. - Update `Statement` to new spec: replaces `priority` for `expiry`. - Removes `dump()` - - `getStatements` requires the `topics` parameter - - Adds `getBroadcasts` and `getPosted` for filtering based on the `dest` parameter - - Adds `subscribeStatements(topics)` for subscribing + - `getStatements` takes a `topicFilter` parameter + - Adds filtering functions for broadcasts and posted + - Adds `subscribeStatements(topicFilter)` for subscribing ## 0.4.1 2026-03-16 diff --git a/packages/sdk-statement/src/filters.ts b/packages/sdk-statement/src/filters.ts new file mode 100644 index 0000000..19c96c2 --- /dev/null +++ b/packages/sdk-statement/src/filters.ts @@ -0,0 +1,12 @@ +import { SizedHex } from "polkadot-api" +import { Statement } from "./codec" +import { TopicFilter } from "./types" + +export const topicFilter = (topics: Array>): TopicFilter => ({ + matchAll: topics, +}) + +export const filterBroadcasts = () => (stmt: Statement) => + stmt.decryptionKey === undefined +export const filterPosted = (dest: SizedHex<32>) => (stmt: Statement) => + stmt.decryptionKey === dest diff --git a/packages/sdk-statement/src/index.ts b/packages/sdk-statement/src/index.ts index 2be3c1a..239c9bc 100644 --- a/packages/sdk-statement/src/index.ts +++ b/packages/sdk-statement/src/index.ts @@ -1,5 +1,6 @@ export * from "./codec" -export * from "./statement-sdk" +export * from "./filters" export * from "./signer" -export type { SubmitResult, TopicFilter, StatementEvent } from "./types" +export * from "./statement-sdk" +export type { StatementEvent, SubmitResult, TopicFilter } from "./types" export { stringToTopic } from "./utils" diff --git a/packages/sdk-statement/src/statement-sdk.ts b/packages/sdk-statement/src/statement-sdk.ts index 834d01c..26fc173 100644 --- a/packages/sdk-statement/src/statement-sdk.ts +++ b/packages/sdk-statement/src/statement-sdk.ts @@ -1,4 +1,3 @@ -import { SizedHex } from "@polkadot-api/substrate-bindings" import { toHex } from "@polkadot-api/utils" import { lastValueFrom, @@ -7,14 +6,13 @@ import { Observable, filter as rxjsFilter, scan, + startWith, takeWhile, } from "rxjs" import { getApi } from "./api" import { Statement, statementCodec } from "./codec" import { SubmitResult, TopicFilter } from "./types" -const ANY_FILTER: TopicFilter = "any" - /** * Create statement sdk. * @@ -25,7 +23,7 @@ export const createStatementSdk = (endpoint: string) => { const api = getApi(endpoint) const getStatements$ = ( - filter: TopicFilter, + filter: TopicFilter = "any", ): Observable<{ statements: Statement[] remaining?: number @@ -51,9 +49,7 @@ export const createStatementSdk = (endpoint: string) => { * * @param filter Topic filter for statements. Defaults to matching all. */ - const getStatements = ( - filter: TopicFilter = ANY_FILTER, - ): Promise => + const getStatements = (filter?: TopicFilter): Promise => lastValueFrom( getStatements$(filter).pipe( scan( @@ -68,6 +64,7 @@ export const createStatementSdk = (endpoint: string) => { ), takeWhile((v) => v.remaining > 0, true), map((v) => v.statements), + startWith([]), ), ) @@ -92,41 +89,10 @@ export const createStatementSdk = (endpoint: string) => { * @param onError Callback for errors. * @returns Unsubscribe function. */ - subscribeStatements: (filter: TopicFilter): Observable => + subscribeStatements: (filter?: TopicFilter): Observable => getStatements$(filter).pipe( map((v) => v.statements), mergeAll(), ), - - /** - * Get broadcasts (statements with no decryptionKey) matching topics. - * - * @param topics Topics to match (all must be present). - */ - getBroadcasts: async ( - topics: Array> = [], - ): Promise => { - const filter: TopicFilter = - topics.length > 0 ? { matchAll: topics } : ANY_FILTER - const statements = await getStatements(filter) - return statements.filter((stmt) => stmt.decryptionKey === undefined) - }, - - /** - * Get posted statements (with decryptionKey) matching topics and - * destination. - * - * @param topics Topics to match (all must be present). - * @param dest Destination decryption key. - */ - getPosted: async ( - topics: Array>, - dest: SizedHex<32>, - ): Promise => { - const filter: TopicFilter = - topics.length > 0 ? { matchAll: topics } : ANY_FILTER - const statements = await getStatements(filter) - return statements.filter((stmt) => stmt.decryptionKey === dest) - }, } } diff --git a/packages/sdk-statement/src/types.ts b/packages/sdk-statement/src/types.ts index 79e0635..708432b 100644 --- a/packages/sdk-statement/src/types.ts +++ b/packages/sdk-statement/src/types.ts @@ -73,8 +73,8 @@ type SubmitRejected = { } | { /** - * Account reached its statement limit and submitted expiry is too low - * to evict existing. + * Account reached its statement limit and submitted expiry is too low to + * evict existing. */ reason: "accountFull" /** From 27248ba37a4f0474aac156ae3be482464c09e019 Mon Sep 17 00:00:00 2001 From: Victor Oliva Date: Fri, 20 Mar 2026 12:27:40 +0100 Subject: [PATCH 5/5] update example, add function to close connection --- examples/statement-playground/index.ts | 24 ++++++++++++++++----- examples/statement-playground/package.json | 4 ++++ packages/sdk-statement/CHANGELOG.md | 2 +- packages/sdk-statement/src/api.ts | 5 ++++- packages/sdk-statement/src/codec/codec.ts | 2 +- packages/sdk-statement/src/statement-sdk.ts | 7 +++++- 6 files changed, 35 insertions(+), 9 deletions(-) diff --git a/examples/statement-playground/index.ts b/examples/statement-playground/index.ts index 3effc2d..a9833f6 100644 --- a/examples/statement-playground/index.ts +++ b/examples/statement-playground/index.ts @@ -1,7 +1,9 @@ import { createStatementSdk, + filterPosted, getStatementSigner, stringToTopic, + topicFilter, type Statement, } from "@polkadot-api/sdk-statement" import { Binary, Blake2256 } from "@polkadot-api/substrate-bindings" @@ -28,18 +30,30 @@ const stmt1: Statement = { } // SIGN AND SUBMIT STATEMENT +const subscription = sdk + .getStatement$() + .subscribe((r) => console.log("received new statement", r)) + const signed1 = await alice.sign(stmt1) console.log(await sdk.submit(signed1)) console.log( + "posted to 'key' with topics '1' and '2'", JSON.stringify( - await sdk.getPosted( - [stringToTopic("1"), stringToTopic("2")], - stringToTopic("key"), - ), + ( + await sdk.getStatements( + topicFilter([stringToTopic("1"), stringToTopic("2")]), + ) + ).filter(filterPosted(stringToTopic("key"))), jsonSerialize, ), ) // GET ALL STATEMENTS (i.e. `dump`) -console.log(JSON.stringify(await sdk.getStatements(), jsonSerialize)) +console.log( + "all statements", + JSON.stringify(await sdk.getStatements(), jsonSerialize), +) + +subscription.unsubscribe() +sdk.destroy() diff --git a/examples/statement-playground/package.json b/examples/statement-playground/package.json index 6b72aa5..f004d58 100644 --- a/examples/statement-playground/package.json +++ b/examples/statement-playground/package.json @@ -3,6 +3,10 @@ "module": "index.ts", "type": "module", "private": true, + "scripts": { + "startNode": "zombienet --provider native spawn ./zombienet.toml", + "start": "bun run ./index.ts" + }, "dependencies": { "@polkadot-api/sdk-statement": "workspace:*", "@polkadot-api/substrate-bindings": "^0.19.0", diff --git a/packages/sdk-statement/CHANGELOG.md b/packages/sdk-statement/CHANGELOG.md index 1c47e3a..51b2a0f 100644 --- a/packages/sdk-statement/CHANGELOG.md +++ b/packages/sdk-statement/CHANGELOG.md @@ -10,7 +10,7 @@ - Removes `dump()` - `getStatements` takes a `topicFilter` parameter - Adds filtering functions for broadcasts and posted - - Adds `subscribeStatements(topicFilter)` for subscribing + - Adds `getStatement$(topicFilter)` for subscribing ## 0.4.1 2026-03-16 diff --git a/packages/sdk-statement/src/api.ts b/packages/sdk-statement/src/api.ts index f99cc6e..5d0347d 100644 --- a/packages/sdk-statement/src/api.ts +++ b/packages/sdk-statement/src/api.ts @@ -18,7 +18,8 @@ export const getApi = (endpoint: string) => { const sendUnsubscribe = () => { // Fire-and-forget - subId != null && client.request(unsubscribeMethod, [subId]) + subId != null && + client.request(unsubscribeMethod, [subId]).catch(() => {}) } client._request(method, params, { @@ -54,5 +55,7 @@ export const getApi = (endpoint: string) => { "statement_unsubscribeStatement", [topicFilter], ), + + destroy: () => client.destroy(), } } diff --git a/packages/sdk-statement/src/codec/codec.ts b/packages/sdk-statement/src/codec/codec.ts index 1a41c38..a66f064 100644 --- a/packages/sdk-statement/src/codec/codec.ts +++ b/packages/sdk-statement/src/codec/codec.ts @@ -137,7 +137,7 @@ const createExpiry = (timestamp: number, sequenceNumber: number = 0) => { if (sequenceNumber < 0 || sequenceNumber > MAX_SEQ_NUMBER) { throw new RangeError(`sequenceNumber must be 0-${MAX_SEQ_NUMBER}`) } - return (BigInt(timestamp) << 32n) | BigInt(sequenceNumber) + return (BigInt(Math.floor(timestamp)) << 32n) | BigInt(sequenceNumber) } const parseExpiry = (expiry: bigint) => ({ diff --git a/packages/sdk-statement/src/statement-sdk.ts b/packages/sdk-statement/src/statement-sdk.ts index 26fc173..d87d0e2 100644 --- a/packages/sdk-statement/src/statement-sdk.ts +++ b/packages/sdk-statement/src/statement-sdk.ts @@ -89,10 +89,15 @@ export const createStatementSdk = (endpoint: string) => { * @param onError Callback for errors. * @returns Unsubscribe function. */ - subscribeStatements: (filter?: TopicFilter): Observable => + getStatement$: (filter?: TopicFilter): Observable => getStatements$(filter).pipe( map((v) => v.statements), mergeAll(), ), + + /** + * Close the connection and clean resources. + */ + destroy: api.destroy, } }