@parity/product-sdk-bulletin 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,194 +1,52 @@
1
- import { TypedApi, PolkadotSigner } from 'polkadot-api';
1
+ import { BulletinTypedApi, AsyncBulletinClient, ClientConfig, StoreBuilder, AuthCallBuilder, CallBuilder } from '@parity/bulletin-sdk';
2
+ export { AsyncBulletinClient, AuthCallBuilder, AuthorizationScope, BulletinClientInterface, BulletinError, BulletinPreparer, BulletinTypedApi, CID, CallBuilder, Chunk, ChunkDetails, ChunkProgressEvent, ChunkStatus, ChunkedStoreResult, ChunkerConfig, ClientConfig, DEFAULT_CHUNKER_CONFIG, DEFAULT_CLIENT_CONFIG, DEFAULT_STORE_OPTIONS, DagManifest, ErrorCode, MAX_CHUNK_SIZE, MAX_FILE_SIZE, MockBulletinClient, MockClientConfig, MockOperation, ProgressCallback, ProgressEvent, StoreBuilder, StoreOptions, StoreResult, SubmitFn, TransactionReceipt, TransactionStatusEvent, TxStatus, WaitFor, calculateCid, cidFromBytes, cidToBytes, convertCid, estimateAuthorization, getContentHash, parseCid, reassembleChunks, resolveClientConfig, validateChunkSize } from '@parity/bulletin-sdk';
3
+ import { PolkadotSigner } from 'polkadot-api';
4
+ import { bulletin } from '@parity/product-sdk-descriptors/bulletin';
2
5
  import { WaitFor, TxStatus } from '@parity/product-sdk-tx';
3
- import { Environment } from '@parity/product-sdk-chain-client';
4
6
  export { Environment } from '@parity/product-sdk-chain-client';
5
7
 
6
8
  /**
7
- * Hash algorithms supported by the Bulletin Chain.
9
+ * Known Bulletin Chain networks.
8
10
  *
9
- * Values are multihash codes as defined in the
10
- * {@link https://github.com/multiformats/multicodec multicodec table}.
11
+ * Pairs each environment with the genesis hash and the PAPI descriptor needed
12
+ * to construct an `AsyncBulletinClient`. Re-uses the descriptor exported by
13
+ * `@parity/product-sdk-descriptors/bulletin` — the bulletin descriptor is the
14
+ * same across all environments today, so the difference between entries is
15
+ * the genesis hash (and, downstream, the chain RPC URL).
11
16
  */
12
- declare const HashAlgorithm: {
13
- /** BLAKE2b-256 — default for product-sdk and the chain SDK. */
14
- readonly Blake2b256: 45600;
15
- /** SHA2-256 — default for bulletin-deploy. */
16
- readonly Sha2_256: 18;
17
- /** Keccak-256 — Ethereum compatibility. */
18
- readonly Keccak256: 27;
19
- };
20
- /** A multihash code supported by the Bulletin Chain. */
21
- type HashAlgorithm = (typeof HashAlgorithm)[keyof typeof HashAlgorithm];
17
+
18
+ interface BulletinNetwork {
19
+ /** Genesis hash of the bulletin chain on this environment. */
20
+ genesisHash: `0x${string}`;
21
+ /** PAPI descriptor for typed API access. */
22
+ descriptor: typeof bulletin;
23
+ }
22
24
  /**
23
- * CID codecs supported by the Bulletin Chain.
25
+ * Bulletin Chain network presets.
24
26
  *
25
- * Values are multicodec codes.
27
+ * Use these with {@link BulletinClient.create} when you want to be explicit
28
+ * about the network rather than passing an environment string. Reads go
29
+ * through the host's preimage subscription (container-only); no gateway
30
+ * URL is configured per network.
26
31
  */
