Recently, Basti published the merkleized-metadata Rust crate, which implements Polkadot RFC-78.
I have now published JavaScript/TypeScript bindings to this crate which are available using npm i merkleized-metadata
and should work in browsers or NodeJS (see the browser example here and the NodeJS example here).
The interface is similar to the Rust crate, and looks like this:
import { init, RuntimeMetadata } from 'merkleized-metadata'
// Initialize the package:
const mm = await init();
console.log("Initialized")
// Build our metadata object from the hex bytes obtained from
// state.getMetadata or the runtime API metadata.metadata_at_version(15):
const runtimeMetadata = RuntimeMetadata.fromHex(METADATA);
// Calculate the metadata digest and then hash it to get the metadata hash
// that we'd add to the signer payload for the CheckMetadataHash extension:
const digest = mm.generateMetadataDigest(runtimeMetadata, {
base58Prefix: BASE58_PREFIX, // Eg 0 for Polkadot, 42 for Substrate
decimals: DECIMALS, // Eg 10 for Polkadot
specName: SPEC_NAME, // Eg "polkadot"
specVersion: SPEC_VERSION, // Eg 1_002_004 for Polkadot 1.2.4
tokenSymbol: TOKEN_SYMBOL // Eg "DOT"
});
console.log("Metadata Hash:", digest.hash())
// We can also build a proof which contains the information needed to
// decode a given extrinsic. This would be sent to devices like ledgers along
// with the above hash so that they could decode and use it to display an extrinsic.
const proof = mm.generateProofForExtrinsic(
TX, // Hex for the transaction bytes
TX_ADDITIONAL_SIGNED, // The bytes that extensions add to the signer payload (optional)
runtimeMetadata
);
console.log("Extrinsic proof:", proof.encode())
If you already understand the reasons for this merkleized-metadata work, then this is likely everything you’d like to know!
If you’d like to know more, here’s a mini-FAQ:
Why do we want this merkleized metadata stuff?
RFC-78 covers this in more detail, but to summarize, the main problems being solved with the merkleized-metadata work are:
- Ensuring that the metadata used to decode and display a transaction to some user who will sign it matches the current on chain metadata (or more accurately: rejecting transactions where this is not the case).
- Enabling memory constrained devices like Ledgers to display information about arbitrary transactions without them having to store the full uptodate metadata (which can each be hundreds of kilobytes in size) for all chains of interest.
The first problem is solved by adding a CheckMetadataHash
signed extension to the chain, which transactions can opt into using. Any transaction that opts in must include a metadata hash as part of the transaction’s signer payload. The idea here is that any app which decodes and displays the transaction to a user can hash the metadata used to decode it and add it to the signer payload (or confirm that it is already added and correct) before signing it. This protects the app from using incorrect metadata to decode and display a transaction: if the metadata hash differs from the one known on chain, the transaction will be rejected.
The second problem is solved by constructing a proof for a specific extrinsic. This proof contains the information needed to decode and display the transaction, as well as the information needed to generate the same metadata hash that we can generate from our full metadata. This proof is much much smaller than the full metadata. The idea here is that we can send just the proof along with some transaction to a device like a Ledger, and it can then use the proof to display the transaction to users before they sign it, as well as verifying that the metadata hash in the signer payload is correct. The device no longer needs to keep a full copy of any metadata.
Why do we need a TypeScript wrapper?
Two of the concrete workflows that we want to support here are:
- Sending the proof + transaction to a Ledger (or similar) device from some wallet browser extension (this is something that Ledger devices will require to sign transactions when the Polkadot app update lands ~July 1st, and already requires on Kusama).
- Allowing wallets to verify that the metadata that they will use to decode and show transactions to users is correct.
For (1), we need to generate a proof given a transaction and some full metadata. For (2), we need to be able to generate the metadata hash so that we can add/check it in the signer payload. For both, we need a library written in JavaScript so that browser extensions can use it. Hence, this wrapper!