@talismn/sapi 0.0.4 → 0.0.5

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.
@@ -0,0 +1,9 @@
1
+ type RpcSendFunc = <T>(method: string, params: unknown[], isCacheable?: boolean) => Promise<T>;
2
+ /**
3
+ * Fetches the highest supported version of metadata from the chain.
4
+ *
5
+ * @param rpcSend
6
+ * @returns hex-encoded metadata starting with the magic number
7
+ */
8
+ export declare const fetchBestMetadata: (rpcSend: RpcSendFunc, allowLegacyFallback?: boolean) => Promise<`0x${string}`>;
9
+ export {};
@@ -1,2 +1,3 @@
1
1
  export * from "./types";
2
2
  export * from "./sapi";
3
+ export * from "./fetchBestMetadata";
@@ -9,6 +9,7 @@ var utils = require('@polkadot-api/utils');
9
9
  var types = require('@polkadot/types');
10
10
  var merkleizeMetadata = require('@polkadot-api/merkleize-metadata');
11
11
  var substrateBindings = require('@polkadot-api/substrate-bindings');
12
+ var scaleTs = require('scale-ts');
12
13
 
13
14
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
14
15
 
@@ -530,4 +531,51 @@ const getScaleApi = (connector, hexMetadata, token, hasCheckMetadataHash, signed
530
531
  };
531
532
  };
532
533
 
534
+ const MAGIC_NUMBER = 1635018093;
535
+
536
+ // it's important to set a max because some chains also return high invalid version numbers in the metadata_versions list (ex on Polkadot, related to JAM?)
537
+ const MAX_SUPPORTED_METADATA_VERSION = 16;
538
+ /**
539
+ * Fetches the highest supported version of metadata from the chain.
540
+ *
541
+ * @param rpcSend
542
+ * @returns hex-encoded metadata starting with the magic number
543
+ */
544
+ const fetchBestMetadata = async (rpcSend, allowLegacyFallback) => {
545
+ try {
546
+ // fetch available versions of metadata
547
+ const metadataVersions = await rpcSend("state_call", ["Metadata_metadata_versions", "0x"], true);
548
+ const availableVersions = scaleTs.Vector(scaleTs.u32).dec(metadataVersions);
549
+ const bestVersion = Math.max(...availableVersions.filter(v => v <= MAX_SUPPORTED_METADATA_VERSION));
550
+ const metadata = await rpcSend("state_call", ["Metadata_metadata_at_version", utils.toHex(scaleTs.u32.enc(bestVersion))], true);
551
+ return normalizeMetadata(metadata);
552
+ } catch (cause) {
553
+ // if the chain doesnt support the Metadata pallet, fallback to legacy rpc provided metadata (V14)
554
+ const message = cause?.message;
555
+ if (allowLegacyFallback || message?.includes("is not found") ||
556
+ // ex: crust standalone
557
+ message?.includes("Module doesn't have export Metadata_metadata_versions") // ex: 3DPass
558
+ ) {
559
+ return await rpcSend("state_getMetadata", [], true);
560
+ }
561
+
562
+ // otherwise throw so it can be handled by the caller
563
+ throw new Error("Failed to fetch metadata", {
564
+ cause
565
+ });
566
+ }
567
+ };
568
+
569
+ /**
570
+ * Removes everything before the magic number in the metadata.
571
+ * This ensures Opaque metadata is usable by pjs
572
+ */
573
+ const normalizeMetadata = metadata => {
574
+ const hexMagicNumber = utils.toHex(scaleTs.u32.enc(MAGIC_NUMBER)).slice(2);
575
+ const magicNumberIndex = metadata.indexOf(hexMagicNumber);
576
+ if (magicNumberIndex === -1) throw new Error("Invalid metadata format: magic number not found");
577
+ return `0x${metadata.slice(magicNumberIndex)}`;
578
+ };
579
+
580
+ exports.fetchBestMetadata = fetchBestMetadata;
533
581
  exports.getScaleApi = getScaleApi;
@@ -9,6 +9,7 @@ var utils = require('@polkadot-api/utils');
9
9
  var types = require('@polkadot/types');
10
10
  var merkleizeMetadata = require('@polkadot-api/merkleize-metadata');
11
11
  var substrateBindings = require('@polkadot-api/substrate-bindings');
12
+ var scaleTs = require('scale-ts');
12
13
 
13
14
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
14
15
 
@@ -530,4 +531,51 @@ const getScaleApi = (connector, hexMetadata, token, hasCheckMetadataHash, signed
530
531
  };
531
532
  };
532
533
 
