-
Notifications
You must be signed in to change notification settings - Fork 453
Description
Description
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_chainRPC returns:"Bifrost Mainnet"(display name)state_getRuntimeVersionRPC 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
- Add a Ledger account for a chain where display name differs from spec_name (e.g., Bifrost Network)
- Set Ledger app mode to "Generic" in extension settings
- Attempt to sign any transaction
- Observe the error: "Spec name not expected..."
Related
@polkadot-api/merkleize-metadatavalidation logicMetadataDefinterface in@polkadot/extension-inject