# Deprecate `signPayload` in favour of new `createTransaction` ## đŸ’„ The Signer Interface Revolution: `createTransaction` This document summarizes the transition from the legacy `signPayload` interface to the new, future-proof `createTransaction` standard for Substrate-based chains. ## 📝 Summary of Change The existing `signPayload` function is an outdated standard that couples DApps to the internal implementation details of `@polkadot/api`, lacks support for modern features, and breaks when chain extensions change. The proposed `createTransaction` is a new, rigorous interface designed to restore flexibility and interoperability. It shifts the responsibility of final extrinsic construction (including handling complex features like custom and versioned extensions) from the DApp layer back to the wallet/signer layer, making it resilient to chain upgrades and future-proof for Extrinsic V5. ## 🎯 Goal: A New Standard The ultimate goal is to introduce a well-specified, forward-compatible function that achieves: * **Interoperability:** Enables seamless communication between different SDKs, wallets, and tooling (e.g., Ledger, Talisman, SubWallet). * **Modern Extrinsic Support:** Full support for Extrinsic V4 and the next-generation Extrinsic V5 (including General transactions and [RFC-99](https://polkadot-fellows.github.io/RFCs/approved/0099-transaction-extension-version.html) versioned extensions). * **Custom Extension Support:** Allows chain developers to define custom extensions in their metadata without requiring bespoke wallet updates. ## 🛠 Required Implementation Steps This is a full ecosystem change requiring coordinated updates across three main areas: ### 1. PolkadotJS API (Access Layer) The PolkadotJS API must be updated to manage the new signing flow: * **Feature Detection:** Implement logic in the `signAndSend` pipeline to check if the connected signer exposes `createTransaction`. * **New Payload Generation:** Construct the verbose `TxPayload` structure, including the full chain metadata and an explicit breakdown of all required `extensions` (`id`, `extra`, `additionalSigned`). * **Fallback:** If `createTransaction` is not found on the signer, gracefully fall back to generating and sending the legacy `signPayload`. ### 2. Wallets / Signers (Extension Layer) Wallets (like extensions, hardware, and offline signers) must implement the `createTransaction` method in order to use it: * **Implement `createTransaction(input: TxPayload)`:** This function must consume the `TxPayload` input. * Transaction Assembly (Crucial Shift): The wallet is now responsible for: * Decentralizing: Decoding the `callData` and provided `extensions`. * Signing: Generating the signature based on the data. * **Final Assembly:** Taking the call data, signed extensions, and the signature to produce the final, complete, SCALE-encoded extrinsic (HexString), ready for broadcast. * **User Interface:** Update UI to correctly display the human-readable details from the new, detailed, and flexible `extensions` array and `context`. ### 3. DApps / Ecosystem Projects (Consumers) * **Upgrade Dependencies:** Upgrade `@polkadot/api` and related libraries to enable the new feature-detection logic. * **Context Provision:** Ensure the application can fetch and provide the latest `context.metadata` to the API when creating the transaction payload. ## 🚀 Proposed TS Interface ```ts type HexString = `0x${string}`; export interface TxPayloadV1 { /** * @description Payload version. MUST be 1. */ version: 1; /** * @description Signer selection hint. Allows the implementer to identify which private-key. (e.g., SS58 address, account-name). * The DApp MUST provide the SS58 address of the intended signer. * This is used by the implementer to identify the correct key/account to use. */ signer: string | null; /** * @description SCALE-encoded Call (module indicator + function indicator + params) */ callData: HexString; /** * @description Transaction extensions supplied by the caller (order irrelevant). * The consumer (DApp/API) is responsible for calculating and providing all components. * The implementer (Signer) MAY attempt to infer missing ones if necessary. */ extensions: Array<{ /** Identifier as defined in metadata (e.g., "CheckSpecVersion", "ChargeAssetTxPayment"). */ id: string; /** * Explicit "extra" to sign (goes into the extrinsic body). * SCALE-encoded per the extension's "extra" type as defined in the metadata. */ extra: HexString; /** * "Implicit" data to sign (known by the chain, not included into the extrinsic body). * SCALE-encoded per the extension's "additionalSigned" type as defined in the metadata. */ additionalSigned: HexString; }>; /** * @description Transaction Extension Version * - MUST be 0 for Extrinsic V4 transactions. * - Set to a runtime-supported version (> 0) for Extrinsic V5 transactions. * * The implementer: * - MUST use this field to determine the required extensions for creating the extrinsic. * - MAY use this field to infer missing extensions that the implementer could know how to handle. */ txExtVersion: number; /** * @description Context needed for decoding, display, construction and (optionally) inferring certain extensions. */ context: { /** * RuntimeMetadataPrefixed blob (SCALE), starting with ASCII "meta" magic (`0x6d657461`). * Must be V14+. For V5+ versioned extensions, MUST provide V16+. */ metadata: HexString; /* The chain's genesis blockhash */ genesisHash: HexString; /** * Native token display info (used by some implementers), also needed to compute * the `CheckMetadataHash` value. */ tokenSymbol: string; tokenDecimals: number; /** * Highest known block number to aid mortality UX. */ bestBlockHeight: number; }; } export interface Signer { /** * @description Creates a SCALE-encoded extrinsic (ready to broadcast). */ createTransaction?: (input: TxPayload) => Promise<HexString>; ..... ..... } ``` ## 📍 Scope of Work * **Implement Signer Feature Detection:** Update the `#signViaSigner` internal method within the API to: * Check for the presence of the new `createTransaction` method on the injected `Signer` object. * If present, prioritize calling `createTransaction` and treat the result as the final, ready-to-broadcast extrinsic (`HexString`). * If not present, fall back to the legacy flow, calling `signPayload` and performing the signature-attachment and final serialization within the API. * **Implement Payload Builder:** Create a internal helper method, `#createTxPayloadV1`, responsible for: * Calculating and assembling the complete `TxPayloadV1` object from the signature options. * Ensuring the full, current chain metadata is retrieved and included in `context.metadata`. * Determining and correctly setting the `txExtVersion` based on chain capabilities or provided options. * Populating the comprehensive `extensions` array with accurate `id`, `extra`, and `additionalSigned` fields. * Export the necessary types, most importantly the `TxPayloadV1` interface, to allow external signers and DApps to easily integrate. * Write comprehensive unit and integration tests to cover the new signing flow: * Success Path: Test a mock signer implementing `createTransaction` that returns a valid `V4` or `V5` extrinsic. * Fallback Path: Test a mock signer that only implements `signPayload` to confirm the legacy fallback works correctly. * V5 Feature Test: Test constructing a payload that utilizes versioned extensions (e.g., `txExtVersion` > 0) to validate correct payload generation. * **Documentation:** Update the relevant developer documentation to explain the preferred use of `createTransaction` and the necessary input format. ## 👇 Files Likely to be Updated 1. [packages/types](https://github.com/polkadot-js/api/tree/master/packages/types) * *src/types/extrinsic.ts* * *src/types/index.ts* 2. [packages/api](https://github.com/polkadot-js/api/tree/master/packages/api) * *src/submittable/createClass.ts* * *src/promise/createTransaction.spec.ts* (related tests) --- *Note: Thanks to https://github.com/josepot for taking this initiative!*