534
+ const MAGIC_NUMBER = 1635018093;
535
+
536
+ // it's important to set a max because some chains also return high invalid version numbers in the metadata_versions list (ex on Polkadot, related to JAM?)
537
+ const MAX_SUPPORTED_METADATA_VERSION = 16;
538
+ /**
539
+ * Fetches the highest supported version of metadata from the chain.
540
+ *
541
+ * @param rpcSend
542
+ * @returns hex-encoded metadata starting with the magic number
543
+ */
544
+ const fetchBestMetadata = async (rpcSend, allowLegacyFallback) => {
545
+ try {
546
+ // fetch available versions of metadata
547
+ const metadataVersions = await rpcSend("state_call", ["Metadata_metadata_versions", "0x"], true);
548
+ const availableVersions = scaleTs.Vector(scaleTs.u32).dec(metadataVersions);
549
+ const bestVersion = Math.max(...availableVersions.filter(v => v <= MAX_SUPPORTED_METADATA_VERSION));
550
+ const metadata = await rpcSend("state_call", ["Metadata_metadata_at_version", utils.toHex(scaleTs.u32.enc(bestVersion))], true);
551
+ return normalizeMetadata(metadata);
552
+ } catch (cause) {
553
+ // if the chain doesnt support the Metadata pallet, fallback to legacy rpc provided metadata (V14)
554
+ const message = cause?.message;
555
+ if (allowLegacyFallback || message?.includes("is not found") ||
556
+ // ex: crust standalone
557
+ message?.includes("Module doesn't have export Metadata_metadata_versions") // ex: 3DPass
558
+ ) {
559
+ return await rpcSend("state_getMetadata", [], true);
560
+ }
561
+
562
+ // otherwise throw so it can be handled by the caller
563
+ throw new Error("Failed to fetch metadata", {
564
+ cause
565
+ });
566
+ }
567
+ };
568
+
569
+ /**
570
+ * Removes everything before the magic number in the metadata.
571
+ * This ensures Opaque metadata is usable by pjs
572
+ */
573
+ const normalizeMetadata = metadata => {
574
+ const hexMagicNumber = utils.toHex(scaleTs.u32.enc(MAGIC_NUMBER)).slice(2);
575
+ const magicNumberIndex = metadata.indexOf(hexMagicNumber);
576
+ if (magicNumberIndex === -1) throw new Error("Invalid metadata format: magic number not found");
577
+ return `0x${metadata.slice(magicNumberIndex)}`;
578
+ };
579
+
580
+ exports.fetchBestMetadata = fetchBestMetadata;
533
581
  exports.getScaleApi = getScaleApi;
@@ -7,6 +7,7 @@ import { toHex, mergeUint8 } from '@polkadot-api/utils';
7
7
  import { TypeRegistry, Metadata } from '@polkadot/types';
8
8
  import { merkleizeMetadata } from '@polkadot-api/merkleize-metadata';
9
9
  import { enhanceEncoder, Bytes, u16 } from '@polkadot-api/substrate-bindings';
10
+ import { Vector, u32 } from 'scale-ts';
10
11
 
11
12
  var packageJson = {
12
13
  name: "@talismn/sapi"};
@@ -524,4 +525,50 @@ const getScaleApi = (connector, hexMetadata, token, hasCheckMetadataHash, signed
524
525
  };
525
526
  };
526
527
 
527
- export { getScaleApi };
528
+ const MAGIC_NUMBER = 1635018093;
529
+
530
+ // it's important to set a max because some chains also return high invalid version numbers in the metadata_versions list (ex on Polkadot, related to JAM?)
531
+ const MAX_SUPPORTED_METADATA_VERSION = 16;
532
+ /**
533
+ * Fetches the highest supported version of metadata from the chain.
534
+ *
535
+ * @param rpcSend
536
+ * @returns hex-encoded metadata starting with the magic number
537
+ */
538
+ const fetchBestMetadata = async (rpcSend, allowLegacyFallback) => {
539
+ try {
540
+ // fetch available versions of metadata
541
+ const metadataVersions = await rpcSend("state_call", ["Metadata_metadata_versions", "0x"], true);
542
+ const availableVersions = Vector(u32).dec(metadataVersions);
543
+ const bestVersion = Math.max(...availableVersions.filter(v => v <= MAX_SUPPORTED_METADATA_VERSION));
544
+ const metadata = await rpcSend("state_call", ["Metadata_metadata_at_version", toHex(u32.enc(bestVersion))], true);
545
+ return normalizeMetadata(metadata);
546
+ } catch (cause) {
547
+ // if the chain doesnt support the Metadata pallet, fallback to legacy rpc provided metadata (V14)
548
+ const message = cause?.message;
549
+ if (allowLegacyFallback || message?.includes("is not found") ||
550
+ // ex: crust standalone
551
+ message?.includes("Module doesn't have export Metadata_metadata_versions") // ex: 3DPass
552
+ ) {
553
+ return await rpcSend("state_getMetadata", [], true);
554
+ }
555
+
556
+ // otherwise throw so it can be handled by the caller
557
+ throw new Error("Failed to fetch metadata", {
558
+ cause
559
+ });
560
+ }
561
+ };
562
+
563
+ /**
564
+ * Removes everything before the magic number in the metadata.
565
+ * This ensures Opaque metadata is usable by pjs
566
+ */
567
+ const normalizeMetadata = metadata => {
568
+ const hexMagicNumber = toHex(u32.enc(MAGIC_NUMBER)).slice(2);
569
+ const magicNumberIndex = metadata.indexOf(hexMagicNumber);
570
+ if (magicNumberIndex === -1) throw new Error("Invalid metadata format: magic number not found");
571
+ return `0x${metadata.slice(magicNumberIndex)}`;
572
+ };
573
+
574
+ export { fetchBestMetadata, getScaleApi };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@talismn/sapi",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "author": "Talisman",
5
5
  "homepage": "https://talisman.xyz",
6
6
  "license": "GPL-3.0-or-later",
@@ -30,6 +30,7 @@
30
30
  "@polkadot/util": "13.5.1",
31
31
  "anylogger": "^1.0.11",
32
32
  "polkadot-api": "1.13.1",
33
+ "scale-ts": "^1.6.1",
33
34
  "@talismn/scale": "0.1.2"
34
35
  },
35
36
  "devDependencies": {
@@ -38,8 +39,8 @@
38
39
  "jest": "^29.7",
39
40
  "ts-jest": "^29.2.5",
40
41
  "typescript": "^5.6.3",
41
- "@talismn/eslint-config": "0.0.3",
42
- "@talismn/tsconfig": "0.0.2"
42
+ "@talismn/tsconfig": "0.0.2",
43
+ "@talismn/eslint-config": "0.0.3"
43
44
  },
44
45
  "eslintConfig": {
45
46
  "root": true,