All files / app/hooks use-prepare-ledger.ts

0% Statements 0/55
0% Branches 0/48
0% Functions 0/10
0% Lines 0/51

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114                                                                                                                                                                                                                                   
import type { LedgerMessageEvents } from '../main/register-ledger-listeners';
import { useCheckForUpdates } from './use-check-for-updates';
import { useListenLedgerEffect } from './use-listen-ledger-effect';
import { messages$ } from './use-message-events';
import { EARLIEST_SUPPORTED_LEDGER_VERSION } from '@constants/index';
import { isTestnet } from '@utils/network-utils';
import { LedgerError } from '@zondax/ledger-blockstack';
import compareVersions from 'compare-versions';
import { useEffect, useMemo, useState } from 'react';
import { Observable } from 'rxjs';
import { filter } from 'rxjs/operators';
 
const publicKeysDoNotMatchError = `Public key mismatch. Make sure you're using the same Ledger you created the wallet with.`;
 
export enum LedgerConnectStep {
  Disconnected,
  ConnectedAppClosed,
  ConnectedAppOpen,
  ActionComplete,
}
 
interface AppVersion {
  major: number;
  minor: number;
  patch: number;
}
 
const ledgerEvents$ = messages$.pipe(
  filter(value => value.type === 'ledger-event')
) as Observable<LedgerMessageEvents>;
 
export function usePrepareLedger() {
  const [step, setStep] = useState<LedgerConnectStep>(LedgerConnectStep.Disconnected);
  const [isLocked, setIsLocked] = useState(false);
  const [appVersion, setAppVersion] = useState<AppVersion | null>(null);
  const { isNewerReleaseAvailable } = useCheckForUpdates();
 
  const versionSupportsTestnetLedger = useMemo(() => {
    Iif (appVersion === null) return false;
    return appVersion.major >= 0 && appVersion.minor > 11;
  }, [appVersion]);
 
  const isSupportedAppVersion = useMemo(() => {
    Iif (appVersion === null) return true;
    Iif (!versionSupportsTestnetLedger && isTestnet()) return false;
    const { major, minor, patch } = appVersion;
    const currentVersion = `${major}.${minor}.${patch}`;
    return compareVersions.compare(currentVersion, EARLIEST_SUPPORTED_LEDGER_VERSION, '>=');
  }, [appVersion, versionSupportsTestnetLedger]);
 
  const appVersionErrorText = useMemo(() => {
    Iif (!versionSupportsTestnetLedger && isTestnet()) {
      return `Cannot use Ledger on testnet with app version 0.11.0 or lower. Upgrade on Ledger Live.`;
    }
    return `
      Make sure to upgrade your Stacks app to the latest version in Ledger Live. ${
        isNewerReleaseAvailable ? 'You should also upgrade Leather to the latest version.' : ''
      }
      This version Leather works with ${EARLIEST_SUPPORTED_LEDGER_VERSION} onwards.
      Detected version ${String(appVersion?.major)}.${String(appVersion?.minor)}.${String(
      appVersion?.patch
    )}
    `;
  }, [
    appVersion?.major,
    appVersion?.minor,
    appVersion?.patch,
    isNewerReleaseAvailable,
    versionSupportsTestnetLedger,
  ]);
 
  useListenLedgerEffect();
 
  useEffect(() => {
    const sub = ledgerEvents$
      .pipe(filter(message => message.type === 'ledger-event'))
      .subscribe(message => {
        Iif (message.name === 'disconnected') {
          setStep(LedgerConnectStep.Disconnected);
        }
        Iif ('action' in message && message.action === 'get-version') {
          Iif (
            Number.isInteger(message.major) &&
            Number.isInteger(message.minor) &&
            Number.isInteger(message.patch)
          ) {
            setAppVersion({
              major: message.major,
              minor: message.minor,
              patch: message.patch,
            });
          }
          setIsLocked(message.deviceLocked);
          Iif (message.returnCode === LedgerError.AppDoesNotSeemToBeOpen) {
            setStep(LedgerConnectStep.ConnectedAppClosed);
          }
          Iif (message.returnCode === LedgerError.NoErrors) {
            setStep(LedgerConnectStep.ConnectedAppOpen);
          }
        }
      });
    return () => sub.unsubscribe();
  }, []);
 
  return {
    step,
    isLocked,
    appVersion,
    isSupportedAppVersion,
    appVersionErrorText,
    publicKeysDoNotMatchError,
  };
}