diff --git a/apps/extension/src/Setup/Ledger/LedgerConnect.tsx b/apps/extension/src/Setup/Ledger/LedgerConnect.tsx index 618a40a3a7..81c2d1aca3 100644 --- a/apps/extension/src/Setup/Ledger/LedgerConnect.tsx +++ b/apps/extension/src/Setup/Ledger/LedgerConnect.tsx @@ -59,8 +59,8 @@ export const LedgerConnect: React.FC = ({ if (returnCode !== LedgerError.NoErrors) { throw new Error(errorMessage); } - const canImportShielded = await ledger?.isZip32Supported(); - setIsZip32Supported(canImportShielded); + const isMaspSupported = await ledger?.isZip32Supported(); + setIsZip32Supported(isMaspSupported); setIsLedgerConnecting(true); setCurrentApprovalStep(1); @@ -68,7 +68,7 @@ export const LedgerConnect: React.FC = ({ makeBip44Path(chains.namada.bip44.coinType, bip44Path) ); - if (canImportShielded) { + if (isMaspSupported) { // Import Shielded Keys const path = makeSaplingPath(chains.namada.bip44.coinType, { account: zip32Path.account, @@ -157,8 +157,8 @@ export const LedgerConnect: React.FC = ({ {isLedgerConnecting && !isZip32Supported && ( - Shielded key import will be enabled in NamadaApp v - {LEDGER_MIN_VERSION_ZIP32} + Shielded key import is not available for the Nano S or on versions + below {LEDGER_MIN_VERSION_ZIP32} )} diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index 7d9089df64..b6e5a1088f 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -1,5 +1,6 @@ // Make Ledger available for direct-import as it is not dependent on Sdk initialization export { + LEDGER_MASP_BLACKLISTED, LEDGER_MIN_VERSION_ZIP32, Ledger, initLedgerUSBTransport, diff --git a/packages/sdk/src/ledger.ts b/packages/sdk/src/ledger.ts index 8d9898aa78..64c72a61a4 100644 --- a/packages/sdk/src/ledger.ts +++ b/packages/sdk/src/ledger.ts @@ -28,9 +28,12 @@ export type LedgerProofGenerationKey = { export type LedgerStatus = { version: ResponseVersion; info: ResponseAppInfo; + deviceId?: string; + deviceName?: string; }; export const LEDGER_MIN_VERSION_ZIP32 = "3.0.0"; +export const LEDGER_MASP_BLACKLISTED = "nanoS"; export type Bparams = { spend: { @@ -119,10 +122,13 @@ export class Ledger { public async status(): Promise { const version = await this.namadaApp.getVersion(); const info = await this.namadaApp.getAppInfo(); + const device = this.namadaApp.transport.deviceModel; return { version, info, + deviceId: device?.id, + deviceName: device?.productName, }; } @@ -235,7 +241,7 @@ export class Ledger { promptUser = true ): Promise { try { - await this.validateVersionForZip32(); + await this.validateZip32Support(); const { xfvk }: ResponseViewKey = await this.namadaApp.retrieveKeys( path, @@ -268,7 +274,7 @@ export class Ledger { promptUser = true ): Promise { try { - await this.validateVersionForZip32(); + await this.validateZip32Support(); const { ak, nsk }: ResponseProofGenKey = await this.namadaApp.retrieveKeys( @@ -343,21 +349,33 @@ export class Ledger { public async isZip32Supported(): Promise { const { info: { appVersion }, + deviceId, } = await this.status(); - return !semver.lt(appVersion, LEDGER_MIN_VERSION_ZIP32); + const isSupportedVersion = !semver.lt(appVersion, LEDGER_MIN_VERSION_ZIP32); + const isSupportedDevice = deviceId !== LEDGER_MASP_BLACKLISTED; + + return isSupportedVersion && isSupportedDevice; } /** - * Validate the version against the minimum required version for Zip32 functionality. + * Validate the version against the minimum required version and + * device type for Zip32 functionality. * Throw error if it is unsupported or app is not initialized. * @async * @returns void */ - private async validateVersionForZip32(): Promise { + private async validateZip32Support(): Promise { if (!(await this.isZip32Supported())) { const { info: { appVersion }, + deviceId, + deviceName, } = await this.status(); + + if (deviceId === LEDGER_MASP_BLACKLISTED) { + throw new Error(`This method is not supported on ${deviceName}!`); + } + throw new Error( `This method requires Zip32 and is unsupported in ${appVersion}! ` + `Please update to at least ${LEDGER_MIN_VERSION_ZIP32}!` diff --git a/packages/sdk/src/tests/ledger.test.ts b/packages/sdk/src/tests/ledger.test.ts index 0934e984f2..e608fb6f4d 100644 --- a/packages/sdk/src/tests/ledger.test.ts +++ b/packages/sdk/src/tests/ledger.test.ts @@ -100,9 +100,15 @@ describe("ledger", () => { it("should return the status of the ledger", async () => { const version = { version: "1.0.0" }; const info = { info: "info" }; + const deviceId = "nanoSP"; + const deviceName = "Ledger Nano S+"; + const namadaApp = { getVersion: jest.fn().mockReturnValue(version), getAppInfo: jest.fn().mockReturnValue(info), + transport: { + deviceModel: { id: deviceId, productName: deviceName }, + }, }; const ledger: Ledger = new (Ledger as any)(namadaApp as any); @@ -110,7 +116,7 @@ describe("ledger", () => { expect(namadaApp.getVersion).toHaveBeenCalled(); expect(namadaApp.getAppInfo).toHaveBeenCalled(); - expect(res).toEqual({ version, info }); + expect(res).toEqual({ version, info, deviceId, deviceName }); }); });