Skip to content

Ledger Generic App signing fails due to spec_name mismatch #1605

@pilab-dohyeon

Description

@pilab-dohyeon

Description

Image

When signing transactions with the Ledger Generic App on chains where the display name (from system_chain RPC) differs from the actual spec_name (from state_getRuntimeVersion), the merkleizeMetadata function throws an error:

Error: Spec name not expected. Received <display_name> expected <spec_name>

Root Cause

In packages/extension-ui/src/Popup/Signing/LedgerSign.tsx, the getMetadataProof function passes chain.name.toLowerCase() as the specName parameter to merkleizeMetadata:

const merkleizedMetadata = merkleizeMetadata(m, {
  base58Prefix: chain.ss58Format,
  decimals: chain.tokenDecimals,
  specName: chain.name.toLowerCase(),  // <-- Problem: this is display name, not spec_name
  specVersion: chain.specVersion,
  tokenSymbol: chain.tokenSymbol
});

The chain.name value comes from the MetadataDef.chain field, which is populated from the system_chain RPC call. This returns the human-readable display name (e.g., "Bifrost Mainnet"), not the actual runtime spec_name (e.g., "thebifrost-mainnet").

The merkleizeMetadata function internally extracts the spec_name from the raw metadata's System.Version constant and validates it against the provided specName parameter. When they don't match, it throws an error.

Example

For Bifrost Network:

  • system_chain RPC returns: "Bifrost Mainnet" (display name)
  • state_getRuntimeVersion RPC returns: { specName: "thebifrost-mainnet", ... }
  • chain.name.toLowerCase() produces: "bifrost mainnet"
  • Expected by metadata: "thebifrost-mainnet"

Result: Error: Spec name not expected. Received bifrost mainnet expected thebifrost-mainnet

Proposed Solution

Option 1: Don't pass specName (let merkleizeMetadata extract it)

The merkleizeMetadata function can extract spec_name directly from the raw metadata if the specName parameter is not provided. The validation is skipped when specName is null or undefined.

const merkleizedMetadata = merkleizeMetadata(m, {
  base58Prefix: chain.ss58Format,
  decimals: chain.tokenDecimals,
  // specName omitted - extracted from rawMetadata automatically
  specVersion: chain.specVersion,
  tokenSymbol: chain.tokenSymbol
});

Option 2: Extract spec_name from metadata explicitly

Use @polkadot/types to parse the metadata and extract the actual spec_name:

import { Metadata, TypeRegistry } from '@polkadot/types';

function getSpecNameFromMetadata(rawMetadata: Uint8Array): string {
  const registry = new TypeRegistry();
  const metadata = new Metadata(registry, rawMetadata);
  registry.setMetadata(metadata);

  const version = metadata.asLatest.pallets
    .find(p => p.name.toString() === 'System')
    ?.constants
    .find(c => c.name.toString() === 'Version');

  if (version) {
    const versionType = registry.createType(
      registry.createLookupType(version.type),
      version.value
    );
    return (versionType as any).specName.toString();
  }

  throw new Error('Could not find spec_name in metadata');
}

Option 3: Store spec_name in MetadataDef

Add specName field to MetadataDef interface and populate it from state_getRuntimeVersion RPC when fetching metadata.

Affected Chains

Any chain where system_chain display name differs from spec_name:

  • Bifrost Network (mainnet, testnet, dev)
  • Potentially other chains with similar naming conventions

Environment

  • polkadot-js extension version: 0.62.6
  • @polkadot-api/merkleize-metadata: (bundled)
  • Ledger app: Generic App

Steps to Reproduce

  1. Add a Ledger account for a chain where display name differs from spec_name (e.g., Bifrost Network)
  2. Set Ledger app mode to "Generic" in extension settings
  3. Attempt to sign any transaction
  4. Observe the error: "Spec name not expected..."

Related

  • @polkadot-api/merkleize-metadata validation logic
  • MetadataDef interface in @polkadot/extension-inject

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions