Skip to content

Instantly share code, notes, and snippets.

@jsdw
Created July 11, 2023 14:35
Show Gist options
  • Save jsdw/0a27b8c98293ebd082d629d0b3e6d2e2 to your computer and use it in GitHub Desktop.
Save jsdw/0a27b8c98293ebd082d629d0b3e6d2e2 to your computer and use it in GitHub Desktop.
Signing a transaction with polkadot JS browser extension
import * as dapp from "@polkadot/extension-dapp";
import { Keyring } from "@polkadot/ui-keyring";
import {
mnemonicGenerate,
cryptoWaitReady,
signatureVerify,
decodeAddress,
} from "@polkadot/util-crypto";
import { stringToU8a, u8aToHex, stringToHex, hexToU8a } from "@polkadot/util";
import keyring from "@polkadot/ui-keyring";
import { ApiPromise, WsProvider } from "@polkadot/api";
//// This is the JSON format we need to pass to a signer:
// export interface SignerPayloadJSON {
// /** The ss-58 encoded address */
// address: string;
// /** The checkpoint hash of the block, in hex */
// blockHash: string;
// /** The checkpoint block number, in hex */
// blockNumber: string;
// /** The era for this transaction, in hex */
// era: string;
// /** The genesis hash of the chain, in hex */
// genesisHash: string;
// /** The encoded method (with arguments) in hex */
// method: string;
// /** The nonce for this transaction, in hex */
// nonce: string;
// /** The current spec version for the runtime */
// specVersion: string;
// /** The tip for this transaction, in hex */
// tip: string;
// /** The current transaction version for the runtime */
// transactionVersion: string;
// /** The applicable signed extensions for this runtime */
// signedExtensions: string[];
// /** The version of the extrinsic we are dealing with */
// version: number;
// }
await main();
async function main() {
await cryptoWaitReady();
await dapp.web3Enable("my cool dapp");
// In "real life" hopefully we can use:
//
// const allInjected = await dapp.web3Enable("my cool dapp");
// // get an account by some specific address; we can also search in other ways.
// const alice = await dapp.web3FromAddress("5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY");
// // use account to sign the payload provided.
// alice.signer.signPayload(payload)
//
// But Talisman seems to get stuck trying to work with local nodes (see
// https://github.com/TalismanSociety/talisman/issues/923), and PJS, for
// me at least, doesn't load properly (inc latest version), but the below
// line seems to prod it into working properly instead.
var pjs = await injectedWeb3["polkadot-js"].enable("my cool app");
// The payload format was obtained from starting a `polkadot --dev` node,
// and a dev version of the `polkadot-js/extension` (by going to about:debugging#addons
// in firefox and then loading `packages/extension/build/manifest.json` after doing an
// `yarn && npm run build` to actually build the thing). I tweaked the dev version to
// console.log the payload in the `signPayload` function so we could see what PJS UI
// handed it (I expect I could have added the log in PJS UI instead but hey ho).
//
// To get the payload values, I'd suggest plucking them out of Subxt at first:
// - specVersion and transactionVersion and genesisHash are available via `OfflineClient`.
// - `era` and `blockHash` and `blockNumber` I think are the mortality things; `blockHash`
// can be `genesisHash` or whatever the checkpoint block is, `blockNumber` must match,
// and `era` I'm not certain about offhand; can stick to immortal transactions though as below.
// - `method` is the hex encoded call data
// - `nonce` is the hex encoded account nonce
// - `signedExtensions` is the list of extensions which can be obtained from Metadata.
// - `tip` is a tip to give (I guess there's no way to tip in a different asset atm?).
// - `version` is 4; the tx protocol version.
//
// Would be cool if we could get PJS to automatically decode a signer payload into this,
// though that would then require PJS to be present, and doing it ourselves we can basically
// avoid that.
let { signature } = await pjs.signer.signPayload({
"specVersion": "0x000024d6",
"transactionVersion": "0x00000018",
"address": "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
"blockHash": "0xd7aad6185db012b7ffbce710b55234d6c9589170566b925ee50cfa3d7f1e6f8f",
"blockNumber": "0x00000000",
"era": "0x0000",
"genesisHash": "0xd7aad6185db012b7ffbce710b55234d6c9589170566b925ee50cfa3d7f1e6f8f",
"method": "0x0503001cbd2d43530a44705ad088af313e18f80b53ef16b36177cd4b77b846f2a5f07c0b00c465f14670",
"nonce": "0x00000000",
"signedExtensions": [
"CheckNonZeroSender",
"CheckSpecVersion",
"CheckTxVersion",
"CheckGenesis",
"CheckMortality",
"CheckNonce",
"CheckWeight",
"ChargeTransactionPayment",
"PrevalidateAttests"
],
"tip": "0x00000000000000000000000000000000",
"version": 4
})
// Signature seems to be a 65 byte thing, so probably already a MultiSignature ie the format we want.
console.log("signature:", signature)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment