diff --git a/docs-v2/openapi-v1.yaml b/docs-v2/openapi-v1.yaml
index 688d82b4c..17d6470f0 100755
--- a/docs-v2/openapi-v1.yaml
+++ b/docs-v2/openapi-v1.yaml
@@ -2667,6 +2667,30 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Error'
+ /transaction/metadata-blob:
+ post:
+ tags:
+ - transaction
+ summary: Get the metadata blob for offline signers.
+ description: Returns the minimal metadata ("metadata blob" or "proof") needed by offline
+ signers to decode a transaction's signing payload, along with the metadata
+ hash for the CheckMetadataHash signed extension per RFC-0078.
+ operationId: getTransactionMetadataBlob
+ requestBody:
+ $ref: '#/components/requestBodies/TransactionMetadataBlob'
+ responses:
+ "200":
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/TransactionMetadataBlob'
+ "400":
+ description: invalid request body or metadata V15 not available
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
/pallets/assets/{assetId}/asset-info:
get:
tags:
@@ -8406,6 +8430,74 @@ components:
- `RuntimeVersion`: https://crates.parity.io/sp_version/struct.RuntimeVersion.html
- `SignedExtension`: https://crates.parity.io/sp_runtime/traits/trait.SignedExtension.html
- FRAME Support: https://crates.parity.io/frame_support/metadata/index.html
+ MetadataBlobBody:
+ type: object
+ properties:
+ tx:
+ type: string
+ format: hex
+ description: Full encoded extrinsic as a hex string.
+ txAdditionalSigned:
+ type: string
+ format: hex
+ description: Optional additional signed data for the extrinsic. Used together with `tx`.
+ callData:
+ type: string
+ format: hex
+ description: Call data as hex string. Use this alongside includedInExtrinsic
+ and includedInSignedData instead of `tx`.
+ includedInExtrinsic:
+ type: string
+ format: hex
+ description: Signed extension data included in the extrinsic. Required when using callData.
+ includedInSignedData:
+ type: string
+ format: hex
+ description: Signed extension data included in the signature. Required when using callData.
+ at:
+ type: string
+ description: Block hash or number to query at. Defaults to finalized head.
+ format: unsignedInteger or $hex
+ description: >-
+ Request body for generating the metadata blob. You must provide either:
+ (1) `tx` (full extrinsic) with optional `txAdditionalSigned`, or
+ (2) `callData` with `includedInExtrinsic` and `includedInSignedData`.
+ TransactionMetadataBlob:
+ type: object
+ properties:
+ at:
+ $ref: '#/components/schemas/BlockIdentifiers'
+ metadataHash:
+ type: string
+ format: hex
+ description: The 32-byte metadata hash (hex-encoded) that should be used with
+ the CheckMetadataHash signed extension.
+ metadataBlob:
+ type: string
+ format: hex
+ description: The metadata blob (hex-encoded) containing the minimal metadata
+ required for offline signers to decode the transaction. This is a SCALE-encoded
+ Proof structure per RFC-0078.
+ specVersion:
+ type: number
+ description: The chain's spec version.
+ specName:
+ type: string
+ description: The chain's spec name.
+ base58Prefix:
+ type: number
+ description: The chain's SS58 address prefix.
+ decimals:
+ type: number
+ description: Number of decimals for the chain's native token.
+ tokenSymbol:
+ type: string
+ description: Symbol for the chain's native token.
+ description: >-
+ Response containing the metadata blob for offline signers. The metadataBlob
+ contains type definitions needed to decode the specific transaction, Merkle
+ proofs verifying these types are part of the full metadata, and extra chain
+ info.
TransactionPool:
type: object
properties:
@@ -8533,6 +8625,12 @@ components:
schema:
$ref: '#/components/schemas/DryRunBody'
required: true
+ TransactionMetadataBlob:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MetadataBlobBody'
+ required: true
ContractMetadata:
content:
application/json:
diff --git a/docs/src/openapi-v1.yaml b/docs/src/openapi-v1.yaml
index 688d82b4c..c5e87c982 100755
--- a/docs/src/openapi-v1.yaml
+++ b/docs/src/openapi-v1.yaml
@@ -2667,6 +2667,44 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Error'
+ /transaction/metadata-blob:
+ post:
+ tags:
+ - transaction
+ summary: Get the metadata blob for offline signers.
+ description: >-
+ Returns the minimal metadata ("metadata blob" or "proof") needed by offline
+ signers to decode a transaction's signing payload, along with the metadata
+ hash for the CheckMetadataHash signed extension per RFC-0078.
+
+
+ The metadata blob contains type definitions needed to decode the specific
+ transaction, Merkle proofs verifying these types are part of the full metadata,
+ and extra chain info (specVersion, specName, base58Prefix, decimals, tokenSymbol).
+
+
+ Offline signers can use this to decode the transaction to display what the user
+ is signing and verify the metadata subset matches the on-chain metadata via
+ Merkle proofs.
+
+
+ Reference: RFC-0078 https://polkadot-fellows.github.io/RFCs/approved/0078-merkleized-metadata.html
+ operationId: getTransactionMetadataBlob
+ requestBody:
+ $ref: '#/components/requestBodies/TransactionMetadataBlob'
+ responses:
+ "200":
+ description: successful operation
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/TransactionMetadataBlob'
+ "400":
+ description: invalid request body or metadata V15 not available
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
/pallets/assets/{assetId}/asset-info:
get:
tags:
@@ -8406,6 +8444,74 @@ components:
- `RuntimeVersion`: https://crates.parity.io/sp_version/struct.RuntimeVersion.html
- `SignedExtension`: https://crates.parity.io/sp_runtime/traits/trait.SignedExtension.html
- FRAME Support: https://crates.parity.io/frame_support/metadata/index.html
+ MetadataBlobBody:
+ type: object
+ properties:
+ tx:
+ type: string
+ format: hex
+ description: Full encoded extrinsic as a hex string.
+ txAdditionalSigned:
+ type: string
+ format: hex
+ description: Optional additional signed data for the extrinsic. Used together with `tx`.
+ callData:
+ type: string
+ format: hex
+ description: Call data as hex string. Use this alongside includedInExtrinsic
+ and includedInSignedData instead of `tx`.
+ includedInExtrinsic:
+ type: string
+ format: hex
+ description: Signed extension data included in the extrinsic. Required when using callData.
+ includedInSignedData:
+ type: string
+ format: hex
+ description: Signed extension data included in the signature. Required when using callData.
+ at:
+ type: string
+ description: Block hash or number to query at. Defaults to finalized head.
+ format: unsignedInteger or $hex
+ description: >-
+ Request body for generating the metadata blob. You must provide either:
+ (1) `tx` (full extrinsic) with optional `txAdditionalSigned`, or
+ (2) `callData` with `includedInExtrinsic` and `includedInSignedData`.
+ TransactionMetadataBlob:
+ type: object
+ properties:
+ at:
+ $ref: '#/components/schemas/BlockIdentifiers'
+ metadataHash:
+ type: string
+ format: hex
+ description: The 32-byte metadata hash (hex-encoded) that should be used with
+ the CheckMetadataHash signed extension.
+ metadataBlob:
+ type: string
+ format: hex
+ description: The metadata blob (hex-encoded) containing the minimal metadata
+ required for offline signers to decode the transaction. This is a SCALE-encoded
+ Proof structure per RFC-0078.
+ specVersion:
+ type: number
+ description: The chain's spec version.
+ specName:
+ type: string
+ description: The chain's spec name.
+ base58Prefix:
+ type: number
+ description: The chain's SS58 address prefix.
+ decimals:
+ type: number
+ description: Number of decimals for the chain's native token.
+ tokenSymbol:
+ type: string
+ description: Symbol for the chain's native token.
+ description: >-
+ Response containing the metadata blob for offline signers. The metadataBlob
+ contains type definitions needed to decode the specific transaction, Merkle
+ proofs verifying these types are part of the full metadata, and extra chain
+ info.
TransactionPool:
type: object
properties:
@@ -8533,6 +8639,12 @@ components:
schema:
$ref: '#/components/schemas/DryRunBody'
required: true
+ TransactionMetadataBlob:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/MetadataBlobBody'
+ required: true
ContractMetadata:
content:
application/json:
diff --git a/package.json b/package.json
index f0fb499ee..672a4831a 100644
--- a/package.json
+++ b/package.json
@@ -51,6 +51,7 @@
"test:test-release": "yarn build:scripts && node scripts/build/runYarnPack.js"
},
"dependencies": {
+ "@polkadot-api/merkleize-metadata": "^1.1.29",
"@polkadot/api": "16.5.2",
"@polkadot/api-augment": "16.5.2",
"@polkadot/api-contract": "16.5.2",
diff --git a/src/chains-config/assetHubKusamaControllers.ts b/src/chains-config/assetHubKusamaControllers.ts
index 5fd7bc47b..834b4d9b1 100644
--- a/src/chains-config/assetHubKusamaControllers.ts
+++ b/src/chains-config/assetHubKusamaControllers.ts
@@ -1,4 +1,4 @@
-// Copyright 2017-2025 Parity Technologies (UK) Ltd.
+// Copyright 2017-2026 Parity Technologies (UK) Ltd.
// This file is part of Substrate API Sidecar.
//
// Substrate API Sidecar is free software: you can redistribute it and/or modify
@@ -88,6 +88,7 @@ export const assetHubKusamaControllers: ControllerConfig = {
'TransactionDryRun',
'TransactionFeeEstimate',
'TransactionMaterial',
+ 'TransactionMetadataBlob',
'TransactionSubmit',
],
options: {
diff --git a/src/chains-config/assetHubNextWestendControllers.ts b/src/chains-config/assetHubNextWestendControllers.ts
index ef932c18e..2c004023a 100644
--- a/src/chains-config/assetHubNextWestendControllers.ts
+++ b/src/chains-config/assetHubNextWestendControllers.ts
@@ -1,4 +1,4 @@
-// Copyright 2017-2025 Parity Technologies (UK) Ltd.
+// Copyright 2017-2026 Parity Technologies (UK) Ltd.
// This file is part of Substrate API Sidecar.
//
// Substrate API Sidecar is free software: you can redistribute it and/or modify
@@ -86,6 +86,7 @@ export const assetHubNextWestendControllers: ControllerConfig = {
'TransactionDryRun',
'TransactionFeeEstimate',
'TransactionMaterial',
+ 'TransactionMetadataBlob',
'TransactionSubmit',
],
options: {
diff --git a/src/chains-config/assetHubPolkadotControllers.ts b/src/chains-config/assetHubPolkadotControllers.ts
index 41e86395d..cd1eecd1f 100644
--- a/src/chains-config/assetHubPolkadotControllers.ts
+++ b/src/chains-config/assetHubPolkadotControllers.ts
@@ -1,4 +1,4 @@
-// Copyright 2017-2025 Parity Technologies (UK) Ltd.
+// Copyright 2017-2026 Parity Technologies (UK) Ltd.
// This file is part of Substrate API Sidecar.
//
// Substrate API Sidecar is free software: you can redistribute it and/or modify
@@ -88,6 +88,7 @@ export const assetHubPolkadotControllers: ControllerConfig = {
'TransactionDryRun',
'TransactionFeeEstimate',
'TransactionMaterial',
+ 'TransactionMetadataBlob',
'TransactionSubmit',
],
options: {
diff --git a/src/chains-config/assetHubWestendControllers.ts b/src/chains-config/assetHubWestendControllers.ts
index bef457b15..34687a47a 100644
--- a/src/chains-config/assetHubWestendControllers.ts
+++ b/src/chains-config/assetHubWestendControllers.ts
@@ -1,4 +1,4 @@
-// Copyright 2017-2025 Parity Technologies (UK) Ltd.
+// Copyright 2017-2026 Parity Technologies (UK) Ltd.
// This file is part of Substrate API Sidecar.
//
// Substrate API Sidecar is free software: you can redistribute it and/or modify
@@ -87,6 +87,7 @@ export const assetHubWestendControllers: ControllerConfig = {
'TransactionDryRun',
'TransactionFeeEstimate',
'TransactionMaterial',
+ 'TransactionMetadataBlob',
'TransactionSubmit',
],
options: {
diff --git a/src/chains-config/defaultControllers.ts b/src/chains-config/defaultControllers.ts
index 59dd24fba..705a1146c 100644
--- a/src/chains-config/defaultControllers.ts
+++ b/src/chains-config/defaultControllers.ts
@@ -1,4 +1,4 @@
-// Copyright 2017-2025 Parity Technologies (UK) Ltd.
+// Copyright 2017-2026 Parity Technologies (UK) Ltd.
// This file is part of Substrate API Sidecar.
//
// Substrate API Sidecar is free software: you can redistribute it and/or modify
@@ -54,6 +54,7 @@ export const defaultControllers: ControllerConfig = {
'TransactionDryRun',
'TransactionFeeEstimate',
'TransactionMaterial',
+ 'TransactionMetadataBlob',
'TransactionSubmit',
],
options: {
diff --git a/src/chains-config/kusamaControllers.ts b/src/chains-config/kusamaControllers.ts
index 7433282e2..1710d922e 100644
--- a/src/chains-config/kusamaControllers.ts
+++ b/src/chains-config/kusamaControllers.ts
@@ -1,4 +1,4 @@
-// Copyright 2017-2025 Parity Technologies (UK) Ltd.
+// Copyright 2017-2026 Parity Technologies (UK) Ltd.
// This file is part of Substrate API Sidecar.
//
// Substrate API Sidecar is free software: you can redistribute it and/or modify
@@ -56,6 +56,7 @@ export const kusamaControllers: ControllerConfig = {
'TransactionDryRun',
'TransactionFeeEstimate',
'TransactionMaterial',
+ 'TransactionMetadataBlob',
'TransactionSubmit',
],
options: {
diff --git a/src/chains-config/polkadotControllers.ts b/src/chains-config/polkadotControllers.ts
index b14e46a9c..807d76d2d 100644
--- a/src/chains-config/polkadotControllers.ts
+++ b/src/chains-config/polkadotControllers.ts
@@ -1,4 +1,4 @@
-// Copyright 2017-2025 Parity Technologies (UK) Ltd.
+// Copyright 2017-2026 Parity Technologies (UK) Ltd.
// This file is part of Substrate API Sidecar.
//
// Substrate API Sidecar is free software: you can redistribute it and/or modify
@@ -55,6 +55,7 @@ export const polkadotControllers: ControllerConfig = {
'TransactionDryRun',
'TransactionFeeEstimate',
'TransactionMaterial',
+ 'TransactionMetadataBlob',
'TransactionSubmit',
],
options: {
diff --git a/src/chains-config/westendControllers.ts b/src/chains-config/westendControllers.ts
index 496879e20..40cc08527 100644
--- a/src/chains-config/westendControllers.ts
+++ b/src/chains-config/westendControllers.ts
@@ -1,4 +1,4 @@
-// Copyright 2017-2025 Parity Technologies (UK) Ltd.
+// Copyright 2017-2026 Parity Technologies (UK) Ltd.
// This file is part of Substrate API Sidecar.
//
// Substrate API Sidecar is free software: you can redistribute it and/or modify
@@ -55,6 +55,7 @@ export const westendControllers: ControllerConfig = {
'TransactionDryRun',
'TransactionFeeEstimate',
'TransactionMaterial',
+ 'TransactionMetadataBlob',
'TransactionSubmit',
],
options: {
diff --git a/src/controllers/index.ts b/src/controllers/index.ts
index 97d29b45c..c1a12d41a 100644
--- a/src/controllers/index.ts
+++ b/src/controllers/index.ts
@@ -1,4 +1,4 @@
-// Copyright 2017-2025 Parity Technologies (UK) Ltd.
+// Copyright 2017-2026 Parity Technologies (UK) Ltd.
// This file is part of Substrate API Sidecar.
//
// Substrate API Sidecar is free software: you can redistribute it and/or modify
@@ -81,7 +81,13 @@ import {
RcTransactionSubmit,
} from './rc/transaction';
import { RuntimeCode, RuntimeMetadata, RuntimeSpec } from './runtime';
-import { TransactionDryRun, TransactionFeeEstimate, TransactionMaterial, TransactionSubmit } from './transaction';
+import {
+ TransactionDryRun,
+ TransactionFeeEstimate,
+ TransactionMaterial,
+ TransactionMetadataBlob,
+ TransactionSubmit,
+} from './transaction';
/**
* Object containing every controller class definition.
*/
@@ -154,6 +160,7 @@ export const controllers = {
TransactionDryRun,
TransactionFeeEstimate,
TransactionMaterial,
+ TransactionMetadataBlob,
TransactionSubmit,
Paras,
ParasInclusion,
diff --git a/src/controllers/transaction/TransactionMetadataBlobController.ts b/src/controllers/transaction/TransactionMetadataBlobController.ts
new file mode 100644
index 000000000..f768fc132
--- /dev/null
+++ b/src/controllers/transaction/TransactionMetadataBlobController.ts
@@ -0,0 +1,97 @@
+// Copyright 2017-2026 Parity Technologies (UK) Ltd.
+// This file is part of Substrate API Sidecar.
+//
+// Substrate API Sidecar is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+import { TransactionMetadataBlobService } from '../../services';
+import { IMetadataBlobBody, IPostRequestHandler } from '../../types/requests';
+import AbstractController from '../AbstractController';
+
+/**
+ * POST metadata blob for a transaction.
+ *
+ * This endpoint generates the minimal metadata ("metadata blob" or "proof")
+ * needed by offline signers to decode a transaction's signing payload.
+ * It also returns the metadata hash that should be used with the
+ * CheckMetadataHash signed extension per RFC-0078.
+ *
+ * Request body:
+ * - Alternative 1: Full extrinsic
+ * - `tx`: Hex-encoded full extrinsic
+ * - `txAdditionalSigned`: (Optional) Hex-encoded additional signed data
+ *
+ * - Alternative 2: Extrinsic parts
+ * - `callData`: Hex-encoded call data
+ * - `includedInExtrinsic`: Hex-encoded signed extension extra data
+ * - `includedInSignedData`: Hex-encoded signed extension additional signed data
+ *
+ * - `at`: (Optional) Block hash or number. Defaults to finalized head.
+ *
+ * Returns:
+ * - `at`: Block context (hash and height)
+ * - `metadataHash`: The 32-byte metadata hash for CheckMetadataHash as hex
+ * - `metadataBlob`: The minimal metadata proof for offline signers as hex
+ * - `specVersion`: Runtime spec version
+ * - `specName`: Runtime spec name
+ * - `base58Prefix`: SS58 address prefix
+ * - `decimals`: Native token decimals
+ * - `tokenSymbol`: Native token symbol
+ *
+ * The `metadataBlob` contains:
+ * - Type definitions needed to decode the specific transaction
+ * - Merkle proofs verifying these types are part of the full metadata
+ * - Extra info (specVersion, specName, base58Prefix, decimals, tokenSymbol)
+ *
+ * Offline signers can use this to:
+ * 1. Decode the transaction to display what the user is signing
+ * 2. Verify the metadata subset matches the on-chain metadata via merkle proofs
+ *
+ * Reference:
+ * - RFC-0078: https://polkadot-fellows.github.io/RFCs/approved/0078-merkleized-metadata.html
+ * - Original issue: https://github.com/paritytech/substrate-api-sidecar/issues/1783
+ */
+export default class TransactionMetadataBlobController extends AbstractController {
+ static controllerName = 'TransactionMetadataBlob';
+ static requiredPallets = [];
+
+ constructor(api: string) {
+ super(api, '/transaction/metadata-blob', new TransactionMetadataBlobService(api));
+ this.initRoutes();
+ }
+
+ protected initRoutes(): void {
+ this.router.post(this.path, TransactionMetadataBlobController.catchWrap(this.getMetadataBlob));
+ }
+
+ /**
+ * POST handler for generating metadata blob.
+ */
+ private getMetadataBlob: IPostRequestHandler = async (
+ { body: { tx, txAdditionalSigned, callData, includedInExtrinsic, includedInSignedData, at } },
+ res,
+ ): Promise => {
+ const hash = await this.getHashFromAt(at);
+
+ TransactionMetadataBlobController.sanitizedSend(
+ res,
+ await this.service.fetchMetadataBlob(this.api, hash, {
+ tx,
+ txAdditionalSigned,
+ callData,
+ includedInExtrinsic,
+ includedInSignedData,
+ }),
+ );
+ };
+}
diff --git a/src/controllers/transaction/index.ts b/src/controllers/transaction/index.ts
index 0727b6b32..02968332a 100644
--- a/src/controllers/transaction/index.ts
+++ b/src/controllers/transaction/index.ts
@@ -1,4 +1,4 @@
-// Copyright 2017-2025 Parity Technologies (UK) Ltd.
+// Copyright 2017-2026 Parity Technologies (UK) Ltd.
// This file is part of Substrate API Sidecar.
//
// Substrate API Sidecar is free software: you can redistribute it and/or modify
@@ -17,4 +17,5 @@
export { default as TransactionDryRun } from './TransactionDryRunController';
export { default as TransactionFeeEstimate } from './TransactionFeeEstimateController';
export { default as TransactionMaterial } from './TransactionMaterialController';
+export { default as TransactionMetadataBlob } from './TransactionMetadataBlobController';
export { default as TransactionSubmit } from './TransactionSubmitController';
diff --git a/src/services/transaction/TransactionMetadataBlobService.spec.ts b/src/services/transaction/TransactionMetadataBlobService.spec.ts
new file mode 100644
index 000000000..deb1e4386
--- /dev/null
+++ b/src/services/transaction/TransactionMetadataBlobService.spec.ts
@@ -0,0 +1,332 @@
+// Copyright 2017-2026 Parity Technologies (UK) Ltd.
+// This file is part of Substrate API Sidecar.
+//
+// Substrate API Sidecar is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+import type { ApiPromise } from '@polkadot/api';
+import { ApiDecoration } from '@polkadot/api/types';
+import type { Hash } from '@polkadot/types/interfaces';
+import { BadRequest } from 'http-errors';
+
+import { polkadotMetadataRpcV1003000 } from '../../test-helpers/metadata/polkadotV1003000Metadata';
+import { polkadotRegistryV1003000 } from '../../test-helpers/registries';
+import { blockHash22887036 } from '../test-helpers/mock';
+import { TransactionMetadataBlobService } from './TransactionMetadataBlobService';
+
+// Mock the merkleize-metadata functions
+const mockDigest = jest
+ .fn()
+ .mockReturnValue(
+ new Uint8Array([
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32,
+ ]),
+ );
+const mockGetProofForExtrinsic = jest.fn().mockReturnValue(new Uint8Array([0xde, 0xad, 0xbe, 0xef]));
+const mockGetProofForExtrinsicParts = jest.fn().mockReturnValue(new Uint8Array([0xca, 0xfe, 0xba, 0xbe]));
+
+const mockMerkleized = {
+ digest: mockDigest,
+ getProofForExtrinsic: mockGetProofForExtrinsic,
+ getProofForExtrinsicParts: mockGetProofForExtrinsicParts,
+};
+
+const mockMerkleizeMetadata = jest.fn().mockReturnValue(mockMerkleized);
+
+// Mock metadata functions
+const mockMetadataAtVersion = () =>
+ Promise.resolve().then(() => {
+ return polkadotRegistryV1003000.createType('Option', polkadotMetadataRpcV1003000);
+ });
+
+const mockMetadataVersions = jest.fn().mockResolvedValue({
+ toJSON: jest.fn().mockReturnValue([14, 15]),
+});
+
+const mockMetadataVersionsNoV15 = jest.fn().mockResolvedValue({
+ toJSON: jest.fn().mockReturnValue([14]),
+});
+
+const mockHeader = {
+ number: {
+ unwrap: () => ({
+ toString: () => '22887036',
+ }),
+ },
+};
+
+const runtimeVersion = {
+ specName: polkadotRegistryV1003000.createType('Text', 'polkadot'),
+ specVersion: polkadotRegistryV1003000.createType('u32', 1003000),
+ transactionVersion: polkadotRegistryV1003000.createType('u32', 26),
+};
+
+const mockProperties = {
+ ss58Format: {
+ isSome: true,
+ value: {
+ toNumber: () => 0,
+ },
+ },
+ tokenDecimals: {
+ isSome: true,
+ value: {
+ toJSON: () => 10,
+ },
+ },
+ tokenSymbol: {
+ isSome: true,
+ value: {
+ toJSON: () => 'DOT',
+ },
+ },
+};
+
+const mockHistoricApi = {
+ call: {
+ metadata: {
+ metadataVersions: mockMetadataVersions,
+ metadataAtVersion: mockMetadataAtVersion,
+ },
+ },
+ registry: polkadotRegistryV1003000,
+} as unknown as ApiDecoration<'promise'>;
+
+const mockApi = {
+ ...mockHistoricApi,
+ rpc: {
+ chain: {
+ getHeader: () => Promise.resolve(mockHeader),
+ },
+ state: {
+ getRuntimeVersion: () => Promise.resolve(runtimeVersion),
+ },
+ system: {
+ properties: () => Promise.resolve(mockProperties),
+ },
+ },
+ at: (_hash: Hash) => Promise.resolve(mockHistoricApi),
+} as unknown as ApiPromise;
+
+describe('TransactionMetadataBlobService', () => {
+ let getMerkleizeMetadataSpy: jest.SpyInstance;
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ // Mock the getMerkleizeMetadata method on the prototype to return our mock
+ getMerkleizeMetadataSpy = jest
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ .spyOn(TransactionMetadataBlobService.prototype as any, 'getMerkleizeMetadata')
+ .mockResolvedValue(mockMerkleizeMetadata);
+ });
+
+ afterEach(() => {
+ getMerkleizeMetadataSpy.mockRestore();
+ });
+
+ describe('fetchMetadataBlob', () => {
+ it('should return metadata blob when given a full extrinsic (tx)', async () => {
+ const service = new TransactionMetadataBlobService('mock');
+ // timestamp.set()
+ const tx = '0x280403000b207eba5c8501';
+
+ const result = await service.fetchMetadataBlob(mockApi, blockHash22887036, { tx });
+
+ expect(result.at.hash).toEqual(blockHash22887036);
+ expect(result.at.height).toEqual('22887036');
+ expect(result.metadataHash).toMatch(/^0x[a-f0-9]{64}$/);
+ expect(result.metadataBlob).toMatch(/^0x[a-f0-9]+$/);
+ expect(result.specVersion).toEqual(1003000);
+ expect(result.specName).toEqual('polkadot');
+ expect(result.base58Prefix).toEqual(0);
+ expect(result.decimals).toEqual(10);
+ expect(result.tokenSymbol).toEqual('DOT');
+ expect(mockGetProofForExtrinsic).toHaveBeenCalledWith(tx, undefined);
+ });
+
+ it('should return metadata blob when given extrinsic parts', async () => {
+ const service = new TransactionMetadataBlobService('mock');
+ const params = {
+ callData: '0x0403',
+ includedInExtrinsic: '0x00',
+ includedInSignedData: '0x00',
+ };
+
+ const result = await service.fetchMetadataBlob(mockApi, blockHash22887036, params);
+
+ expect(result.at.hash).toEqual(blockHash22887036);
+ expect(result.metadataBlob).toMatch(/^0x[a-f0-9]+$/);
+ expect(mockGetProofForExtrinsicParts).toHaveBeenCalledWith(
+ params.callData,
+ params.includedInExtrinsic,
+ params.includedInSignedData,
+ );
+ });
+
+ it('should use txAdditionalSigned when provided with tx', async () => {
+ const service = new TransactionMetadataBlobService('mock');
+ // timestamp.set()
+ const tx = '0x280403000b207eba5c8501';
+ const txAdditionalSigned = '0x1234';
+
+ await service.fetchMetadataBlob(mockApi, blockHash22887036, { tx, txAdditionalSigned });
+
+ expect(mockGetProofForExtrinsic).toHaveBeenCalledWith(tx, txAdditionalSigned);
+ });
+
+ it('should throw BadRequest when neither tx nor callData is provided', async () => {
+ const service = new TransactionMetadataBlobService('mock');
+
+ await expect(service.fetchMetadataBlob(mockApi, blockHash22887036, {})).rejects.toThrow(BadRequest);
+ await expect(service.fetchMetadataBlob(mockApi, blockHash22887036, {})).rejects.toThrow(
+ 'Must provide either `tx` (full extrinsic) or `callData` with `includedInExtrinsic` and `includedInSignedData`.',
+ );
+ });
+
+ it('should throw BadRequest when callData is provided without includedInExtrinsic', async () => {
+ const service = new TransactionMetadataBlobService('mock');
+ const params = {
+ callData: '0x0403',
+ includedInSignedData: '0x00',
+ };
+
+ await expect(service.fetchMetadataBlob(mockApi, blockHash22887036, params)).rejects.toThrow(BadRequest);
+ await expect(service.fetchMetadataBlob(mockApi, blockHash22887036, params)).rejects.toThrow(
+ 'When using `callData`, must also provide `includedInExtrinsic` and `includedInSignedData`.',
+ );
+ });
+
+ it('should throw BadRequest when callData is provided without includedInSignedData', async () => {
+ const service = new TransactionMetadataBlobService('mock');
+ const params = {
+ callData: '0x0403',
+ includedInExtrinsic: '0x00',
+ };
+
+ await expect(service.fetchMetadataBlob(mockApi, blockHash22887036, params)).rejects.toThrow(BadRequest);
+ await expect(service.fetchMetadataBlob(mockApi, blockHash22887036, params)).rejects.toThrow(
+ 'When using `callData`, must also provide `includedInExtrinsic` and `includedInSignedData`.',
+ );
+ });
+
+ it('should throw BadRequest when V15 metadata is not available', async () => {
+ const mockHistoricApiNoV15 = {
+ call: {
+ metadata: {
+ metadataVersions: mockMetadataVersionsNoV15,
+ metadataAtVersion: mockMetadataAtVersion,
+ },
+ },
+ registry: polkadotRegistryV1003000,
+ } as unknown as ApiDecoration<'promise'>;
+
+ const mockApiNoV15 = {
+ ...mockApi,
+ at: (_hash: Hash) => Promise.resolve(mockHistoricApiNoV15),
+ } as unknown as ApiPromise;
+
+ const service = new TransactionMetadataBlobService('mock');
+ const tx = '0x280403000b207eba5c8501';
+
+ await expect(service.fetchMetadataBlob(mockApiNoV15, blockHash22887036, { tx })).rejects.toThrow(BadRequest);
+ await expect(service.fetchMetadataBlob(mockApiNoV15, blockHash22887036, { tx })).rejects.toThrow(
+ 'Metadata V15 is not available on this chain. CheckMetadataHash requires V15 metadata.',
+ );
+ });
+
+ it('should handle array token decimals and symbols', async () => {
+ const mockPropertiesArray = {
+ ss58Format: {
+ isSome: true,
+ value: {
+ toNumber: () => 2,
+ },
+ },
+ tokenDecimals: {
+ isSome: true,
+ value: {
+ toJSON: () => [12, 10],
+ },
+ },
+ tokenSymbol: {
+ isSome: true,
+ value: {
+ toJSON: () => ['KSM', 'DOT'],
+ },
+ },
+ };
+
+ const mockApiArray = {
+ ...mockApi,
+ rpc: {
+ ...mockApi.rpc,
+ system: {
+ properties: () => Promise.resolve(mockPropertiesArray),
+ },
+ },
+ } as unknown as ApiPromise;
+
+ const service = new TransactionMetadataBlobService('mock');
+ const tx = '0x280403000b207eba5c8501';
+
+ const result = await service.fetchMetadataBlob(mockApiArray, blockHash22887036, { tx });
+
+ expect(result.decimals).toEqual(12);
+ expect(result.tokenSymbol).toEqual('KSM');
+ expect(result.base58Prefix).toEqual(2);
+ });
+
+ it('should use default values when properties are not available', async () => {
+ const mockPropertiesEmpty = {
+ ss58Format: {
+ isSome: false,
+ },
+ tokenDecimals: {
+ isSome: false,
+ },
+ tokenSymbol: {
+ isSome: false,
+ },
+ };
+
+ const mockApiEmpty = {
+ ...mockApi,
+ rpc: {
+ ...mockApi.rpc,
+ system: {
+ properties: () => Promise.resolve(mockPropertiesEmpty),
+ },
+ },
+ } as unknown as ApiPromise;
+
+ const service = new TransactionMetadataBlobService('mock');
+ const tx = '0x280403000b207eba5c8501';
+
+ const result = await service.fetchMetadataBlob(mockApiEmpty, blockHash22887036, { tx });
+
+ expect(result.decimals).toEqual(10);
+ expect(result.tokenSymbol).toEqual('DOT');
+ expect(result.base58Prefix).toEqual(42);
+ });
+
+ it('should call merkleizeMetadata with correct parameters', async () => {
+ const service = new TransactionMetadataBlobService('mock');
+ const tx = '0x280403000b207eba5c8501';
+
+ await service.fetchMetadataBlob(mockApi, blockHash22887036, { tx });
+
+ expect(mockMerkleizeMetadata).toHaveBeenCalledWith(expect.any(Uint8Array), { decimals: 10, tokenSymbol: 'DOT' });
+ });
+ });
+});
diff --git a/src/services/transaction/TransactionMetadataBlobService.ts b/src/services/transaction/TransactionMetadataBlobService.ts
new file mode 100644
index 000000000..af376e975
--- /dev/null
+++ b/src/services/transaction/TransactionMetadataBlobService.ts
@@ -0,0 +1,185 @@
+// Copyright 2017-2026 Parity Technologies (UK) Ltd.
+// This file is part of Substrate API Sidecar.
+//
+// Substrate API Sidecar is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+import { ApiPromise } from '@polkadot/api';
+import { ApiDecoration } from '@polkadot/api/types';
+import type { BlockHash } from '@polkadot/types/interfaces';
+import { u8aToHex } from '@polkadot/util';
+import { BadRequest, InternalServerError } from 'http-errors';
+import { MetadataBlobParams } from 'src/types/requests';
+import { ITransactionMetadataBlob } from 'src/types/responses';
+
+import { AbstractService } from '../AbstractService';
+
+const DEFAULT_DECIMALS = 10;
+const DEFAULT_SS58_PREFIX = 42;
+const DEFAULT_TOKEN_SYMBOL = 'DOT';
+
+// Dynamic import helper that TypeScript won't transform to require()
+// This is necessary because @polkadot-api/merkleize-metadata is an ESM-only package
+// eslint-disable-next-line @typescript-eslint/no-implied-eval
+const dynamicImport = new Function('specifier', 'return import(specifier)') as (specifier: string) => Promise;
+
+// Cached module after first load
+let merkleizeMetadataModule: typeof import('@polkadot-api/merkleize-metadata') | null = null;
+
+async function loadMerkleizeMetadata(): Promise<
+ (typeof import('@polkadot-api/merkleize-metadata'))['merkleizeMetadata']
+> {
+ if (!merkleizeMetadataModule) {
+ merkleizeMetadataModule = await dynamicImport(
+ '@polkadot-api/merkleize-metadata',
+ );
+ }
+ return merkleizeMetadataModule.merkleizeMetadata;
+}
+
+export class TransactionMetadataBlobService extends AbstractService {
+ protected async getMerkleizeMetadata(): Promise<
+ (typeof import('@polkadot-api/merkleize-metadata'))['merkleizeMetadata']
+ > {
+ return loadMerkleizeMetadata();
+ }
+
+ /**
+ * Fetch metadata blob (proof) for a given transaction.
+ * This returns the minimal metadata needed by offline signers to decode
+ * the transaction, along with the metadata hash for CheckMetadataHash.
+ *
+ * @param api ApiPromise to use for the call
+ * @param hash `BlockHash` hash to query at
+ * @param params Request parameters
+ */
+ async fetchMetadataBlob(
+ api: ApiPromise,
+ hash: BlockHash,
+ params: MetadataBlobParams,
+ ): Promise {
+ if (!params.tx && !params.callData) {
+ throw new BadRequest(
+ 'Must provide either `tx` (full extrinsic) or `callData` with `includedInExtrinsic` and `includedInSignedData`.',
+ );
+ }
+
+ if (params.callData && (!params.includedInExtrinsic || !params.includedInSignedData)) {
+ throw new BadRequest(
+ 'When using `callData`, must also provide `includedInExtrinsic` and `includedInSignedData`.',
+ );
+ }
+
+ const historicApi = await api.at(hash);
+
+ const [header, version, properties, metadataV15Raw] = await Promise.all([
+ api.rpc.chain.getHeader(hash),
+ api.rpc.state.getRuntimeVersion(hash),
+ api.rpc.system.properties(),
+ this.fetchMetadataV15(historicApi),
+ ]);
+
+ const tokenDecimalsRaw = properties.tokenDecimals.isSome ? properties.tokenDecimals.value : null;
+ const tokenSymbolRaw = properties.tokenSymbol.isSome ? properties.tokenSymbol.value : null;
+ const ss58Format = properties.ss58Format.isSome ? properties.ss58Format.value : null;
+
+ let decimals = DEFAULT_DECIMALS;
+ if (tokenDecimalsRaw) {
+ const decimalsJson = tokenDecimalsRaw.toJSON();
+ if (Array.isArray(decimalsJson)) {
+ decimals = (decimalsJson[0] as number) ?? DEFAULT_DECIMALS;
+ } else if (typeof decimalsJson === 'number') {
+ decimals = decimalsJson;
+ }
+ }
+
+ let tokenSymbol = DEFAULT_TOKEN_SYMBOL;
+ if (tokenSymbolRaw) {
+ const symbolJson = tokenSymbolRaw.toJSON();
+ if (Array.isArray(symbolJson)) {
+ tokenSymbol = (symbolJson[0] as string) ?? DEFAULT_TOKEN_SYMBOL;
+ } else if (typeof symbolJson === 'string') {
+ tokenSymbol = symbolJson;
+ }
+ }
+
+ const base58Prefix = ss58Format?.toNumber() ?? DEFAULT_SS58_PREFIX;
+
+ // Use dynamic import for ESM module compatibility
+ const merkleizeMetadata = await this.getMerkleizeMetadata();
+ const merkleized = merkleizeMetadata(metadataV15Raw, {
+ decimals,
+ tokenSymbol,
+ });
+
+ const metadataHash = u8aToHex(merkleized.digest());
+
+ let metadataBlobBytes: Uint8Array;
+
+ if (params.tx) {
+ metadataBlobBytes = merkleized.getProofForExtrinsic(params.tx, params.txAdditionalSigned);
+ } else {
+ metadataBlobBytes = merkleized.getProofForExtrinsicParts(
+ params.callData!,
+ params.includedInExtrinsic!,
+ params.includedInSignedData!,
+ );
+ }
+
+ const metadataBlob = u8aToHex(metadataBlobBytes);
+
+ return {
+ at: {
+ hash,
+ height: header.number.unwrap().toString(10),
+ },
+ metadataHash,
+ metadataBlob,
+ specVersion: version.specVersion.toNumber(),
+ specName: version.specName.toString(),
+ base58Prefix,
+ decimals,
+ tokenSymbol,
+ };
+ }
+
+ /**
+ * Fetch V15 metadata from the chain.
+ * V15 metadata is required for RFC-0078 merkleization.
+ */
+ private async fetchMetadataV15(apiAt: ApiDecoration<'promise'>): Promise {
+ try {
+ const availableVersions = await apiAt.call.metadata.metadataVersions();
+ const versions = availableVersions.toJSON() as number[];
+
+ if (!versions.includes(15)) {
+ throw new BadRequest('Metadata V15 is not available on this chain. CheckMetadataHash requires V15 metadata.');
+ }
+
+ const metadataOpt = await apiAt.call.metadata.metadataAtVersion(15);
+
+ if (metadataOpt.isNone) {
+ throw new InternalServerError('Failed to fetch metadata V15 from the chain.');
+ }
+
+ return metadataOpt.unwrap().toU8a();
+ } catch (err) {
+ if (err instanceof BadRequest || err instanceof InternalServerError) {
+ throw err;
+ }
+ throw new InternalServerError(
+ `Failed to fetch metadata V15: ${err instanceof Error ? err.message : 'Unknown error'}`,
+ );
+ }
+ }
+}
diff --git a/src/services/transaction/index.ts b/src/services/transaction/index.ts
index d68170c6e..76b9a2ece 100644
--- a/src/services/transaction/index.ts
+++ b/src/services/transaction/index.ts
@@ -1,4 +1,4 @@
-// Copyright 2017-2025 Parity Technologies (UK) Ltd.
+// Copyright 2017-2026 Parity Technologies (UK) Ltd.
// This file is part of Substrate API Sidecar.
//
// Substrate API Sidecar is free software: you can redistribute it and/or modify
@@ -17,4 +17,5 @@
export * from './TransactionDryRunService';
export * from './TransactionFeeEstimateService';
export * from './TransactionMaterialService';
+export * from './TransactionMetadataBlobService';
export * from './TransactionSubmitService';
diff --git a/src/types/requests.ts b/src/types/requests.ts
index bb1187454..154361c0a 100644
--- a/src/types/requests.ts
+++ b/src/types/requests.ts
@@ -1,4 +1,4 @@
-// Copyright 2017-2025 Parity Technologies (UK) Ltd.
+// Copyright 2017-2026 Parity Technologies (UK) Ltd.
// This file is part of Substrate API Sidecar.
//
// Substrate API Sidecar is free software: you can redistribute it and/or modify
@@ -28,6 +28,64 @@ export interface ITx {
at: string;
}
+/**
+ * Body for the metadata blob request. Contains transaction data for generating
+ * the minimal metadata proof needed by offline signers.
+ */
+export interface IMetadataBlobBody {
+ /**
+ * Full encoded extrinsic as a hex string. Use this OR the individual components below.
+ */
+ tx?: string;
+ /**
+ * Optional additional signed data for the extrinsic.
+ * Used together with `tx`.
+ */
+ txAdditionalSigned?: string;
+ /**
+ * Call data as hex string. Use this alongside includedInExtrinsic and includedInSignedData.
+ */
+ callData?: string;
+ /**
+ * Signed Extension data included in the extrinsic.
+ */
+ includedInExtrinsic?: string;
+ /**
+ * Signed Extension data included in the signature.
+ */
+ includedInSignedData?: string;
+ /**
+ * Block hash or number to query at. Defaults to finalized head.
+ */
+ at?: string;
+}
+
+/**
+ * Parameters for generating metadata blob proof.
+ */
+export interface MetadataBlobParams {
+ /**
+ * Full encoded extrinsic. Use this OR the individual parts.
+ */
+ tx?: string;
+ /**
+ * Optional tx additional signed data.
+ */
+ txAdditionalSigned?: string;
+ /**
+ * Call data. Use with includedInExtrinsic and includedInSignedData.
+ */
+ callData?: string;
+ /**
+ * Signed Extension data included in the extrinsic.
+ */
+ includedInExtrinsic?: string;
+ /**
+ * Signed Extension data included in the signature.
+ */
+ includedInSignedData?: string;
+}
+
/**
* Body for the RequestHandlerContract. In other words, the body of the POST route that a message to a contract.
*/
diff --git a/src/types/responses/TransactionMetadataBlob.ts b/src/types/responses/TransactionMetadataBlob.ts
new file mode 100644
index 000000000..bb21bb1ce
--- /dev/null
+++ b/src/types/responses/TransactionMetadataBlob.ts
@@ -0,0 +1,28 @@
+// Copyright 2017-2026 Parity Technologies (UK) Ltd.
+// This file is part of Substrate API Sidecar.
+//
+// Substrate API Sidecar is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+import { IAt } from '.';
+
+export interface ITransactionMetadataBlob {
+ at: IAt;
+ metadataHash: string;
+ metadataBlob: string;
+ specVersion: number;
+ specName: string;
+ base58Prefix: number;
+ decimals: number;
+ tokenSymbol: string;
+}
diff --git a/src/types/responses/index.ts b/src/types/responses/index.ts
index a9e5777d1..f71c29a7c 100644
--- a/src/types/responses/index.ts
+++ b/src/types/responses/index.ts
@@ -1,4 +1,4 @@
-// Copyright 2017-2025 Parity Technologies (UK) Ltd.
+// Copyright 2017-2026 Parity Technologies (UK) Ltd.
// This file is part of Substrate API Sidecar.
//
// Substrate API Sidecar is free software: you can redistribute it and/or modify
@@ -67,4 +67,5 @@ export * from './SanitizedEventItemMetadata';
export * from './SanitizedStorageItemMetadata';
export * from './TransactionDryRun';
export * from './TransactionMaterial';
+export * from './TransactionMetadataBlob';
export * from './ValidateAddress';
diff --git a/yarn.lock b/yarn.lock
index 5f18a055e..a872534e6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1464,6 +1464,17 @@ __metadata:
languageName: node
linkType: hard
+"@polkadot-api/merkleize-metadata@npm:^1.1.29":
+ version: 1.1.29
+ resolution: "@polkadot-api/merkleize-metadata@npm:1.1.29"
+ dependencies:
+ "@polkadot-api/metadata-builders": "npm:0.13.9"
+ "@polkadot-api/substrate-bindings": "npm:0.17.0"
+ "@polkadot-api/utils": "npm:0.2.0"
+ checksum: 10/1ad9356b6345c6ed234c9c4f766dca43647a2b9d4bbd9bd3a04c9ef26e9fcce12343753c7ff400b3d53930cae7a48a859f7e774070ad2cdc9332fe52baa284a3
+ languageName: node
+ linkType: hard
+
"@polkadot-api/metadata-builders@npm:0.13.7":
version: 0.13.7
resolution: "@polkadot-api/metadata-builders@npm:0.13.7"
@@ -1474,6 +1485,16 @@ __metadata:
languageName: node
linkType: hard
+"@polkadot-api/metadata-builders@npm:0.13.9":
+ version: 0.13.9
+ resolution: "@polkadot-api/metadata-builders@npm:0.13.9"
+ dependencies:
+ "@polkadot-api/substrate-bindings": "npm:0.17.0"
+ "@polkadot-api/utils": "npm:0.2.0"
+ checksum: 10/4935e492aa91c82d06cd19a5b3ad9059d61d4b75416bd582398645fe0334b7d9e1ec26c7e8c1a3674752f86e70b9872a221047849007b7dfdfc0df6ebc5af7a5
+ languageName: node
+ linkType: hard
+
"@polkadot-api/metadata-builders@npm:0.3.2":
version: 0.3.2
resolution: "@polkadot-api/metadata-builders@npm:0.3.2"
@@ -1620,6 +1641,18 @@ __metadata:
languageName: node
linkType: hard
+"@polkadot-api/substrate-bindings@npm:0.17.0":
+ version: 0.17.0
+ resolution: "@polkadot-api/substrate-bindings@npm:0.17.0"
+ dependencies:
+ "@noble/hashes": "npm:^2.0.1"
+ "@polkadot-api/utils": "npm:0.2.0"
+ "@scure/base": "npm:^2.0.0"
+ scale-ts: "npm:^1.6.1"
+ checksum: 10/dcf75f2db092b6247530a26791446da3a8512a62887d30b6f3173a9f74f890287e7df8f692c23e2fd620f508d7321f89d7580083a3065b1a783eeb9f6ba5dc34
+ languageName: node
+ linkType: hard
+
"@polkadot-api/substrate-bindings@npm:0.6.0":
version: 0.6.0
resolution: "@polkadot-api/substrate-bindings@npm:0.6.0"
@@ -2465,6 +2498,7 @@ __metadata:
resolution: "@substrate/api-sidecar@workspace:."
dependencies:
"@acala-network/chopsticks-testing": "npm:^1.2.5"
+ "@polkadot-api/merkleize-metadata": "npm:^1.1.29"
"@polkadot/api": "npm:16.5.2"
"@polkadot/api-augment": "npm:16.5.2"
"@polkadot/api-contract": "npm:16.5.2"