@orbinum/sdk 0.6.0 → 0.7.1

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/README.md CHANGED
@@ -28,6 +28,7 @@ const client = await OrbinumClient.connect({
28
28
  const info = await client.substrate.getChainInfo();
29
29
  const root = await client.rpcV2.privacy.getMerkleRoot();
30
30
  const block = await client.substrate.getBlock('best');
31
+ const isValidator = await client.rpcV2.chain.isValidator('5GrwvaEF...');
31
32
  ```
32
33
 
33
34
  `OrbinumClient` exposes the following modules:
@@ -36,7 +37,8 @@ const block = await client.substrate.getBlock('best');
36
37
  |---|---|
37
38
  | `client.substrate` | Substrate RPC — blocks, events, transactions |
38
39
  | `client.evm` | EVM JSON-RPC client (`null` if `evmRpc` not set) |
39
- | `client.rpcV2` | Orbinum `rpc-v2` namespaces (`privacy_*`, etc.) |
40
+ | `client.rpcV2` | Orbinum `rpc-v2` namespaces (`chain_*`, `privacy_*`) |
41
+ | `client.rpcV2.chain` | Chain-level queries (`chain_isValidator`, etc.) |
40
42
  | `client.shieldedPool` | Shielded pool extrinsics |
41
43
  | `client.accountMapping` | Alias, chain-link, and identity operations |
42
44
  | `client.precompiles` | EVM precompile wrappers (`null` if `evmRpc` not set) |
package/dist/index.d.mts CHANGED
@@ -159,12 +159,12 @@ declare class SubstrateClient {
159
159
  * const result = await tx.signAndSubmit(signer);
160
160
  * ```
161
161
  */
162
- get unsafe(): polkadot_api.UnsafeApi<unknown>;
162
+ get unsafe(): polkadot_api.TypedApi<polkadot_api.ChainDefinition, false>;
163
163
  /**
164
164
  * Wraps pre-built SCALE call bytes (from protocol-core TransactionBuilder)
165
165
  * into a PAPI UnsafeTransaction that can be signed and submitted.
166
166
  */
167
- txFromCallData(callData: Uint8Array): Promise<polkadot_api.UnsafeTransaction<any, string, string, any, Record<string, {
167
+ txFromCallData(callData: Uint8Array): Promise<polkadot_api.Transaction<any, Record<string, {
168
168
  value: any;
169
169
  additionalSigned: any;
170
170
  } | {
@@ -711,7 +711,6 @@ declare class IndexerClient {
711
711
  constructor(config: IndexerClientConfig);
712
712
  private _fetchResponse;
713
713
  private get;
714
- private post;
715
714
  private getOrNull;
716
715
  private buildQuery;
717
716
  /** Returns the total count of shielded commitments. */
@@ -744,11 +743,15 @@ declare class IndexerClient {
744
743
  /** Returns the spent/unspent status of a nullifier. */
745
744
  getNullifierStatus(hex: string): Promise<NullifierStatusResult>;
746
745
  /**
747
- * Batch-checks which of the given nullifiers are spent.
748
- * Returns only the nullifiers that exist in the spent set.
749
- * Accepts up to 100 nullifiers (0x-prefixed hex).
746
+ * Downloads the full spent nullifier set and returns it as a Set of lowercase hex strings.
747
+ *
748
+ * The server sees an identical GET request regardless of which notes the wallet holds —
749
+ * the intersection is computed locally (PIR-A privacy model).
750
+ *
751
+ * Suitable for wallets with up to ~1M spent nullifiers (~70 MB raw, <20 MB gzip).
752
+ * For the current Orbinum testnet/mainnet scale this is the recommended approach.
750
753
  */
751
- getNullifiersBatch(nullifiers: string[]): Promise<SpentNullifier[]>;
754
+ getAllSpentNullifiers(): Promise<Set<string>>;
752
755
  /**
753
756
  * Returns temporal metadata for private transfers that spent any of the given nullifiers.
754
757
  * Only blockNumber, extrinsicIndex, timestampMs, and hash are returned — no cross-link
@@ -1346,6 +1349,20 @@ declare class AccountMappingModule {
1346
1349
  dispatchAsLinkedAccount(params: DispatchAsLinkedParams, signer: PolkadotSigner): Promise<TxResult>;
1347
1350
  }
1348
1351
 
1352
+ /**
1353
+ * Typed client for general chain state queries under the `chain_*` namespace.
1354
+ */
1355
+ declare class ChainModule {
1356
+ private readonly substrate;
1357
+ constructor(substrate: SubstrateClient);
1358
+ /**
1359
+ * Returns `true` if the given SS58 account is an active Aura validator.
1360
+ *
1361
+ * Reads `pallet_aura::Authorities` directly from storage at the best known block.
1362
+ */
1363
+ isValidator(ss58Address: string): Promise<boolean>;
1364
+ }
1365
+
1349
1366
  type RpcV2MerkleProof = {
1350
1367
  path: string[];
1351
1368
  leafIndex: number;
@@ -1896,6 +1913,8 @@ declare class OrbinumClient {
1896
1913
  readonly accountMapping: AccountMappingModule;
1897
1914
  /** Typed access to `privacy_*` custom RPC endpoints. */
1898
1915
  readonly privacy: PrivacyModule;
1916
+ /** Typed access to general chain state via `chain_*` custom RPC endpoints. */
1917
+ readonly chain: ChainModule;
1899
1918
  /** Typed access to `zkVerifier_*` custom RPC endpoints. */
1900
1919
  readonly zkVerifier: ZkVerifierModule;
1901
1920
  /** Typed access to `relayer_*` RPC endpoints (registry lookup and pending fee queries). */
package/dist/index.d.ts CHANGED
@@ -159,12 +159,12 @@ declare class SubstrateClient {
159
159
  * const result = await tx.signAndSubmit(signer);
160
160
  * ```
161
161
  */
162
- get unsafe(): polkadot_api.UnsafeApi<unknown>;
162
+ get unsafe(): polkadot_api.TypedApi<polkadot_api.ChainDefinition, false>;
163
163
  /**
164
164
  * Wraps pre-built SCALE call bytes (from protocol-core TransactionBuilder)
165
165
  * into a PAPI UnsafeTransaction that can be signed and submitted.
166
166
  */
167
- txFromCallData(callData: Uint8Array): Promise<polkadot_api.UnsafeTransaction<any, string, string, any, Record<string, {
167
+ txFromCallData(callData: Uint8Array): Promise<polkadot_api.Transaction<any, Record<string, {
168
168
  value: any;
169
169
  additionalSigned: any;
170
170
  } | {
@@ -711,7 +711,6 @@ declare class IndexerClient {
711
711
  constructor(config: IndexerClientConfig);
712
712
  private _fetchResponse;
713
713
  private get;
714
- private post;
715
714
  private getOrNull;
716
715
  private buildQuery;
717
716
  /** Returns the total count of shielded commitments. */
@@ -744,11 +743,15 @@ declare class IndexerClient {
744
743
  /** Returns the spent/unspent status of a nullifier. */
745
744
  getNullifierStatus(hex: string): Promise<NullifierStatusResult>;
746
745
  /**
747
- * Batch-checks which of the given nullifiers are spent.
748
- * Returns only the nullifiers that exist in the spent set.
749
- * Accepts up to 100 nullifiers (0x-prefixed hex).
746
+ * Downloads the full spent nullifier set and returns it as a Set of lowercase hex strings.
747
+ *
748
+ * The server sees an identical GET request regardless of which notes the wallet holds —
749
+ * the intersection is computed locally (PIR-A privacy model).
750
+ *
751
+ * Suitable for wallets with up to ~1M spent nullifiers (~70 MB raw, <20 MB gzip).
752
+ * For the current Orbinum testnet/mainnet scale this is the recommended approach.
750
753
  */
751
- getNullifiersBatch(nullifiers: string[]): Promise<SpentNullifier[]>;
754
+ getAllSpentNullifiers(): Promise<Set<string>>;
752
755
  /**
753
756
  * Returns temporal metadata for private transfers that spent any of the given nullifiers.
754
757
  * Only blockNumber, extrinsicIndex, timestampMs, and hash are returned — no cross-link
@@ -1346,6 +1349,20 @@ declare class AccountMappingModule {
1346
1349
  dispatchAsLinkedAccount(params: DispatchAsLinkedParams, signer: PolkadotSigner): Promise<TxResult>;
1347
1350
  }
1348
1351
 
1352
+ /**
1353
+ * Typed client for general chain state queries under the `chain_*` namespace.
1354
+ */
1355
+ declare class ChainModule {
1356
+ private readonly substrate;
1357
+ constructor(substrate: SubstrateClient);
1358
+ /**
1359
+ * Returns `true` if the given SS58 account is an active Aura validator.
1360
+ *
1361
+ * Reads `pallet_aura::Authorities` directly from storage at the best known block.
1362
+ */
1363
+ isValidator(ss58Address: string): Promise<boolean>;
1364
+ }
1365
+
1349
1366
  type RpcV2MerkleProof = {
1350
1367
  path: string[];
1351
1368
  leafIndex: number;
@@ -1896,6 +1913,8 @@ declare class OrbinumClient {
1896
1913
  readonly accountMapping: AccountMappingModule;
1897
1914
  /** Typed access to `privacy_*` custom RPC endpoints. */
1898
1915
  readonly privacy: PrivacyModule;
1916
+ /** Typed access to general chain state via `chain_*` custom RPC endpoints. */
1917
+ readonly chain: ChainModule;
1899
1918
  /** Typed access to `zkVerifier_*` custom RPC endpoints. */
1900
1919
  readonly zkVerifier: ZkVerifierModule;
1901
1920
  /** Typed access to `relayer_*` RPC endpoints (registry lookup and pending fee queries). */
package/dist/index.js CHANGED
@@ -130,7 +130,7 @@ module.exports = __toCommonJS(index_exports);
130
130
 
131
131
  // src/substrate/SubstrateClient.ts
132
132
  var import_polkadot_api = require("polkadot-api");
133
- var import_ws_provider = require("polkadot-api/ws-provider");
133
+ var import_ws = require("polkadot-api/ws");
134
134
  var import_metadata_builders = require("@polkadot-api/metadata-builders");
135
135
  var import_substrate_bindings = require("@polkadot-api/substrate-bindings");
136
136
  var import_substrate_bindings2 = require("@polkadot-api/substrate-bindings");
@@ -176,7 +176,7 @@ var SubstrateClient = class _SubstrateClient {
176
176
  * Throws if the node does not respond within `timeoutMs`.
177
177
  */
178
178
  static async connect(wsUrl, timeoutMs = 15e3) {
179
- const provider = (0, import_ws_provider.getWsProvider)(wsUrl);
179
+ const provider = (0, import_ws.getWsProvider)(wsUrl);
180
180
  const papi = (0, import_polkadot_api.createClient)(provider);
181
181
  await Promise.race([
182
182
  papi._request("system_name", []),
@@ -353,20 +353,20 @@ var SubstrateClient = class _SubstrateClient {
353
353
  * into a PAPI UnsafeTransaction that can be signed and submitted.
354
354
  */
355
355
  async txFromCallData(callData) {
356
- return this._papi.getUnsafeApi().txFromCallData(import_polkadot_api.Binary.fromBytes(callData));
356
+ return this._papi.getUnsafeApi().txFromCallData(callData);
357
357
  }
358
358
  /**
359
359
  * Submits a pre-signed extrinsic (hex string) and waits for finalization.
360
360
  */
361
361
  async submit(signedHex) {
362
- return this._papi.submit(signedHex);
362
+ return this._papi.submit(import_polkadot_api.Binary.fromHex(signedHex));
363
363
  }
364
364
  /**
365
365
  * Submits a pre-signed extrinsic and returns an Observable of tx lifecycle events.
366
366
  * Events: TxSigned → TxBroadcasted → TxBestBlocksState → TxFinalized
367
367
  */
368
368
  submitAndWatch(signedHex) {
369
- return this._papi.submitAndWatch(signedHex);
369
+ return this._papi.submitAndWatch(import_polkadot_api.Binary.fromHex(signedHex));
370
370
  }
371
371
  /**
372
372
  * Submits a bare (unsigned) extrinsic hex and waits for finalization.
@@ -374,7 +374,7 @@ var SubstrateClient = class _SubstrateClient {
374
374
  * The bare tx hex is produced by `tx.getBareTx()` from polkadot-api.
375
375
  */
376
376
  async submitUnsignedAndWatch(bareTxHex) {
377
- return this._papi.submit(bareTxHex);
377
+ return this._papi.submit(import_polkadot_api.Binary.fromHex(bareTxHex));
378
378
  }
379
379
  /**
380
380
  * Convenience: wrap raw call bytes and sign+submit in one step.
@@ -1113,24 +1113,6 @@ var IndexerClient = class {
1113
1113
  }
1114
1114
  return res.json();
1115
1115
  }
1116
- async post(path, body) {
1117
- const controller = new AbortController();
1118
- const timer = setTimeout(() => controller.abort(), this.timeoutMs);
1119
- try {
1120
- const res = await fetch(`${this.baseUrl}${path}`, {
1121
- method: "POST",
1122
- headers: { "Content-Type": "application/json" },
1123
- body: JSON.stringify(body),
1124
- signal: controller.signal
1125
- });
1126
- if (!res.ok) {
1127
- throw new Error(`IndexerClient: HTTP ${res.status} for POST ${path}`);
1128
- }
1129
- return res.json();
1130
- } finally {
1131
- clearTimeout(timer);
1132
- }
1133
- }
1134
1116
  async getOrNull(path) {
1135
1117
  const res = await this._fetchResponse(path);
1136
1118
  if (res.status === 404) return null;
@@ -1194,16 +1176,17 @@ var IndexerClient = class {
1194
1176
  );
1195
1177
  }
1196
1178
  /**
1197
- * Batch-checks which of the given nullifiers are spent.
1198
- * Returns only the nullifiers that exist in the spent set.
1199
- * Accepts up to 100 nullifiers (0x-prefixed hex).
1179
+ * Downloads the full spent nullifier set and returns it as a Set of lowercase hex strings.
1180
+ *
1181
+ * The server sees an identical GET request regardless of which notes the wallet holds —
1182
+ * the intersection is computed locally (PIR-A privacy model).
1183
+ *
1184
+ * Suitable for wallets with up to ~1M spent nullifiers (~70 MB raw, <20 MB gzip).
1185
+ * For the current Orbinum testnet/mainnet scale this is the recommended approach.
1200
1186
  */
1201
- async getNullifiersBatch(nullifiers) {
1202
- if (nullifiers.length === 0) return [];
1203
- const res = await this.post("/shielded/nullifiers/batch", {
1204
- nullifiers: nullifiers.map((n) => n.toLowerCase())
1205
- });
1206
- return res.data;
1187
+ async getAllSpentNullifiers() {
1188
+ const res = await this.get("/shielded/nullifiers/all");
1189
+ return new Set(res.data.map((h) => h.toLowerCase()));
1207
1190
  }
1208
1191
  // ─── Private transfers ─────────────────────────────────────────────────────
1209
1192
  /**
@@ -1839,7 +1822,7 @@ var ShieldedPoolModule = class {
1839
1822
  asset_id: params.assetId,
1840
1823
  amount: params.amount,
1841
1824
  commitment: import_polkadot_api2.Binary.fromHex(params.commitment),
1842
- encrypted_memo: import_polkadot_api2.Binary.fromBytes(params.encryptedMemo)
1825
+ encrypted_memo: params.encryptedMemo
1843
1826
  });
1844
1827
  return toTxResult(
1845
1828
  await (txOptions !== void 0 ? tx.signAndSubmit(signer, txOptions) : tx.signAndSubmit(signer))
@@ -1859,12 +1842,12 @@ var ShieldedPoolModule = class {
1859
1842
  let changeEncryptedMemo;
1860
1843
  if (params.changeEncryptedMemo && params.changeEncryptedMemo.length > 0) {
1861
1844
  EncryptedMemo.validate(params.changeEncryptedMemo, "changeEncryptedMemo");
1862
- changeEncryptedMemo = import_polkadot_api2.Binary.fromBytes(params.changeEncryptedMemo);
1845
+ changeEncryptedMemo = params.changeEncryptedMemo;
1863
1846
  } else {
1864
- changeEncryptedMemo = import_polkadot_api2.Binary.fromBytes(new Uint8Array(0));
1847
+ changeEncryptedMemo = new Uint8Array(0);
1865
1848
  }
1866
1849
  const tx = callUnsafeTx(entry, {
1867
- proof: import_polkadot_api2.Binary.fromBytes(params.proof),
1850
+ proof: params.proof,
1868
1851
  merkle_root: import_polkadot_api2.Binary.fromHex(params.merkleRoot),
1869
1852
  nullifier: import_polkadot_api2.Binary.fromHex(params.nullifier),
1870
1853
  asset_id: params.assetId,
@@ -1897,11 +1880,11 @@ var ShieldedPoolModule = class {
1897
1880
  out.encryptedMemo,
1898
1881
  `privateTransfer.outputs[${i}].encryptedMemo`
1899
1882
  );
1900
- return import_polkadot_api2.Binary.fromBytes(out.encryptedMemo);
1883
+ return out.encryptedMemo;
1901
1884
  });
1902
1885
  const entry = resolveTx(this.substrate.unsafe, "ShieldedPool", "private_transfer");
1903
1886
  const tx = callUnsafeTx(entry, {
1904
- proof: import_polkadot_api2.Binary.fromBytes(params.proof),
1887
+ proof: params.proof,
1905
1888
  merkle_root: import_polkadot_api2.Binary.fromHex(params.merkleRoot),
1906
1889
  nullifiers,
1907
1890
  commitments,
@@ -1929,7 +1912,7 @@ var ShieldedPoolModule = class {
1929
1912
  assetId: item.assetId,
1930
1913
  amount: item.amount.toString(),
1931
1914
  commitment: import_polkadot_api2.Binary.fromHex(item.commitment),
1932
- encryptedMemo: import_polkadot_api2.Binary.fromBytes(item.encryptedMemo)
1915
+ encryptedMemo: item.encryptedMemo
1933
1916
  };
1934
1917
  });
1935
1918
  const entry = resolveTx(this.substrate.unsafe, "ShieldedPool", "shield_batch");
@@ -1952,9 +1935,9 @@ var ShieldedPoolModule = class {
1952
1935
  commitment: import_polkadot_api2.Binary.fromHex(params.commitment),
1953
1936
  amount: params.amount,
1954
1937
  asset_id: params.assetId,
1955
- encrypted_memo: import_polkadot_api2.Binary.fromBytes(params.encryptedMemo),
1956
- proof: import_polkadot_api2.Binary.fromBytes(params.proof),
1957
- public_signals: import_polkadot_api2.Binary.fromBytes(params.publicSignals)
1938
+ encrypted_memo: params.encryptedMemo,
1939
+ proof: params.proof,
1940
+ public_signals: params.publicSignals
1958
1941
  });
1959
1942
  return toTxResult(
1960
1943
  await (txOptions !== void 0 ? tx.signAndSubmit(signer, txOptions) : tx.signAndSubmit(signer))
@@ -2266,12 +2249,7 @@ var AccountMappingModule = class {
2266
2249
  */
2267
2250
  async addChainLink(params, signer) {
2268
2251
  const entry = resolveTx(this.substrate.unsafe, "accountMapping", "addChainLink");
2269
- const tx = callUnsafeTx(
2270
- entry,
2271
- params.chainId,
2272
- import_polkadot_api3.Binary.fromBytes(params.address),
2273
- import_polkadot_api3.Binary.fromBytes(params.signature)
2274
- );
2252
+ const tx = callUnsafeTx(entry, params.chainId, params.address, params.signature);
2275
2253
  return toTxResult(await tx.signAndSubmit(signer));
2276
2254
  }
2277
2255
  /**
@@ -2342,14 +2320,30 @@ var AccountMappingModule = class {
2342
2320
  entry,
2343
2321
  params.owner,
2344
2322
  params.chainId,
2345
- import_polkadot_api3.Binary.fromBytes(params.address),
2346
- import_polkadot_api3.Binary.fromBytes(params.signature),
2347
- import_polkadot_api3.Binary.fromBytes(params.callData)
2323
+ params.address,
2324
+ params.signature,
2325
+ params.callData
2348
2326
  );
2349
2327
  return toTxResult(await tx.signAndSubmit(signer));
2350
2328
  }
2351
2329
  };
2352
2330
 
2331
+ // src/rpc-v2/ChainModule.ts
2332
+ var ChainModule = class {
2333
+ constructor(substrate) {
2334
+ this.substrate = substrate;
2335
+ }
2336
+ substrate;
2337
+ /**
2338
+ * Returns `true` if the given SS58 account is an active Aura validator.
2339
+ *
2340
+ * Reads `pallet_aura::Authorities` directly from storage at the best known block.
2341
+ */
2342
+ async isValidator(ss58Address) {
2343
+ return this.substrate.request("chain_isValidator", [ss58Address]);
2344
+ }
2345
+ };
2346
+
2353
2347
  // src/rpc-v2/helpers.ts
2354
2348
  function mapAssetBalance(balance) {
2355
2349
  return {
@@ -3391,6 +3385,8 @@ var OrbinumClient = class _OrbinumClient {
3391
3385
  accountMapping;
3392
3386
  /** Typed access to `privacy_*` custom RPC endpoints. */
3393
3387
  privacy;
3388
+ /** Typed access to general chain state via `chain_*` custom RPC endpoints. */
3389
+ chain;
3394
3390
  /** Typed access to `zkVerifier_*` custom RPC endpoints. */
3395
3391
  zkVerifier;
3396
3392
  /** Typed access to `relayer_*` RPC endpoints (registry lookup and pending fee queries). */
@@ -3409,6 +3405,7 @@ var OrbinumClient = class _OrbinumClient {
3409
3405
  this.shieldedPool = new ShieldedPoolModule(substrate);
3410
3406
  this.accountMapping = new AccountMappingModule(substrate);
3411
3407
  this.privacy = new PrivacyModule(substrate);
3408
+ this.chain = new ChainModule(substrate);
3412
3409
  this.zkVerifier = new ZkVerifierModule(substrate);
3413
3410
  this.relayerStatus = new RelayerStatusModule(substrate);
3414
3411
  this.precompiles = evm ? {
package/dist/index.mjs CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  createClient,
4
4
  Binary
5
5
  } from "polkadot-api";
6
- import { getWsProvider } from "polkadot-api/ws-provider";
6
+ import { getWsProvider } from "polkadot-api/ws";
7
7
  import { getDynamicBuilder, getLookupFn } from "@polkadot-api/metadata-builders";
8
8
  import { decAnyMetadata, unifyMetadata } from "@polkadot-api/substrate-bindings";
9
9
  import { AccountId } from "@polkadot-api/substrate-bindings";
@@ -226,20 +226,20 @@ var SubstrateClient = class _SubstrateClient {
226
226
  * into a PAPI UnsafeTransaction that can be signed and submitted.
227
227
  */
228
228
  async txFromCallData(callData) {
229
- return this._papi.getUnsafeApi().txFromCallData(Binary.fromBytes(callData));
229
+ return this._papi.getUnsafeApi().txFromCallData(callData);
230
230
  }
231
231
  /**
232
232
  * Submits a pre-signed extrinsic (hex string) and waits for finalization.
233
233
  */
234
234
  async submit(signedHex) {
235
- return this._papi.submit(signedHex);
235
+ return this._papi.submit(Binary.fromHex(signedHex));
236
236
  }
237
237
  /**
238
238
  * Submits a pre-signed extrinsic and returns an Observable of tx lifecycle events.
239
239
  * Events: TxSigned → TxBroadcasted → TxBestBlocksState → TxFinalized
240
240
  */
241
241
  submitAndWatch(signedHex) {
242
- return this._papi.submitAndWatch(signedHex);
242
+ return this._papi.submitAndWatch(Binary.fromHex(signedHex));
243
243
  }
244
244
  /**
245
245
  * Submits a bare (unsigned) extrinsic hex and waits for finalization.
@@ -247,7 +247,7 @@ var SubstrateClient = class _SubstrateClient {
247
247
  * The bare tx hex is produced by `tx.getBareTx()` from polkadot-api.
248
248
  */
249
249
  async submitUnsignedAndWatch(bareTxHex) {
250
- return this._papi.submit(bareTxHex);
250
+ return this._papi.submit(Binary.fromHex(bareTxHex));
251
251
  }
252
252
  /**
253
253
  * Convenience: wrap raw call bytes and sign+submit in one step.
@@ -986,24 +986,6 @@ var IndexerClient = class {
986
986
  }
987
987
  return res.json();
988
988
  }
989
- async post(path, body) {
990
- const controller = new AbortController();
991
- const timer = setTimeout(() => controller.abort(), this.timeoutMs);
992
- try {
993
- const res = await fetch(`${this.baseUrl}${path}`, {
994
- method: "POST",
995
- headers: { "Content-Type": "application/json" },
996
- body: JSON.stringify(body),
997
- signal: controller.signal
998
- });
999
- if (!res.ok) {
1000
- throw new Error(`IndexerClient: HTTP ${res.status} for POST ${path}`);
1001
- }
1002
- return res.json();
1003
- } finally {
1004
- clearTimeout(timer);
1005
- }
1006
- }
1007
989
  async getOrNull(path) {
1008
990
  const res = await this._fetchResponse(path);
1009
991
  if (res.status === 404) return null;
@@ -1067,16 +1049,17 @@ var IndexerClient = class {
1067
1049
  );
1068
1050
  }
1069
1051
  /**
1070
- * Batch-checks which of the given nullifiers are spent.
1071
- * Returns only the nullifiers that exist in the spent set.
1072
- * Accepts up to 100 nullifiers (0x-prefixed hex).
1052
+ * Downloads the full spent nullifier set and returns it as a Set of lowercase hex strings.
1053
+ *
1054
+ * The server sees an identical GET request regardless of which notes the wallet holds —
1055
+ * the intersection is computed locally (PIR-A privacy model).
1056
+ *
1057
+ * Suitable for wallets with up to ~1M spent nullifiers (~70 MB raw, <20 MB gzip).
1058
+ * For the current Orbinum testnet/mainnet scale this is the recommended approach.
1073
1059
  */
1074
- async getNullifiersBatch(nullifiers) {
1075
- if (nullifiers.length === 0) return [];
1076
- const res = await this.post("/shielded/nullifiers/batch", {
1077
- nullifiers: nullifiers.map((n) => n.toLowerCase())
1078
- });
1079
- return res.data;
1060
+ async getAllSpentNullifiers() {
1061
+ const res = await this.get("/shielded/nullifiers/all");
1062
+ return new Set(res.data.map((h) => h.toLowerCase()));
1080
1063
  }
1081
1064
  // ─── Private transfers ─────────────────────────────────────────────────────
1082
1065
  /**
@@ -1712,7 +1695,7 @@ var ShieldedPoolModule = class {
1712
1695
  asset_id: params.assetId,
1713
1696
  amount: params.amount,
1714
1697
  commitment: Binary2.fromHex(params.commitment),
1715
- encrypted_memo: Binary2.fromBytes(params.encryptedMemo)
1698
+ encrypted_memo: params.encryptedMemo
1716
1699
  });
1717
1700
  return toTxResult(
1718
1701
  await (txOptions !== void 0 ? tx.signAndSubmit(signer, txOptions) : tx.signAndSubmit(signer))
@@ -1732,12 +1715,12 @@ var ShieldedPoolModule = class {
1732
1715
  let changeEncryptedMemo;
1733
1716
  if (params.changeEncryptedMemo && params.changeEncryptedMemo.length > 0) {
1734
1717
  EncryptedMemo.validate(params.changeEncryptedMemo, "changeEncryptedMemo");
1735
- changeEncryptedMemo = Binary2.fromBytes(params.changeEncryptedMemo);
1718
+ changeEncryptedMemo = params.changeEncryptedMemo;
1736
1719
  } else {
1737
- changeEncryptedMemo = Binary2.fromBytes(new Uint8Array(0));
1720
+ changeEncryptedMemo = new Uint8Array(0);
1738
1721
  }
1739
1722
  const tx = callUnsafeTx(entry, {
1740
- proof: Binary2.fromBytes(params.proof),
1723
+ proof: params.proof,
1741
1724
  merkle_root: Binary2.fromHex(params.merkleRoot),
1742
1725
  nullifier: Binary2.fromHex(params.nullifier),
1743
1726
  asset_id: params.assetId,
@@ -1770,11 +1753,11 @@ var ShieldedPoolModule = class {
1770
1753
  out.encryptedMemo,
1771
1754
  `privateTransfer.outputs[${i}].encryptedMemo`
1772
1755
  );
1773
- return Binary2.fromBytes(out.encryptedMemo);
1756
+ return out.encryptedMemo;
1774
1757
  });
1775
1758
  const entry = resolveTx(this.substrate.unsafe, "ShieldedPool", "private_transfer");
1776
1759
  const tx = callUnsafeTx(entry, {
1777
- proof: Binary2.fromBytes(params.proof),
1760
+ proof: params.proof,
1778
1761
  merkle_root: Binary2.fromHex(params.merkleRoot),
1779
1762
  nullifiers,
1780
1763
  commitments,
@@ -1802,7 +1785,7 @@ var ShieldedPoolModule = class {
1802
1785
  assetId: item.assetId,
1803
1786
  amount: item.amount.toString(),
1804
1787
  commitment: Binary2.fromHex(item.commitment),
1805
- encryptedMemo: Binary2.fromBytes(item.encryptedMemo)
1788
+ encryptedMemo: item.encryptedMemo
1806
1789
  };
1807
1790
  });
1808
1791
  const entry = resolveTx(this.substrate.unsafe, "ShieldedPool", "shield_batch");
@@ -1825,9 +1808,9 @@ var ShieldedPoolModule = class {
1825
1808
  commitment: Binary2.fromHex(params.commitment),
1826
1809
  amount: params.amount,
1827
1810
  asset_id: params.assetId,
1828
- encrypted_memo: Binary2.fromBytes(params.encryptedMemo),
1829
- proof: Binary2.fromBytes(params.proof),
1830
- public_signals: Binary2.fromBytes(params.publicSignals)
1811
+ encrypted_memo: params.encryptedMemo,
1812
+ proof: params.proof,
1813
+ public_signals: params.publicSignals
1831
1814
  });
1832
1815
  return toTxResult(
1833
1816
  await (txOptions !== void 0 ? tx.signAndSubmit(signer, txOptions) : tx.signAndSubmit(signer))
@@ -2139,12 +2122,7 @@ var AccountMappingModule = class {
2139
2122
  */
2140
2123
  async addChainLink(params, signer) {
2141
2124
  const entry = resolveTx(this.substrate.unsafe, "accountMapping", "addChainLink");
2142
- const tx = callUnsafeTx(
2143
- entry,
2144
- params.chainId,
2145
- Binary3.fromBytes(params.address),
2146
- Binary3.fromBytes(params.signature)
2147
- );
2125
+ const tx = callUnsafeTx(entry, params.chainId, params.address, params.signature);
2148
2126
  return toTxResult(await tx.signAndSubmit(signer));
2149
2127
  }
2150
2128
  /**
@@ -2215,14 +2193,30 @@ var AccountMappingModule = class {
2215
2193
  entry,
2216
2194
  params.owner,
2217
2195
  params.chainId,
2218
- Binary3.fromBytes(params.address),
2219
- Binary3.fromBytes(params.signature),
2220
- Binary3.fromBytes(params.callData)
2196
+ params.address,
2197
+ params.signature,
2198
+ params.callData
2221
2199
  );
2222
2200
  return toTxResult(await tx.signAndSubmit(signer));
2223
2201
  }
2224
2202
  };
2225
2203
 
2204
+ // src/rpc-v2/ChainModule.ts
2205
+ var ChainModule = class {
2206
+ constructor(substrate) {
2207
+ this.substrate = substrate;
2208
+ }
2209
+ substrate;
2210
+ /**
2211
+ * Returns `true` if the given SS58 account is an active Aura validator.
2212
+ *
2213
+ * Reads `pallet_aura::Authorities` directly from storage at the best known block.
2214
+ */
2215
+ async isValidator(ss58Address) {
2216
+ return this.substrate.request("chain_isValidator", [ss58Address]);
2217
+ }
2218
+ };
2219
+
2226
2220
  // src/rpc-v2/helpers.ts
2227
2221
  function mapAssetBalance(balance) {
2228
2222
  return {
@@ -3264,6 +3258,8 @@ var OrbinumClient = class _OrbinumClient {
3264
3258
  accountMapping;
3265
3259
  /** Typed access to `privacy_*` custom RPC endpoints. */
3266
3260
  privacy;
3261
+ /** Typed access to general chain state via `chain_*` custom RPC endpoints. */
3262
+ chain;
3267
3263
  /** Typed access to `zkVerifier_*` custom RPC endpoints. */
3268
3264
  zkVerifier;
3269
3265
  /** Typed access to `relayer_*` RPC endpoints (registry lookup and pending fee queries). */
@@ -3282,6 +3278,7 @@ var OrbinumClient = class _OrbinumClient {
3282
3278
  this.shieldedPool = new ShieldedPoolModule(substrate);
3283
3279
  this.accountMapping = new AccountMappingModule(substrate);
3284
3280
  this.privacy = new PrivacyModule(substrate);
3281
+ this.chain = new ChainModule(substrate);
3285
3282
  this.zkVerifier = new ZkVerifierModule(substrate);
3286
3283
  this.relayerStatus = new RelayerStatusModule(substrate);
3287
3284
  this.precompiles = evm ? {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orbinum/sdk",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "description": "Official TypeScript SDK for Orbinum.",
5
5
  "author": "Orbinum",
6
6
  "license": "MIT",
@@ -35,26 +35,26 @@
35
35
  "homepage": "https://github.com/orbinum/ts-sdk#readme",
36
36
  "devDependencies": {
37
37
  "@eslint/js": "10.0.1",
38
- "@types/node": "25.5.2",
39
- "eslint": "10.2.0",
40
- "prettier": "3.8.1",
38
+ "@types/node": "25.8.0",
39
+ "eslint": "10.4.0",
40
+ "prettier": "3.8.3",
41
41
  "ts-node": "10.9.2",
42
42
  "tsup": "8.5.1",
43
- "typescript": "6.0.2",
44
- "typescript-eslint": "8.58.0",
45
- "vitest": "4.1.3"
43
+ "typescript": "6.0.3",
44
+ "typescript-eslint": "8.59.3",
45
+ "vitest": "4.1.6"
46
46
  },
47
47
  "dependencies": {
48
- "@noble/ciphers": "2.1.1",
49
- "@noble/hashes": "2.0.1",
48
+ "@noble/ciphers": "2.2.0",
49
+ "@noble/hashes": "2.2.0",
50
50
  "@orbinum/proof-generator": "3.7.0",
51
- "@polkadot-api/metadata-builders": "0.13.9",
52
- "@polkadot-api/substrate-bindings": "0.17.0",
53
- "@polkadot-api/tx-utils": "0.2.2",
51
+ "@polkadot-api/metadata-builders": "0.14.2",
52
+ "@polkadot-api/substrate-bindings": "0.20.2",
53
+ "@polkadot-api/tx-utils": "0.3.2",
54
54
  "@polkadot/util-crypto": "14.0.3",
55
- "@scure/base": "2.0.0",
55
+ "@scure/base": "2.2.0",
56
56
  "@zk-kit/baby-jubjub": "1.0.3",
57
- "polkadot-api": "1.23.3",
57
+ "polkadot-api": "2.1.3",
58
58
  "poseidon-lite": "0.3.0"
59
59
  },
60
60
  "scripts": {