diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json
index ab52fa49c..dee684716 100644
--- a/app/_locales/en/messages.json
+++ b/app/_locales/en/messages.json
@@ -1255,6 +1255,9 @@
"ledgerAccountRestriction": {
"message": "You need to make use your last account before you can add a new one."
},
+ "ledgerConnectionInstructionCloseOtherApps": {
+ "message": "Close any other software connected to your device and then click here to refresh."
+ },
"ledgerConnectionInstructionHeader": {
"message": "Prior to clicking confirm:"
},
diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js
index 2010ce69f..40832c0bc 100644
--- a/app/scripts/metamask-controller.js
+++ b/app/scripts/metamask-controller.js
@@ -845,6 +845,10 @@ export default class MetamaskController extends EventEmitter {
this.setLedgerTransportPreference,
this,
),
+ attemptLedgerTransportCreation: nodeify(
+ this.attemptLedgerTransportCreation,
+ this,
+ ),
// mobile
fetchInfoToSync: nodeify(this.fetchInfoToSync, this),
@@ -1556,6 +1560,11 @@ export default class MetamaskController extends EventEmitter {
return keyring;
}
+ async attemptLedgerTransportCreation() {
+ const keyring = await this.getKeyringForDevice('ledger');
+ return await keyring.attemptMakeApp();
+ }
+
/**
* Fetch account list from a trezor device.
*
diff --git a/package.json b/package.json
index c5789c618..b991a755c 100644
--- a/package.json
+++ b/package.json
@@ -105,7 +105,7 @@
"@material-ui/core": "^4.11.0",
"@metamask/contract-metadata": "^1.28.0",
"@metamask/controllers": "^17.0.0",
- "@metamask/eth-ledger-bridge-keyring": "^0.9.0",
+ "@metamask/eth-ledger-bridge-keyring": "^0.10.0",
"@metamask/eth-token-tracker": "^3.0.1",
"@metamask/etherscan-link": "^2.1.0",
"@metamask/jazzicon": "^2.0.0",
diff --git a/shared/constants/hardware-wallets.js b/shared/constants/hardware-wallets.js
index e780555fc..705754fe7 100644
--- a/shared/constants/hardware-wallets.js
+++ b/shared/constants/hardware-wallets.js
@@ -24,3 +24,10 @@ export const WEBHID_CONNECTED_STATUSES = {
NOT_CONNECTED: 'notConnected',
UNKNOWN: 'unknown',
};
+
+export const TRANSPORT_STATES = {
+ NONE: 'NONE',
+ VERIFIED: 'VERIFIED',
+ DEVICE_OPEN_FAILURE: 'DEVICE_OPEN_FAILURE',
+ UNKNOWN_FAILURE: 'UNKNOWN_FAILURE',
+};
diff --git a/ui/components/app/ledger-instruction-field/ledger-instruction-field.js b/ui/components/app/ledger-instruction-field/ledger-instruction-field.js
index 960b6ca07..287db2c8e 100644
--- a/ui/components/app/ledger-instruction-field/ledger-instruction-field.js
+++ b/ui/components/app/ledger-instruction-field/ledger-instruction-field.js
@@ -5,6 +5,7 @@ import {
LEDGER_TRANSPORT_TYPES,
LEDGER_USB_VENDOR_ID,
WEBHID_CONNECTED_STATUSES,
+ TRANSPORT_STATES,
} from '../../../../shared/constants/hardware-wallets';
import {
PLATFORM_FIREFOX,
@@ -14,6 +15,8 @@ import {
import {
setLedgerWebHidConnectedStatus,
getLedgerWebHidConnectedStatus,
+ setLedgerTransportStatus,
+ getLedgerTransportStatus,
} from '../../../ducks/app/app';
import Typography from '../../ui/typography/typography';
@@ -30,6 +33,7 @@ import {
getEnvironmentType,
} from '../../../../app/scripts/lib/util';
import { getLedgerTransportType } from '../../../ducks/metamask/metamask';
+import { attemptLedgerTransportCreation } from '../../../store/actions';
const renderInstructionStep = (text, show = true, color = COLORS.PRIMARY3) => {
return (
@@ -52,6 +56,7 @@ export default function LedgerInstructionField({ showDataInstruction }) {
const webHidConnectedStatus = useSelector(getLedgerWebHidConnectedStatus);
const ledgerTransportType = useSelector(getLedgerTransportType);
+ const transportStatus = useSelector(getLedgerTransportStatus);
const environmentType = getEnvironmentType();
const environmentTypeIsFullScreen =
environmentType === ENVIRONMENT_TYPE_FULLSCREEN;
@@ -75,8 +80,45 @@ export default function LedgerInstructionField({ showDataInstruction }) {
);
}
};
+ const determineTransportStatus = async () => {
+ if (
+ ledgerTransportType === LEDGER_TRANSPORT_TYPES.WEBHID &&
+ webHidConnectedStatus === WEBHID_CONNECTED_STATUSES.CONNECTED &&
+ transportStatus === TRANSPORT_STATES.NONE
+ ) {
+ try {
+ const transportedCreated = await attemptLedgerTransportCreation();
+ dispatch(
+ setLedgerTransportStatus(
+ transportedCreated
+ ? TRANSPORT_STATES.VERIFIED
+ : TRANSPORT_STATES.UNKNOWN_FAILURE,
+ ),
+ );
+ } catch (e) {
+ if (e.message.match('Failed to open the device')) {
+ dispatch(
+ setLedgerTransportStatus(TRANSPORT_STATES.DEVICE_OPEN_FAILURE),
+ );
+ } else if (e.message.match('the device is already open')) {
+ dispatch(setLedgerTransportStatus(TRANSPORT_STATES.VERIFIED));
+ } else {
+ dispatch(
+ setLedgerTransportStatus(TRANSPORT_STATES.UNKNOWN_FAILURE),
+ );
+ }
+ }
+ }
+ };
+ determineTransportStatus();
initialConnectedDeviceCheck();
- }, [dispatch, ledgerTransportType, webHidConnectedStatus]);
+ }, [dispatch, ledgerTransportType, webHidConnectedStatus, transportStatus]);
+
+ useEffect(() => {
+ return () => {
+ dispatch(setLedgerTransportStatus(TRANSPORT_STATES.NONE));
+ };
+ }, [dispatch]);
const usingLedgerLive = ledgerTransportType === LEDGER_TRANSPORT_TYPES.LIVE;
const usingWebHID = ledgerTransportType === LEDGER_TRANSPORT_TYPES.WEBHID;
@@ -104,6 +146,23 @@ export default function LedgerInstructionField({ showDataInstruction }) {
`- ${t('ledgerConnectionInstructionStepFour')}`,
showDataInstruction,
)}
+ {renderInstructionStep(
+
+
+ ,
+ transportStatus === TRANSPORT_STATES.DEVICE_OPEN_FAILURE,
+ )}
{renderInstructionStep(