diff --git a/src/generic_app.ts b/src/generic_app.ts index 8be69b2..f7eb127 100644 --- a/src/generic_app.ts +++ b/src/generic_app.ts @@ -32,33 +32,57 @@ import { getSignReqChunks, P1_VALUES, } from "./common"; +import { supportedApps } from "./supported_apps"; + +const GenericAppName = "Polkadot"; interface TxMetadata { txMetadata: string; } -export function newGenericApp(transport: Transport, chainTicker: string, txMetadataSrvUrl: string): GenericApp { - return GenericApp.new(transport, chainTicker, txMetadataSrvUrl); +export function newPolkadotGenericApp( + transport: Transport, + chainId: string, + txMetadataSrvUrl: string, +): PolkadotGenericApp { + return PolkadotGenericApp.newApp(transport, chainId, txMetadataSrvUrl); +} +export function newPolkadotMigrationApp( + transport: Transport, + chainId: string, + cla: number, + sip0044: number, + txMetadataSrvUrl: string, +): PolkadotGenericApp { + return PolkadotGenericApp.newMigrationApp(transport, cla, sip0044, chainId, txMetadataSrvUrl); } -export class GenericApp { +export class PolkadotGenericApp { transport: Transport; cla: number; slip0044: number; txMetadataSrvUrl: string; - chainTicker: string; + chainId: string; + + static newApp(transport: Transport, chainId: string, txMetadataSrvUrl: string): PolkadotGenericApp { + const polkadotAppParams = supportedApps.find(({ name }) => name === GenericAppName); + if (polkadotAppParams === undefined) throw new Error("polkadot app params missed"); - static new(transport: Transport, chainTicker: string, txMetadataSrvUrl: string): GenericApp { - return new GenericApp(transport, 0x90, 0x80000162, chainTicker, txMetadataSrvUrl); + const { cla, slip0044 } = polkadotAppParams; + return new PolkadotGenericApp(transport, cla, slip0044, chainId, txMetadataSrvUrl); } - private constructor( + static newMigrationApp( transport: Transport, cla: number, slip0044: number, - chainTicker: string, + chainId: string, txMetadataSrvUrl: string, - ) { + ): PolkadotGenericApp { + return new PolkadotGenericApp(transport, cla, slip0044, chainId, txMetadataSrvUrl); + } + + private constructor(transport: Transport, cla: number, slip0044: number, chainId: string, txMetadataSrvUrl: string) { if (transport == null) { throw new Error("Transport has not been defined"); } @@ -66,16 +90,21 @@ export class GenericApp { this.cla = cla; this.slip0044 = slip0044; this.txMetadataSrvUrl = txMetadataSrvUrl; - this.chainTicker = chainTicker; + this.chainId = chainId; } async getTxMetadata(txBlob: Buffer): Promise { const resp = await axios.post(this.txMetadataSrvUrl, { txBlob: txBlob.toString("hex"), - chain: { id: this.chainTicker }, + chain: { id: this.chainId }, }); - return Buffer.from(resp.data.txMetadata, "hex"); + let txMetadata = resp.data.txMetadata; + if (txMetadata.slice(0, 2) === "0x") { + txMetadata = txMetadata.slice(2); + } + + return Buffer.from(txMetadata, "hex"); } async getVersion(): Promise { @@ -245,12 +274,16 @@ export class GenericApp { } } - async sign(account: number, change: number, addressIndex: number, blob: Buffer) { - const txMetadata = await this.getTxMetadata(blob); - return await this.signImpl(account, change, addressIndex, INS.SIGN, blob, txMetadata); + async sign(account: number, change: number, addressIndex: number, txBlob: Buffer) { + const txMetadata = await this.getTxMetadata(txBlob); + return await this.signImpl(account, change, addressIndex, INS.SIGN, txBlob, txMetadata); + } + + async signAdvanced(account: number, change: number, addressIndex: number, txBlob: Buffer, txMetadata: Buffer) { + return await this.signImpl(account, change, addressIndex, INS.SIGN, txBlob, txMetadata); } - async signRaw(account: number, change: number, addressIndex: number, blob: Buffer) { - return await this.signImpl(account, change, addressIndex, INS.SIGN_RAW, blob); + async signRaw(account: number, change: number, addressIndex: number, txBlob: Buffer) { + return await this.signImpl(account, change, addressIndex, INS.SIGN_RAW, txBlob); } } diff --git a/src/index.ts b/src/index.ts index 9e1b46c..9b5ad67 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,4 +18,4 @@ export * from "./legacy_apps"; export * from "./generic_app"; export { SubstrateApp } from "./substrate_app"; -export { newSubstrateApp, supportedApps } from "./supported_apps"; +export { newSubstrateApp, newMigrationGenericApp, supportedApps } from "./supported_apps"; diff --git a/src/supported_apps.ts b/src/supported_apps.ts index 5b680b2..b82e6a5 100644 --- a/src/supported_apps.ts +++ b/src/supported_apps.ts @@ -17,6 +17,7 @@ import { SubstrateApp } from "./substrate_app"; import { type SubstrateAppParams } from "./common"; import type Transport from "@ledgerhq/hw-transport"; +import { PolkadotGenericApp } from "./generic_app"; export function newSubstrateApp(transport: Transport, chainName: string) { const requestedApp = supportedApps.find((app: SubstrateAppParams) => { @@ -28,6 +29,22 @@ export function newSubstrateApp(transport: Transport, chainName: string) { throw new Error(`Error: ${chainName} not supported`); } +export function newMigrationGenericApp(transport: Transport, chainName: string, txMetadataSrvUrl: string) { + const requestedApp = supportedApps.find((app: SubstrateAppParams) => { + return app.name.toLowerCase() === chainName.toLowerCase(); + }); + if (requestedApp != null) { + return PolkadotGenericApp.newMigrationApp( + transport, + requestedApp.cla, + requestedApp.slip0044, + requestedApp.name, + txMetadataSrvUrl, + ); + } + throw new Error(`Error: ${chainName} not supported`); +} + export function getAppParams(chainName: string) { const params = supportedApps.find((app: SubstrateAppParams) => { return app.name.toLowerCase() === chainName.toLowerCase(); diff --git a/tests/integration_generic.test.ts b/tests/integration_generic.test.ts index d9acce9..e576b14 100644 --- a/tests/integration_generic.test.ts +++ b/tests/integration_generic.test.ts @@ -19,19 +19,19 @@ import { blake2bFinal, blake2bInit, blake2bUpdate } from "blakejs"; const ed25519 = require("ed25519-supercop"); -import { newGenericApp } from "../src/generic_app"; +import { newPolkadotGenericApp } from "../src/generic_app"; import { supportedApps } from "../src/supported_apps"; -import { AxiosError } from "axios"; +import Transport from "@ledgerhq/hw-transport"; const TX_METADATA_SRV_URL = "https://api.zondax.ch/polkadot/transaction/metadata"; -const CHAIN_NAME = "Kusama"; -const CHAIN_TICKER = "ksm"; +const CHAIN_NAME = "Polkadot"; +const CHAIN_TICKER = "dot"; const YOUR_PUBKEY = "d280b24dface41f31006e5a2783971fc5a66c862dd7d08f97603d2902b75e47a"; const YOUR_ADDRESS = "HLKocKgeGjpXkGJU6VACtTYJK4ApTCfcGRw51E5jWntcsXv"; const YOUR_BLOB = - "040000313233343536373839303132333435363738393031323334353637383930313233158139ae28a3dfaac5fe1560a5e9e05cd5038d2433158139ae28a3dfaac5fe1560a5e9e05c362400000c000000b0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafeb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe"; + "0000d050f0c8c0a9706b7c0c4e439245a347627901c89d4791239533d1d2c961f1a72ad615c8530de078e565ba644b38b01bcad249e8c0a80aceb4befe330990a59f74ed976c933db269c64dda40104a0f001900000091b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3a071db11cdbfd29285f25d402f1aee7a1c0384269c9c2edb476688d35e346998"; -let transport = {}; +let transport: Transport; jest.setTimeout(60000); @@ -41,8 +41,7 @@ beforeAll(async () => { describe("Integration", function () { test("get version", async () => { - // @ts-expect-error transport will be there - const app = newGenericApp(transport, CHAIN_TICKER, TX_METADATA_SRV_URL); + const app = newPolkadotGenericApp(transport, CHAIN_TICKER, TX_METADATA_SRV_URL); const resp = await app.getVersion(); console.log(resp); @@ -62,8 +61,7 @@ describe("Integration", function () { return; } - // @ts-expect-error transport will be there - const app = newGenericApp(transport, CHAIN_TICKER, TX_METADATA_SRV_URL); + const app = newPolkadotGenericApp(transport, CHAIN_TICKER, TX_METADATA_SRV_URL); const pathAccount = 0x80000000; const pathChange = 0x80000000; @@ -86,8 +84,7 @@ describe("Integration", function () { return; } - // @ts-expect-error transport will be there - const app = newGenericApp(transport, CHAIN_TICKER, TX_METADATA_SRV_URL); + const app = newPolkadotGenericApp(transport, CHAIN_TICKER, TX_METADATA_SRV_URL); const pathAccount = 0x80000000; const pathChange = 0x80000000; @@ -108,8 +105,7 @@ describe("Integration", function () { describe("Tx Metadata", () => { test("Success", async () => { - // @ts-expect-error transport will be there - const app = newGenericApp(transport, CHAIN_TICKER, TX_METADATA_SRV_URL); + const app = newPolkadotGenericApp(transport, CHAIN_TICKER, TX_METADATA_SRV_URL); const txBlob = Buffer.from(YOUR_BLOB, "hex"); const resp = await app.getTxMetadata(txBlob); @@ -118,8 +114,7 @@ describe("Integration", function () { }); test("Wrong/Invalid ticker", async () => { - // @ts-expect-error transport will be there - const app = newGenericApp(transport, "xxx", TX_METADATA_SRV_URL); + const app = newPolkadotGenericApp(transport, "xxx", TX_METADATA_SRV_URL); const txBlob = Buffer.from(YOUR_BLOB, "hex"); try { @@ -130,8 +125,7 @@ describe("Integration", function () { }); test("Empty/Wrong service url", async () => { - // @ts-expect-error transport will be there - const app = newGenericApp(transport, "ksm", ""); + const app = newPolkadotGenericApp(transport, "ksm", ""); const txBlob = Buffer.from(YOUR_BLOB, "hex"); try { @@ -151,8 +145,7 @@ describe("Integration", function () { const txBlob = Buffer.from(YOUR_BLOB, "hex"); - // @ts-expect-error transport will be there - const app = newGenericApp(transport, CHAIN_TICKER, TX_METADATA_SRV_URL); + const app = newPolkadotGenericApp(transport, CHAIN_TICKER, TX_METADATA_SRV_URL); const pathAccount = 0x80000000; const pathChange = 0x80000000;