27
- declare const CidCodec: {
28
- /** Raw binary — default for single-chunk data. */
29
- readonly Raw: 85;
30
- /** DAG-PB — used for multi-chunk manifests / directory structures. */
31
- readonly DagPb: 112;
32
- /** DAG-CBOR — alternative DAG encoding. */
33
- readonly DagCbor: 113;
32
+ declare const BulletinChain: {
33
+ readonly paseo: {
34
+ readonly genesisHash: "0x744960c32e3a3df5440e1ecd4d34096f1ce2230d7016a5ada8a765d5a622b4ea";
35
+ readonly descriptor: any;
36
+ };
34
37
  };
35
- /** A multicodec code supported by the Bulletin Chain. */
36
- type CidCodec = (typeof CidCodec)[keyof typeof CidCodec];
37
- /**
38
- * Compute the CIDv1 (blake2b-256, raw codec) for arbitrary data.
39
- * Deterministic: same input always produces the same CID.
40
- */
41
- declare function computeCid(data: Uint8Array): string;
42
- /**
43
- * Extract the content hash digest from a CIDv1 string and return it as a
44
- * `0x`-prefixed hex string — the preimage key format used by the host API.
45
- *
46
- * Accepts CIDv1 with any hash algorithm supported by the Bulletin Chain
47
- * (blake2b-256, sha2-256, keccak-256).
48
- *
49
- * @param cid - CIDv1 base32 string (as produced by {@link computeCid} or {@link hashToCid}).
50
- * @returns `0x`-prefixed hex string of the 32-byte hash digest.
51
- * @throws If the CID is not CIDv1 or uses an unsupported hash algorithm.
52
- */
53
- declare function cidToPreimageKey(cid: string): `0x${string}`;
54
- /**
55
- * Reconstruct a CIDv1 from a `0x`-prefixed hex hash stored on-chain.
56
- *
57
- * This is the inverse of {@link cidToPreimageKey}: given a 32-byte content hash
58
- * and the CID configuration used when the data was stored, it rebuilds the
59
- * original CIDv1 so you can construct IPFS gateway URLs.
60
- *
61
- * The Bulletin Chain supports multiple hash algorithms and codecs — pass the
62
- * values that match the on-chain `TransactionInfo` to get the correct CID.
63
- * When omitted, defaults match {@link computeCid} (blake2b-256, raw).
64
- *
65
- * @param hexHash - `0x`-prefixed hex string of a 32-byte hash digest
66
- * (66 characters total: `"0x"` + 64 hex chars).
67
- * @param hashCode - Multihash code of the hashing algorithm (default: blake2b-256 `0xb220`).
68
- * Use {@link HashAlgorithm} for the supported values.
69
- * @param codec - Multicodec code of the CID codec (default: raw `0x55`).
70
- * Use {@link CidCodec} for the supported values.
71
- * @returns Base32-lower CIDv1 string.
72
- * @throws If `hexHash` is not exactly 66 characters, or if the hash/codec is unsupported.
73
- *
74
- * @example
75
- * ```ts
76
- * import { hashToCid, HashAlgorithm, CidCodec, gatewayUrl, getGateway } from "@parity/product-sdk-bulletin";
77
- *
78
- * // Default (blake2b-256, raw) — matches computeCid output
79
- * const cid = hashToCid(onChainHash);
80
- *
81
- * // SHA2-256 content stored via bulletin-deploy
82
- * const cid2 = hashToCid(onChainHash, HashAlgorithm.Sha2_256);
83
- *
84
- * // DAG-PB manifest with blake2b-256
85
- * const cid3 = hashToCid(manifestHash, HashAlgorithm.Blake2b256, CidCodec.DagPb);
86
- *
87
- * const url = gatewayUrl(cid, getGateway("paseo"));
88
- * ```
89
- *
90
- * @see {@link cidToPreimageKey} for the reverse direction (CID → hex hash).
91
- * @see {@link computeCid} for computing a CID from raw data.
92
- * @see {@link HashAlgorithm} for supported hash algorithms.
93
- * @see {@link CidCodec} for supported CID codecs.
94
- */
95
- declare function hashToCid(hexHash: `0x${string}`, hashCode?: HashAlgorithm, codec?: CidCodec): string;
38
+ /** Network keys with built-in presets in {@link BulletinChain}. */
39
+ type BulletinEnvironment = keyof typeof BulletinChain;
96
40
 
97
- /** Typed API for the Bulletin Chain, derived from PAPI descriptors. */
98
- type BulletinApi = TypedApi<any>;
41
+ /** Typed API for the Bulletin Chain (re-export from upstream). */
42
+ type BulletinApi = BulletinTypedApi;
99
43
 
100
- /**
101
- * Options for {@link upload}.
102
- *
103
- * Note: `waitFor`, `timeoutMs`, and `onStatus` only apply to the **transaction**
104
- * upload path (when an explicit signer is used or the dev signer fallback is active).
105
- * The preimage path delegates to the host which controls its own submission
106
- * lifecycle — these options are ignored in that case.
107
- */
108
- interface UploadOptions {
109
- /** IPFS gateway base URL (e.g., from `getGateway("paseo")`). If provided, result includes gatewayUrl. */
110
- gateway?: string;
111
- /** When to resolve: `"best-block"` (default) or `"finalized"`. Transaction path only. */
112
- waitFor?: WaitFor;
113
- /** Timeout in ms. Default: 300_000 (5 min). Transaction path only. */
114
- timeoutMs?: number;
115
- /** Lifecycle status callback for UI progress. Transaction path only. */
116
- onStatus?: (status: TxStatus) => void;
117
- }
118
- /** Fields common to all upload results. */
119
- interface UploadResultBase {
120
- /** CIDv1 string (blake2b-256, raw codec). */
121
- cid: string;
122
- /** Gateway URL. Present only if `gateway` was provided in options. */
123
- gatewayUrl?: string;
124
- }
125
- /**
126
- * Result of a successful upload to the Bulletin Chain.
127
- *
128
- * Discriminated on `kind`:
129
- * - `"transaction"` — uploaded via a signed `TransactionStorage.store` extrinsic.
130
- * - `"preimage"` — uploaded via the host preimage API (no user signing).
131
- *
132
- * Use `result.kind` to narrow the type and access path-specific fields.
133
- */
134
- type UploadResult = (UploadResultBase & {
135
- /** Upload was performed via a signed transaction. */
136
- kind: "transaction";
137
- /** Block hash where the store transaction was included. */
138
- blockHash: string;
139
- }) | (UploadResultBase & {
140
- /** Upload was performed via the host preimage API. */
141
- kind: "preimage";
142
- /** Hex key returned by the host preimage API. */
143
- preimageKey: string;
144
- });
145
- /** A single item in a batch upload. */
146
- interface BatchUploadItem {
147
- /** Raw bytes to upload. */
148
- data: Uint8Array;
149
- /** Label for progress tracking (e.g., filename). */
150
- label: string;
151
- }
152
- /** Fields common to all batch upload results. */
153
- interface BatchUploadResultBase {
154
- label: string;
155
- cid: string;
156
- gatewayUrl?: string;
157
- }
158
- /**
159
- * Result for one item in a batch upload.
160
- *
161
- * Discriminated on `kind` (upload path) and `success` (outcome).
162
- * Use `result.success` to check for errors, then `result.kind` to access
163
- * path-specific fields like `blockHash` or `preimageKey`.
164
- */
165
- type BatchUploadResult = (BatchUploadResultBase & {
166
- kind: "transaction";
167
- success: true;
168
- /** Block hash where the store transaction was included. */
169
- blockHash: string;
170
- }) | (BatchUploadResultBase & {
171
- kind: "preimage";
172
- success: true;
173
- /** Hex key returned by the host preimage API. */
174
- preimageKey: string;
175
- }) | (BatchUploadResultBase & {
176
- kind: "transaction" | "preimage";
177
- success: false;
178
- /** Error message describing the failure. */
179
- error: string;
180
- });
181
- /** Options for {@link batchUpload}. */
182
- interface BatchUploadOptions extends UploadOptions {
183
- /** Called after each item completes (success or failure). */
184
- onProgress?: (completed: number, total: number, current: BatchUploadResult) => void;
185
- }
186
44
  /**
187
45
  * Authorization status for a Bulletin Chain account.
188
46
  *
189
- * Returned by {@link checkAuthorization} to enable pre-flight checks before
190
- * uploading. Consumers can use this to show "not authorized" or "insufficient
191
- * quota" messages instead of letting the transaction fail.
47
+ * Returned by {@link checkAuthorization} as a pre-flight check before storing
48
+ * data. Consumers can use this to show "not authorized" or "insufficient quota"
49
+ * messages instead of letting the transaction fail mid-execution.
192
50
  */
193
51
  interface AuthorizationStatus {
194
52
  /** Whether an authorization entry exists for this account. */
@@ -200,104 +58,220 @@ interface AuthorizationStatus {
200
58
  /** Block number when the authorization expires. 0 if not authorized. */
201
59
  expiration: number;
202
60
  }
203
- /** Options for gateway fetch operations. */
204
- interface FetchOptions {
205
- /** Timeout in ms. Default: 30_000. */
206
- timeoutMs?: number;
207
- }
208
- /** Options for query operations that support host lookup auto-resolution. */
209
- interface QueryOptions extends FetchOptions {
61
+ /**
62
+ * Options for {@link BulletinClient.fetchBytes} / {@link BulletinClient.fetchJson}.
63
+ */
64
+ interface QueryOptions {
210
65
  /**
211
- * Timeout for the host preimage lookup subscription in ms.
212
- * Only applies when the query resolves through the host path.
213
- * Default: 30_000.
66
+ * Timeout for the host preimage lookup subscription, in ms.
67
+ * Default: 30_000. Applied per lookup for chunked content (DAG-PB
68
+ * manifest CIDs), the manifest fetch and each child chunk fetch
69
+ * each get this budget.
214
70
  */
215
71
  lookupTimeoutMs?: number;
72
+ /**
73
+ * When `true`, return the raw bytes for the requested CID without
74
+ * parsing or recursing into a DAG-PB manifest. Default: `false` — the
75
+ * client transparently reassembles chunked content so callers don't
76
+ * need to know whether a CID points at a single chunk or a manifest.
77
+ *
78
+ * Set this if you want to inspect the manifest itself, e.g., to read
79
+ * `unixfs.fileSize()` ahead of fetching, or to drive your own chunk
80
+ * pipeline.
81
+ */
82
+ noReassemble?: boolean;
216
83
  }
217
84
 
85
+ /** A single matched entry from `TransactionStorage.Transactions`. */
86
+ interface ChainStoredEntry {
87
+ /** Block number where the transaction was included. */
88
+ block: number;
89
+ /** Index of the entry within the block's transactions array. */
90
+ index: number;
91
+ /** Size of the stored data in bytes (from chain metadata). */
92
+ size: number;
93
+ /** Number of chunks (1 for unchunked data, >1 for chunked + manifest). */
94
+ blockChunks: number;
95
+ }
218
96
  /**
219
- * Ergonomic entry point for Bulletin Chain operations.
97
+ * Verification options for {@link verifyOnChain}.
98
+ */
99
+ interface VerifyOnChainOptions {
100
+ /**
101
+ * Block number to look up. Pass the `blockNumber` returned from a prior
102
+ * `store(...).send()` for an O(1) lookup.
103
+ *
104
+ * If omitted, throws — full-chain scans are not supported because
105
+ * `RetentionPeriod` can be many days of blocks. Use `getEntries()` on
106
+ * `api.query.TransactionStorage.Transactions` directly if you need that.
107
+ */
108
+ block: number;
109
+ /**
110
+ * Optional: index within the block. When provided, narrows verification
111
+ * to that exact slot. Useful when re-checking a known `(block, index)`
112
+ * tuple from an earlier receipt.
113
+ */
114
+ index?: number;
115
+ }
116
+ /**
117
+ * Verify that a CID is recorded in the bulletin chain's `Transactions`
118
+ * storage at the given block.
220
119
  *
221
- * Bundles a typed Bulletin API (from chain-client) and an IPFS gateway URL
222
- * so callers don't need to re-pass them on every call.
120
+ * Returns the matched entry (with block + index) when the CID's content
121
+ * hash and hashing algorithm both match a `Transactions[block]` entry.
122
+ * Returns `null` when no match is found at that block.
223
123
  *
224
- * Both upload and query paths use the host container APIs:
225
- * - **Uploads** the host preimage API signs and submits automatically.
226
- * - **Queries** (`fetchBytes`/`fetchJson`) uses host preimage lookup with caching.
124
+ * @param api - Typed bulletin API.
125
+ * @param cid - CIDv1 string to look up.
126
+ * @param options - Verification target (block number, optional index).
227
127
  *
228
128
  * @example
229
129
  * ```ts
230
- * const bulletin = await BulletinClient.create("paseo");
231
- * const result = await bulletin.upload(fileBytes);
232
- * const metadata = await bulletin.fetchJson<Metadata>(result.cid);
130
+ * const receipt = await client.store(data).send();
131
+ * if (receipt.blockNumber !== undefined) {
132
+ * const entry = await verifyOnChain(client.api, receipt.cid!.toString(), {
133
+ * block: receipt.blockNumber,
134
+ * index: receipt.extrinsicIndex,
135
+ * });
136
+ * if (!entry) console.warn("CID not found in expected block — chain reorg?");
137
+ * }
138
+ * ```
139
+ */
140
+ declare function verifyOnChain(api: BulletinApi, cid: string, options: VerifyOnChainOptions): Promise<ChainStoredEntry | null>;
141
+
142
+ /**
143
+ * Options for {@link BulletinClient.create}.
144
+ *
145
+ * One of two construction shapes is supported:
146
+ *
147
+ * - **Environment shorthand** — pass an `environment` string keyed by
148
+ * {@link BulletinChain}. Wires up the chain-client automatically.
149
+ * - **Explicit network** — pass `genesisHash` and `descriptor` directly
150
+ * (e.g., spread from a {@link BulletinChain} entry, or supply custom
151
+ * values for a private chain).
152
+ */
153
+ type CreateBulletinClientOptions = (CreateBulletinClientCommon & {
154
+ environment: BulletinEnvironment;
155
+ }) | (CreateBulletinClientCommon & {
156
+ genesisHash: `0x${string}`;
157
+ descriptor: (typeof BulletinChain)[BulletinEnvironment]["descriptor"];
158
+ });
159
+ interface CreateBulletinClientCommon {
160
+ /** Signer for transaction submission. Required — every store needs a signer. */
161
+ signer: PolkadotSigner;
162
+ /** Optional config forwarded to {@link AsyncBulletinClient}. */
163
+ config?: Partial<ClientConfig>;
164
+ }
165
+ /**
166
+ * Ergonomic entry point for Bulletin Chain operations.
167
+ *
168
+ * Wraps {@link AsyncBulletinClient} from `@parity/bulletin-sdk` (which handles
169
+ * chunking, DAG-PB manifests, CID calculation, and progress events) and adds:
170
+ *
171
+ * - **Network presets** via {@link BulletinClient.create} and {@link BulletinChain}.
172
+ * - **Read helpers** ({@link fetchBytes}, {@link fetchJson}) routed through
173
+ * the host's preimage subscription — upstream is upload-only and the SDK
174
+ * is container-only by design (no public-gateway fetches).
175
+ * - **Pre-flight authorization check** ({@link checkAuthorization}) for
176
+ * friendlier UX before submitting a store.
177
+ *
178
+ * For uploads, mirror upstream's fluent builders:
179
+ *
180
+ * ```ts
181
+ * const client = await BulletinClient.create({ environment: "paseo", signer });
182
+ * const result = await client.store(data).send();
183
+ * ```
184
+ *
185
+ * For chunked uploads with progress:
186
+ *
187
+ * ```ts
188
+ * const result = await client
189
+ * .store(largeFile)
190
+ * .withChunkSize(1 << 20)
191
+ * .withCallback((evt) => console.log(evt))
192
+ * .send();
233
193
  * ```
234
194
  */
235
195
  declare class BulletinClient {
196
+ /** Underlying upstream client — exposed for power users. */
197
+ readonly inner: AsyncBulletinClient;
198
+ /** Typed Bulletin Chain API. */
236
199
  readonly api: BulletinApi;
237
- readonly gateway: string;
200
+ /** Lazy-resolved host-preimage query strategy, cached for the client lifetime. */
238
201
  private queryStrategyPromise;
202
+ /** Constructed via {@link create} or {@link from}. */
239
203
  private constructor();
240
- /** Lazily resolve and cache the query strategy for the client lifetime. */
204
+ /** Resolve and cache the host query strategy on first use. */
241
205
  private resolveQuery;
242
- /** Create from an environment — resolves API via chain-client, gateway from known list. */
243
- static create(env: Environment): Promise<BulletinClient>;
244
- /** Create from an explicit API and gateway (custom setups, testing). */
245
- static from(api: BulletinApi, gateway: string): BulletinClient;
246
- /** Compute CID without uploading. Static — no instance needed. */
247
- static computeCid(data: Uint8Array): string;
248
206
  /**
249
- * Reconstruct a CID from a `0x`-prefixed hex hash. Static no instance needed.
207
+ * Create a client from an environment shorthand or an explicit network.
250
208
  *
251
- * Useful for converting on-chain hashes back to CIDs for IPFS gateway lookups.
252
- * Pass optional hash algorithm and codec to match the on-chain CID configuration.
209
+ * Environment form uses our `getChainAPI(env)` to resolve the typed API.
210
+ * Explicit form skips the environment lookup and lets you pass any
211
+ * genesis/descriptor combo.
253
212
  *
254
- * @see {@link hashToCid} for full documentation.
255
- */
256
- static hashToCid(hexHash: `0x${string}`, hashCode?: HashAlgorithm, codec?: CidCodec): string;
257
- /**
258
- * Upload data to the Bulletin Chain.
213
+ * @example
214
+ * ```ts
215
+ * // Shorthand
216
+ * const client = await BulletinClient.create({ environment: "paseo", signer });
259
217
  *
260
- * @param data - Raw bytes to store.
261
- * @param signer - Optional signer. When omitted, uses the host preimage API.
262
- * @param options - Upload options (timeout, waitFor, status callback).
218
+ * // Explicit (custom network)
219
+ * const client = await BulletinClient.create({
220
+ * ...BulletinChain.paseo,
221
+ * signer,
222
+ * config: { defaultChunkSize: 1 << 20 },
223
+ * });
224
+ * ```
263
225
  */
264
- upload(data: Uint8Array, signer?: PolkadotSigner, options?: Omit<UploadOptions, "gateway">): Promise<UploadResult>;
226
+ static create(options: CreateBulletinClientOptions): Promise<BulletinClient>;
265
227
  /**
266
- * Upload multiple items sequentially.
228
+ * Construct from a pre-built `AsyncBulletinClient` and PAPI typed API.
267
229
  *
268
- * @param items - Array of items to upload, each with data and a label.
269
- * @param signer - Optional signer. When omitted, auto-resolved.
270
- * @param options - Batch upload options (timeout, progress callback).
230
+ * Use this when you already own the connection lifecycle (BYOD setups,
231
+ * tests). The caller is responsible for calling `papiClient.destroy()`
232
+ * this client's {@link destroy} only tears down the upstream's
233
+ * `onDestroy` hook.
271
234
  */
272
- batchUpload(items: BatchUploadItem[], signer?: PolkadotSigner, options?: Omit<BatchUploadOptions, "gateway">): Promise<BatchUploadResult[]>;
235
+ static from(inner: AsyncBulletinClient, api: BulletinApi): BulletinClient;
236
+ /** Build a store transaction. See upstream `StoreBuilder` for chained options. */
237
+ store(data: Uint8Array): StoreBuilder;
238
+ /** Authorize an account to store data on the chain (sudo required on most networks). */
239
+ authorizeAccount(who: string, transactions: number, bytes: bigint): AuthCallBuilder;
240
+ /** Authorize content storage by hash (anyone can store; no fees). */
241
+ authorizePreimage(contentHash: Uint8Array, maxSize: bigint): AuthCallBuilder;
242
+ /** Renew a stored transaction by block + index. */
243
+ renew(block: number, index: number): CallBuilder;
244
+ /** Estimate the authorization (transactions + bytes) needed for `dataSize`. */
245
+ estimateAuthorization(dataSize: number): {
246
+ transactions: number;
247
+ bytes: number;
248
+ };
273
249
  /**
274
- * Fetch raw bytes by CID.
250
+ * Fetch raw bytes for a CID via the host's preimage lookup.
275
251
  *
276
- * Uses host preimage lookup with caching.
277
- */
278
- fetchBytes(cid: string, options?: QueryOptions): Promise<Uint8Array>;
279
- /**
280
- * Fetch and parse JSON by CID.
252
+ * Container-only outside a Polkadot Browser / Desktop host this
253
+ * throws {@link BulletinHostUnavailableError}. The chain stores
254
+ * content metadata (`content_hash`, size, codec) but the bytes
255
+ * themselves are surfaced through the host's preimage subscription.
281
256
  *
282
- * Auto-resolves query path (same as {@link fetchBytes}).
257
+ * Use {@link verifyOnChain} if you only need to confirm a CID was
258
+ * recorded on-chain (no byte fetch).
283
259
  */
260
+ fetchBytes(cid: string, options?: QueryOptions): Promise<Uint8Array>;
261
+ /** Fetch and parse JSON for a CID. */
284
262
  fetchJson<T>(cid: string, options?: QueryOptions): Promise<T>;
285
- /** Check if a CID exists on the gateway. */
286
- cidExists(cid: string): Promise<boolean>;
287
- /** Build the full gateway URL for a CID. */
288
- gatewayUrl(cid: string): string;
263
+ /** Pre-flight: check whether `address` can store on the bulletin chain. */
264
+ checkAuthorization(address: string): Promise<AuthorizationStatus>;
289
265
  /**
290
- * Check whether an account is authorized to store data on the Bulletin Chain.
291
- *
292
- * Use as a pre-flight check before {@link upload} to provide clear UX
293
- * instead of letting the transaction fail mid-execution.
266
+ * Verify that a CID was recorded on-chain at the given block.
294
267
  *
295
- * @param address - SS58-encoded account address to check.
296
- * @returns Authorization status with remaining quota.
297
- *
298
- * @see {@link checkAuthorization} for the standalone function equivalent.
268
+ * Common pattern: pass `blockNumber` (and optionally `extrinsicIndex`)
269
+ * from a `store(...).send()` receipt to confirm the upload landed.
270
+ * See {@link verifyOnChain} for details.
299
271
  */
300
- checkAuthorization(address: string): Promise<AuthorizationStatus>;
272
+ verifyOnChain(cid: string, options: VerifyOnChainOptions): Promise<ChainStoredEntry | null>;
273
+ /** Tear down the underlying connection. */
274
+ destroy(): Promise<void>;
301
275
  }
302
276
 
303
277
  /**
@@ -331,122 +305,108 @@ declare class BulletinClient {
331
305
  * @see {@link BulletinClient.checkAuthorization} for the client method equivalent.
332
306
  */
333
307
  declare function checkAuthorization(api: BulletinApi, address: string): Promise<AuthorizationStatus>;
334
-
335
308
  /**
336
- * Base class for all Bulletin Chain errors.
337
- *
338
- * Use `instanceof BulletinError` to catch any bulletin-related error.
339
- *
340
- * @example
341
- * ```ts
342
- * try {
343
- * await bulletin.upload(data);
344
- * } catch (e) {
345
- * if (e instanceof BulletinError) {
346
- * console.error("Bulletin operation failed:", e.message);
347
- * }
348
- * }
349
- * ```
309
+ * Options for {@link authorizeAccount}.
350
310
  */
351
- declare class BulletinError extends Error {
352
- constructor(message: string, options?: ErrorOptions);
353
- }
354
- /**
355
- * The host preimage API is unavailable.
356
- *
357
- * Thrown when bulletin operations require the host container but it's not available.
358
- * This typically means the SDK is running outside of Polkadot Browser/Desktop.
359
- */
360
- declare class BulletinHostUnavailableError extends BulletinError {
361
- constructor(operation: "upload" | "query");
311
+ interface AuthorizeAccountOptions {
312
+ /**
313
+ * Wrap the extrinsic in `Sudo.sudo(...)` before submission. Default: `false`.
314
+ *
315
+ * Use `true` on networks where granting bulletin storage authorization
316
+ * requires sudo permissions (most production / managed test networks).
317
+ * Use `false` (default) when the account self-authorizes typical for
318
+ * local development chains.
319
+ */
320
+ viaSudo?: boolean;
321
+ /** When to resolve: `"best-block"` (default) or `"finalized"`. */
322
+ waitFor?: WaitFor;
323
+ /** Timeout in ms. Default: 300_000 (5 min). */
324
+ timeoutMs?: number;
325
+ /** Lifecycle status callback for UI progress. */
326
+ onStatus?: (status: TxStatus) => void;
362
327
  }
363
328
  /**
364
- * The host preimage lookup timed out.
329
+ * Grant an account authorization to store data on the Bulletin Chain.
330
+ *
331
+ * Submits a `TransactionStorage.authorize_account` extrinsic, optionally
332
+ * wrapped in `Sudo.sudo(...)` for networks that require sudo to grant
333
+ * authorization. Mirrors the call shape of {@link upload} — top-level
334
+ * function, takes an explicit signer, returns a block hash on success.
335
+ *
336
+ * Pair with {@link checkAuthorization} for a typical "check, grant if
337
+ * insufficient, then upload" flow.
338
+ *
339
+ * ## Additive semantics — call once per authorization need
340
+ *
341
+ * `authorize_account` is **additive** within an unexpired authorization window
342
+ * for `AuthorizationScope::Account` (see `pallet-bulletin-transaction-storage`,
343
+ * `fn authorize`). Each successful call **adds** to the existing
344
+ * `transactions_allowance` and `bytes_allowance` rather than overwriting them.
345
+ *
346
+ * Implications for callers:
347
+ *
348
+ * - Calling this function twice with `(100, 1MB)` while the previous
349
+ * authorization is still active leaves the account with quota for `200`
350
+ * transactions and `2MB` — likely unintended.
351
+ * - **This function does NOT use `withRetry`.** Retrying a successful-but-
352
+ * acknowledgment-lost submission would double-grant the quota. Callers
353
+ * needing retry should wrap this function and use {@link checkAuthorization}
354
+ * to verify the post-state before retrying.
355
+ * - To "reset" a quota, let the existing authorization expire
356
+ * (`AuthorizationPeriod` blocks). The next call after expiry creates a fresh
357
+ * authorization rather than adding.
358
+ *
359
+ * Note: `AuthorizationScope::Preimage` uses `set` semantics in the same
360
+ * pallet. This helper is for account-scope authorization only.
361
+ *
362
+ * @param api - Typed Bulletin Chain API instance.
363
+ * @param who - SS58-encoded account to authorize.
364
+ * @param transactions - Number of transactions to **add** to the account's allowance.
365
+ * @param bytes - Byte budget to **add** to the account's allowance.
366
+ * @param signer - Signer for the extrinsic. On `viaSudo: true` this must be the sudo key.
367
+ * @param options - Optional `viaSudo` flag plus standard submission controls.
368
+ * @returns Block hash where the extrinsic was included.
369
+ * @throws {ProductBulletinError} If `viaSudo: true` is requested but the chain has no `Sudo` pallet.
370
+ *
371
+ * @example Direct (account self-authorizes — local dev)
372
+ * ```ts
373
+ * import { authorizeAccount } from "@parity/product-sdk-bulletin";
365
374
  *
366
- * The host was unable to retrieve the requested content within the timeout period.
367
- * The content may still become available later.
368
- */
369
- declare class BulletinLookupTimeoutError extends BulletinError {
370
- /** The CID that was being looked up. */
371
- readonly cid: string;
372
- /** The timeout duration in milliseconds. */
373
- readonly timeoutMs: number;
374
- constructor(cid: string, timeoutMs: number);
375
- }
376
- /**
377
- * The host interrupted the preimage lookup.
375
+ * await authorizeAccount(api, address, 100, 100n * 1024n * 1024n, signer);
376
+ * ```
378
377
  *
379
- * The host terminated the lookup subscription, typically after repeated failures
380
- * or when the host determines the content is unavailable.
381
- */
382
- declare class BulletinLookupInterruptedError extends BulletinError {
383
- /** The CID that was being looked up. */
384
- readonly cid: string;
385
- constructor(cid: string);
386
- }
387
- /**
388
- * Failed to check authorization status for an account.
378
+ * @example Sudo-wrapped (managed test network)
379
+ * ```ts
380
+ * await authorizeAccount(api, userAddress, 100, 1_000_000n, sudoSigner, {
381
+ * viaSudo: true,
382
+ * });
383
+ * ```
389
384
  *
390
- * Wraps RPC or query errors that occur when checking if an account
391
- * is authorized to store data on the Bulletin Chain.
385
+ * @see {@link checkAuthorization} for the read counterpart.
386
+ * @see {@link BulletinClient.authorizeAccount} for the client method equivalent.
392
387
  */
393
- declare class BulletinAuthorizationError extends BulletinError {
394
- /** The address that was being checked. */
395
- readonly address: string;
396
- constructor(address: string, cause?: unknown);
397
- }
388
+ declare function authorizeAccount(api: BulletinApi, who: string, transactions: number, bytes: bigint, signer: PolkadotSigner, options?: AuthorizeAccountOptions): Promise<{
389
+ blockHash: string;
390
+ }>;
391
+
398
392
  /**
399
- * The IPFS gateway for the specified environment is not available.
393
+ * Build a `PolkadotSigner` whose underlying signer is resolved on every call.
400
394
  *
401
- * Thrown when attempting to use gateway features for a network that
402
- * doesn't have a live bulletin gateway yet.
403
- */
404
- declare class BulletinGatewayUnavailableError extends BulletinError {
405
- /** The environment that was requested. */
406
- readonly environment: string;
407
- constructor(environment: string);
408
- }
409
- /**
410
- * An IPFS gateway request failed.
395
+ * `AsyncBulletinClient` takes a fixed `PolkadotSigner` at construction, but
396
+ * apps often build the bulletin client before any account is selected. This
397
+ * wrapper defers signer resolution: each call to `signTx` / `signBytes`
398
+ * invokes `getSigner()` and forwards to the result. If the getter returns
399
+ * `null`, calls throw with a clear message.
411
400
  *
412
- * Thrown when a fetch to the IPFS gateway returns a non-OK response.
413
- */
414
- declare class BulletinGatewayFetchError extends BulletinError {
415
- /** The CID that was being fetched. */
416
- readonly cid: string;
417
- /** The HTTP status code returned by the gateway. */
418
- readonly status: number;
419
- /** The HTTP status text returned by the gateway. */
420
- readonly statusText: string;
421
- constructor(cid: string, status: number, statusText: string);
422
- }
423
- /**
424
- * Invalid CID format or version.
401
+ * The `publicKey` field is *also* resolved lazily accessing it before a
402
+ * signer is available throws. This means callers that read `publicKey`
403
+ * eagerly will fail fast with the same error rather than seeing a stale
404
+ * key from a previously-selected account.
425
405
  *
426
- * Thrown when a CID string cannot be parsed or has an unexpected version/codec.
406
+ * Account changes between calls are picked up automatically: each sign
407
+ * resolves the current signer.
427
408
  */
428
- declare class BulletinCidError extends BulletinError {
429
- /** The invalid CID string, if available. */
430
- readonly cid?: string;
431
- constructor(message: string, cid?: string);
432
- }
433
-
434
- /**
435
- * Get the IPFS gateway URL for an environment.
436
- * @throws {BulletinGatewayUnavailableError} If the network doesn't have a live gateway yet.
437
- */
438
- declare function getGateway(env: Environment): string;
439
- /** Build the full gateway URL for a CID. */
440
- declare function gatewayUrl(cid: string, gateway: string): string;
441
- /** Check if a CID exists on the gateway (HEAD request). Returns false on any error or timeout. */
442
- declare function cidExists(cid: string, gateway: string, options?: FetchOptions): Promise<boolean>;
443
- /**
444
- * Fetch raw bytes from the gateway.
445
- * @throws {BulletinGatewayFetchError} If the gateway returns a non-OK response.
446
- */
447
- declare function fetchBytes(cid: string, gateway: string, options?: FetchOptions): Promise<Uint8Array>;
448
- /** Fetch and parse JSON from the gateway. */
449
- declare function fetchJson<T>(cid: string, gateway: string, options?: FetchOptions): Promise<T>;
409
+ declare function createLazySigner(getSigner: () => PolkadotSigner | null, onMissing?: string): PolkadotSigner;
450
410
 
451
411
  /**
452
412
  * Query strategy for the Bulletin Chain.
@@ -470,86 +430,175 @@ interface QueryStrategy {
470
430
  declare function resolveQueryStrategy(): Promise<QueryStrategy>;
471
431
 
472
432
  /**
473
- * Fetch raw bytes for a CID using the host preimage lookup.
433
+ * Fetch raw bytes for a CID via the host's preimage lookup.
434
+ *
435
+ * Container-only by design: the bulletin SDK does not retrieve content
436
+ * through public IPFS gateways. Inside a Polkadot Browser / Desktop
437
+ * container, the host's `preimageManager` provides a cached, polling-
438
+ * managed lookup that returns bytes when the underlying IPFS network
439
+ * makes them available. Outside a container, this throws
440
+ * {@link BulletinHostUnavailableError}.
474
441
  *
475
- * Uses local cache + managed IPFS polling via the host container.
442
+ * The bulletin chain stores transaction *metadata* on-chain
443
+ * (`chunk_root`, `content_hash`, `size`, `cid_codec`, `hashing`) — the
444
+ * content bytes themselves live in IPFS and are surfaced through the
445
+ * host's preimage subscription, never via direct gateway fetch.
446
+ *
447
+ * To prove that a CID was stored on-chain (without fetching the bytes),
448
+ * use `verifyOnChain` from `verify.ts`.
476
449
  *
477
450
  * @param cid - CIDv1 string to fetch.
478
- * @param options - Query options (lookupTimeoutMs for host).
479
- * @returns Raw bytes of the content.
480
- * @throws {Error} If the host preimage API is unavailable.
451
+ * @param options - Query options (`lookupTimeoutMs` for host).
452
+ * @throws {BulletinHostUnavailableError} If running outside a container.
481
453
  */
482
454
  declare function queryBytes(cid: string, options?: QueryOptions): Promise<Uint8Array>;
483
455
  /**
484
- * Fetch and parse JSON for a CID, auto-resolving the query path.
485
- *
486
- * Delegates to {@link queryBytes} and parses the result as JSON.
456
+ * Fetch and parse JSON for a CID via the host's preimage lookup.
487
457
  *
488
- * @param cid - CIDv1 string to fetch.
489
- * @param options - Query options.
490
- * @returns Parsed JSON value.
491
- * @throws {Error} If the host preimage API is unavailable.
458
+ * Convenience wrapper over {@link queryBytes}.
492
459
  */
493
460
  declare function queryJson<T>(cid: string, options?: QueryOptions): Promise<T>;
461
+ /**
462
+ * Execute a query using a pre-resolved strategy.
463
+ *
464
+ * Exposed so `BulletinClient` can resolve the strategy once at
465
+ * construction time and reuse it across calls without re-detecting
466
+ * the host environment on every fetch.
467
+ *
468
+ * **Reassembly is automatic.** If `cid` carries the DAG-PB codec
469
+ * (`0x70`) — meaning the upload was chunked and a UnixFS manifest was
470
+ * created — this function recursively fetches each chunk via `strategy
471
+ * .lookup` and returns the concatenated bytes. Pass `noReassemble: true`
472
+ * to get the raw manifest bytes instead.
473
+ *
474
+ * For raw-codec CIDs (`0x55`, single-chunk content), the bytes returned
475
+ * by the host are returned directly — no parsing overhead.
476
+ */
477
+ declare function executeQuery(strategy: QueryStrategy, cid: string, options?: QueryOptions): Promise<Uint8Array>;
494
478
 
495
479
  /**
496
- * Discriminated union describing how data will be uploaded to the Bulletin Chain.
480
+ * Hash algorithms supported by the Bulletin Chain.
497
481
  *
498
- * - `"preimage"` the host handles signing and chain submission via its preimage API.
499
- * - `"signer"` — a `TransactionStorage.store` transaction is signed and submitted directly.
482
+ * Values are multihash codes from the multicodec table.
500
483
  */
501
- type UploadStrategy = {
502
- kind: "preimage";
503
- submit: (data: Uint8Array) => Promise<string>;
504
- } | {
505
- kind: "signer";
506
- signer: PolkadotSigner;
484
+ declare const HashAlgorithm: {
485
+ /** BLAKE2b-256 — chain default. */
486
+ readonly Blake2b256: 45600;
487
+ /** SHA2-256. */
488
+ readonly Sha2_256: 18;
489
+ /** Keccak-256 — Ethereum compatibility. */
490
+ readonly Keccak256: 27;
491
+ };
492
+ type HashAlgorithm = (typeof HashAlgorithm)[keyof typeof HashAlgorithm];
493
+ /**
494
+ * CID codecs supported by the Bulletin Chain.
495
+ */
496
+ declare const CidCodec: {
497
+ /** Raw binary — default for single-chunk data. */
498
+ readonly Raw: 85;
499
+ /** DAG-PB — used for multi-chunk manifests / IPFS UnixFS. */
500
+ readonly DagPb: 112;
501
+ /** DAG-CBOR — alternative DAG encoding. */
502
+ readonly DagCbor: 113;
507
503
  };
504
+ type CidCodec = (typeof CidCodec)[keyof typeof CidCodec];
508
505
  /**
509
- * Determine the upload strategy for the Bulletin Chain.
506
+ * Reconstruct a CIDv1 from a `0x`-prefixed 32-byte hex hash.
510
507
  *
511
- * Resolution order:
512
- * 1. If an explicit signer is provided, use it directly.
513
- * 2. Otherwise, use the host preimage API (the SDK is designed to run inside a container).
508
+ * Useful when reading on-chain `TransactionInfo.content_hash` and you need
509
+ * the CID to look up content via an IPFS gateway.
514
510
  *
515
- * @param explicitSigner - Optional signer provided by the caller. When present,
516
- * skips host auto-detection entirely.
517
- * @returns The resolved upload strategy.
518
- * @throws {Error} If no signer is provided and the host preimage API is unavailable.
511
+ * @param hexHash - 66-char `0x`-prefixed hex of a 32-byte digest.
512
+ * @param hashCode - Multihash code (default: blake2b-256).
513
+ * @param codec - Multicodec code (default: raw).
519
514
  */
520
- declare function resolveUploadStrategy(explicitSigner?: PolkadotSigner): Promise<UploadStrategy>;
515
+ declare function hashToCid(hexHash: `0x${string}`, hashCode?: HashAlgorithm, codec?: CidCodec): string;
516
+ /**
517
+ * Extract the 32-byte content hash digest from a CIDv1 and return it as a
518
+ * `0x`-prefixed hex string.
519
+ *
520
+ * Useful for matching a CID against on-chain `TransactionInfo.content_hash`.
521
+ */
522
+ declare function cidToPreimageKey(cid: string): `0x${string}`;
521
523
 
522
524
  /**
523
- * Upload data to the Bulletin Chain.
525
+ * Bulletin error types.
524
526
  *
525
- * When a signer is provided, submits a `TransactionStorage.store` transaction
526
- * directly. When omitted, uses the host preimage API — the host signs and
527
- * submits automatically.
527
+ * Two error families coexist:
528
528
  *
529
- * Computes the CIDv1 (blake2b-256, raw codec) locally.
529
+ * 1. **Upstream SDK errors** `BulletinError` and `ErrorCode` from
530
+ * `@parity/bulletin-sdk` cover upload/store/authorization failures
531
+ * surfaced by `AsyncBulletinClient`. Each carries `code`, `retryable`,
532
+ * and `recoveryHint`.
533
+ * 2. **Read-side errors** declared here — host preimage availability /
534
+ * lookup timeouts / interrupts, plus CID format problems, surfaced by
535
+ * our retrieval helpers (`fetchBytes`, `fetchJson`, `verifyOnChain`).
530
536
  *
531
- * @param api - Typed Bulletin Chain API.
532
- * @param data - Raw bytes to store.
533
- * @param signer - Optional signer. When omitted, uses host preimage API.
534
- * @param options - Upload options (gateway, timeout, waitFor, status callback).
535
- * @returns Upload result with CID and either blockHash or preimageKey.
537
+ * Catch upstream errors with `instanceof BulletinError`. Catch our read-side
538
+ * errors with `instanceof ProductBulletinError` (or the specific subclass).
536
539
  */
537
- declare function upload(api: BulletinApi, data: Uint8Array, signer?: PolkadotSigner, options?: UploadOptions): Promise<UploadResult>;
540
+
538
541
  /**
539
- * Upload multiple items sequentially to the Bulletin Chain.
542
+ * Base class for read-side errors raised by `@parity/product-sdk-bulletin`.
540
543
  *
541
- * Bulletin Chain requires sequential transaction submission (nonce ordering).
542
- * Individual failures are captured in results — the batch does not abort.
544
+ * Distinct from upstream `BulletinError` which covers upload/store failures.
545
+ */
546
+ declare class ProductBulletinError extends Error {
547
+ constructor(message: string, options?: ErrorOptions);
548
+ }
549
+ /**
550
+ * The host preimage API is unavailable.
543
551
  *
544
- * Signer resolution follows the same rules as {@link upload}: when omitted,
545
- * the strategy is auto-resolved once and reused for all items.
552
+ * Thrown when bulletin operations require the host container but it's not
553
+ * available. This typically means the SDK is running outside Polkadot
554
+ * Browser / Desktop. The bulletin SDK is container-only by design — see
555
+ * the README for the rationale.
556
+ */
557
+ declare class BulletinHostUnavailableError extends ProductBulletinError {
558
+ constructor(operation: "upload" | "query");
559
+ }
560
+ /**
561
+ * The host preimage lookup timed out.
546
562
  *
547
- * @param api - Typed Bulletin Chain API.
548
- * @param items - Array of items to upload, each with data and a label.
549
- * @param signer - Optional signer. When omitted, auto-resolved.
550
- * @param options - Batch upload options (gateway, timeout, progress callback).
551
- * @returns Array of results, one per item, preserving input order.
563
+ * The host was unable to retrieve the requested content within the timeout
564
+ * period. The content may still become available later.
552
565
  */
553
- declare function batchUpload(api: BulletinApi, items: BatchUploadItem[], signer?: PolkadotSigner, options?: BatchUploadOptions): Promise<BatchUploadResult[]>;
566
+ declare class BulletinLookupTimeoutError extends ProductBulletinError {
567
+ /** The CID that was being looked up. */
568
+ readonly cid: string;
569
+ /** The timeout duration in milliseconds. */
570
+ readonly timeoutMs: number;
571
+ constructor(cid: string, timeoutMs: number);
572
+ }
573
+ /**
574
+ * The host interrupted the preimage lookup.
575
+ *
576
+ * The host terminated the lookup subscription, typically after repeated
577
+ * failures or when the host determines the content is unavailable.
578
+ */
579
+ declare class BulletinLookupInterruptedError extends ProductBulletinError {
580
+ /** The CID that was being looked up. */
581
+ readonly cid: string;
582
+ constructor(cid: string);
583
+ }
584
+ /**
585
+ * Invalid CID format or version.
586
+ */
587
+ declare class BulletinCidError extends ProductBulletinError {
588
+ /** The invalid CID string, if available. */
589
+ readonly cid?: string;
590
+ constructor(message: string, cid?: string);
591
+ }
592
+ /**
593
+ * Failed to check authorization status for an account.
594
+ *
595
+ * Wraps RPC or query errors that occur when checking if an account
596
+ * is authorized to store data on the Bulletin Chain.
597
+ */
598
+ declare class BulletinAuthorizationError extends ProductBulletinError {
599
+ /** The address that was being checked. */
600
+ readonly address: string;
601
+ constructor(address: string, cause?: unknown);
602
+ }
554
603
 
555
- export { type AuthorizationStatus, type BatchUploadItem, type BatchUploadOptions, type BatchUploadResult, type BulletinApi, BulletinAuthorizationError, BulletinCidError, BulletinClient, BulletinError, BulletinGatewayFetchError, BulletinGatewayUnavailableError, BulletinHostUnavailableError, BulletinLookupInterruptedError, BulletinLookupTimeoutError, CidCodec, type FetchOptions, HashAlgorithm, type QueryOptions, type QueryStrategy, type UploadOptions, type UploadResult, type UploadStrategy, batchUpload, checkAuthorization, cidExists, cidToPreimageKey, computeCid, fetchBytes, fetchJson, gatewayUrl, getGateway, hashToCid, queryBytes, queryJson, resolveQueryStrategy, resolveUploadStrategy, upload };
604
+ export { type AuthorizationStatus, type AuthorizeAccountOptions, type BulletinApi, BulletinAuthorizationError, BulletinChain, BulletinCidError, BulletinClient, type BulletinEnvironment, BulletinHostUnavailableError, BulletinLookupInterruptedError, BulletinLookupTimeoutError, type BulletinNetwork, type ChainStoredEntry, CidCodec, type CreateBulletinClientOptions, HashAlgorithm, ProductBulletinError, type QueryOptions, type QueryStrategy, type VerifyOnChainOptions, authorizeAccount, checkAuthorization, cidToPreimageKey, createLazySigner, executeQuery, hashToCid, queryBytes, queryJson, resolveQueryStrategy, verifyOnChain };