Merge pull request #12584 from MetaMask/Version-v10.5.0
Version v10.5.0 RCfeature/default_network_editable
commit
263e80da5e
@ -0,0 +1,37 @@ |
|||||||
|
import { cloneDeep } from 'lodash'; |
||||||
|
import { LEDGER_TRANSPORT_TYPES } from '../../../shared/constants/hardware-wallets'; |
||||||
|
|
||||||
|
const version = 66; |
||||||
|
|
||||||
|
/** |
||||||
|
* Changes the useLedgerLive boolean property to the ledgerTransportType enum |
||||||
|
*/ |
||||||
|
export default { |
||||||
|
version, |
||||||
|
async migrate(originalVersionedData) { |
||||||
|
const versionedData = cloneDeep(originalVersionedData); |
||||||
|
versionedData.meta.version = version; |
||||||
|
const state = versionedData.data; |
||||||
|
const newState = transformState(state); |
||||||
|
versionedData.data = newState; |
||||||
|
return versionedData; |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
function transformState(state) { |
||||||
|
const defaultTransportType = window.navigator.hid |
||||||
|
? LEDGER_TRANSPORT_TYPES.WEBHID |
||||||
|
: LEDGER_TRANSPORT_TYPES.U2F; |
||||||
|
const useLedgerLive = Boolean(state.PreferencesController?.useLedgerLive); |
||||||
|
const newState = { |
||||||
|
...state, |
||||||
|
PreferencesController: { |
||||||
|
...state?.PreferencesController, |
||||||
|
ledgerTransportType: useLedgerLive |
||||||
|
? LEDGER_TRANSPORT_TYPES.LIVE |
||||||
|
: defaultTransportType, |
||||||
|
}, |
||||||
|
}; |
||||||
|
delete newState.PreferencesController.useLedgerLive; |
||||||
|
return newState; |
||||||
|
} |
@ -0,0 +1,116 @@ |
|||||||
|
import { LEDGER_TRANSPORT_TYPES } from '../../../shared/constants/hardware-wallets'; |
||||||
|
import migration66 from './066'; |
||||||
|
|
||||||
|
describe('migration #66', () => { |
||||||
|
beforeEach(() => { |
||||||
|
jest.resetAllMocks(); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should update the version metadata', async () => { |
||||||
|
const oldStorage = { |
||||||
|
meta: { |
||||||
|
version: 65, |
||||||
|
}, |
||||||
|
data: {}, |
||||||
|
}; |
||||||
|
|
||||||
|
const newStorage = await migration66.migrate(oldStorage); |
||||||
|
expect(newStorage.meta).toStrictEqual({ |
||||||
|
version: 66, |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should set ledgerTransportType to `u2f` if no preferences controller exists and webhid is not available', async () => { |
||||||
|
const oldStorage = { |
||||||
|
meta: {}, |
||||||
|
data: {}, |
||||||
|
}; |
||||||
|
|
||||||
|
const newStorage = await migration66.migrate(oldStorage); |
||||||
|
expect( |
||||||
|
newStorage.data.PreferencesController.ledgerTransportType, |
||||||
|
).toStrictEqual(LEDGER_TRANSPORT_TYPES.U2F); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should set ledgerTransportType to `u2f` if no useLedgerLive property exists and webhid is not available', async () => { |
||||||
|
const oldStorage = { |
||||||
|
meta: {}, |
||||||
|
data: { |
||||||
|
PreferencesController: {}, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
const newStorage = await migration66.migrate(oldStorage); |
||||||
|
expect( |
||||||
|
newStorage.data.PreferencesController.ledgerTransportType, |
||||||
|
).toStrictEqual(LEDGER_TRANSPORT_TYPES.U2F); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should set ledgerTransportType to `u2f` if useLedgerLive is false and webhid is not available', async () => { |
||||||
|
const oldStorage = { |
||||||
|
meta: {}, |
||||||
|
data: { |
||||||
|
PreferencesController: { |
||||||
|
useLedgerLive: false, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
const newStorage = await migration66.migrate(oldStorage); |
||||||
|
expect( |
||||||
|
newStorage.data.PreferencesController.ledgerTransportType, |
||||||
|
).toStrictEqual(LEDGER_TRANSPORT_TYPES.U2F); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should set ledgerTransportType to `webhid` if useLedgerLive is false and webhid is available', async () => { |
||||||
|
const oldStorage = { |
||||||
|
meta: {}, |
||||||
|
data: { |
||||||
|
PreferencesController: { |
||||||
|
useLedgerLive: false, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}; |
||||||
|
jest |
||||||
|
.spyOn(window, 'navigator', 'get') |
||||||
|
.mockImplementation(() => ({ hid: true })); |
||||||
|
const newStorage = await migration66.migrate(oldStorage); |
||||||
|
expect( |
||||||
|
newStorage.data.PreferencesController.ledgerTransportType, |
||||||
|
).toStrictEqual(LEDGER_TRANSPORT_TYPES.WEBHID); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should set ledgerTransportType to `ledgerLive` if useLedgerLive is true', async () => { |
||||||
|
const oldStorage = { |
||||||
|
meta: {}, |
||||||
|
data: { |
||||||
|
PreferencesController: { |
||||||
|
useLedgerLive: true, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
const newStorage = await migration66.migrate(oldStorage); |
||||||
|
expect( |
||||||
|
newStorage.data.PreferencesController.ledgerTransportType, |
||||||
|
).toStrictEqual('ledgerLive'); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should not change ledgerTransportType if useLedgerLive is true and webhid is available', async () => { |
||||||
|
const oldStorage = { |
||||||
|
meta: {}, |
||||||
|
data: { |
||||||
|
PreferencesController: { |
||||||
|
useLedgerLive: true, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}; |
||||||
|
jest |
||||||
|
.spyOn(window, 'navigator', 'get') |
||||||
|
.mockImplementation(() => ({ hid: true })); |
||||||
|
const newStorage = await migration66.migrate(oldStorage); |
||||||
|
expect( |
||||||
|
newStorage.data.PreferencesController.ledgerTransportType, |
||||||
|
).toStrictEqual(LEDGER_TRANSPORT_TYPES.LIVE); |
||||||
|
}); |
||||||
|
}); |
@ -0,0 +1 @@ |
|||||||
|
export { default } from './ledger-instruction-field'; |
@ -0,0 +1,212 @@ |
|||||||
|
import React, { useEffect } from 'react'; |
||||||
|
import { useDispatch, useSelector } from 'react-redux'; |
||||||
|
import PropTypes from 'prop-types'; |
||||||
|
import { |
||||||
|
LEDGER_TRANSPORT_TYPES, |
||||||
|
LEDGER_USB_VENDOR_ID, |
||||||
|
WEBHID_CONNECTED_STATUSES, |
||||||
|
TRANSPORT_STATES, |
||||||
|
} from '../../../../shared/constants/hardware-wallets'; |
||||||
|
import { |
||||||
|
PLATFORM_FIREFOX, |
||||||
|
ENVIRONMENT_TYPE_FULLSCREEN, |
||||||
|
} from '../../../../shared/constants/app'; |
||||||
|
|
||||||
|
import { |
||||||
|
setLedgerWebHidConnectedStatus, |
||||||
|
getLedgerWebHidConnectedStatus, |
||||||
|
setLedgerTransportStatus, |
||||||
|
getLedgerTransportStatus, |
||||||
|
} from '../../../ducks/app/app'; |
||||||
|
|
||||||
|
import Typography from '../../ui/typography/typography'; |
||||||
|
import Button from '../../ui/button'; |
||||||
|
import { useI18nContext } from '../../../hooks/useI18nContext'; |
||||||
|
import { |
||||||
|
COLORS, |
||||||
|
FONT_WEIGHT, |
||||||
|
TYPOGRAPHY, |
||||||
|
} from '../../../helpers/constants/design-system'; |
||||||
|
import Dialog from '../../ui/dialog'; |
||||||
|
import { |
||||||
|
getPlatform, |
||||||
|
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 ( |
||||||
|
show && ( |
||||||
|
<Typography |
||||||
|
boxProps={{ margin: 0 }} |
||||||
|
color={color} |
||||||
|
fontWeight={FONT_WEIGHT.BOLD} |
||||||
|
variant={TYPOGRAPHY.H7} |
||||||
|
> |
||||||
|
{text} |
||||||
|
</Typography> |
||||||
|
) |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
export default function LedgerInstructionField({ showDataInstruction }) { |
||||||
|
const t = useI18nContext(); |
||||||
|
const dispatch = useDispatch(); |
||||||
|
|
||||||
|
const webHidConnectedStatus = useSelector(getLedgerWebHidConnectedStatus); |
||||||
|
const ledgerTransportType = useSelector(getLedgerTransportType); |
||||||
|
const transportStatus = useSelector(getLedgerTransportStatus); |
||||||
|
const environmentType = getEnvironmentType(); |
||||||
|
const environmentTypeIsFullScreen = |
||||||
|
environmentType === ENVIRONMENT_TYPE_FULLSCREEN; |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
const initialConnectedDeviceCheck = async () => { |
||||||
|
if ( |
||||||
|
ledgerTransportType === LEDGER_TRANSPORT_TYPES.WEBHID && |
||||||
|
webHidConnectedStatus !== WEBHID_CONNECTED_STATUSES.CONNECTED |
||||||
|
) { |
||||||
|
const devices = await window.navigator.hid.getDevices(); |
||||||
|
const webHidIsConnected = devices.some( |
||||||
|
(device) => device.vendorId === Number(LEDGER_USB_VENDOR_ID), |
||||||
|
); |
||||||
|
dispatch( |
||||||
|
setLedgerWebHidConnectedStatus( |
||||||
|
webHidIsConnected |
||||||
|
? WEBHID_CONNECTED_STATUSES.CONNECTED |
||||||
|
: WEBHID_CONNECTED_STATUSES.NOT_CONNECTED, |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
}; |
||||||
|
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, transportStatus]); |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
return () => { |
||||||
|
dispatch(setLedgerTransportStatus(TRANSPORT_STATES.NONE)); |
||||||
|
}; |
||||||
|
}, [dispatch]); |
||||||
|
|
||||||
|
const usingLedgerLive = ledgerTransportType === LEDGER_TRANSPORT_TYPES.LIVE; |
||||||
|
const usingWebHID = ledgerTransportType === LEDGER_TRANSPORT_TYPES.WEBHID; |
||||||
|
|
||||||
|
const isFirefox = getPlatform() === PLATFORM_FIREFOX; |
||||||
|
|
||||||
|
return ( |
||||||
|
<div> |
||||||
|
<div className="confirm-detail-row"> |
||||||
|
<Dialog type="message"> |
||||||
|
<div className="ledger-live-dialog"> |
||||||
|
{renderInstructionStep(t('ledgerConnectionInstructionHeader'))} |
||||||
|
{renderInstructionStep( |
||||||
|
`- ${t('ledgerConnectionInstructionStepOne')}`, |
||||||
|
!isFirefox && usingLedgerLive, |
||||||
|
)} |
||||||
|
{renderInstructionStep( |
||||||
|
`- ${t('ledgerConnectionInstructionStepTwo')}`, |
||||||
|
!isFirefox && usingLedgerLive, |
||||||
|
)} |
||||||
|
{renderInstructionStep( |
||||||
|
`- ${t('ledgerConnectionInstructionStepThree')}`, |
||||||
|
)} |
||||||
|
{renderInstructionStep( |
||||||
|
`- ${t('ledgerConnectionInstructionStepFour')}`, |
||||||
|
showDataInstruction, |
||||||
|
)} |
||||||
|
{renderInstructionStep( |
||||||
|
<span> |
||||||
|
<Button |
||||||
|
type="link" |
||||||
|
onClick={async () => { |
||||||
|
if (environmentTypeIsFullScreen) { |
||||||
|
window.location.reload(); |
||||||
|
} else { |
||||||
|
global.platform.openExtensionInBrowser(null, null, true); |
||||||
|
} |
||||||
|
}} |
||||||
|
> |
||||||
|
{t('ledgerConnectionInstructionCloseOtherApps')} |
||||||
|
</Button> |
||||||
|
</span>, |
||||||
|
transportStatus === TRANSPORT_STATES.DEVICE_OPEN_FAILURE, |
||||||
|
)} |
||||||
|
{renderInstructionStep( |
||||||
|
<span> |
||||||
|
<Button |
||||||
|
type="link" |
||||||
|
onClick={async () => { |
||||||
|
if (environmentTypeIsFullScreen) { |
||||||
|
const connectedDevices = await window.navigator.hid.requestDevice( |
||||||
|
{ |
||||||
|
filters: [{ vendorId: LEDGER_USB_VENDOR_ID }], |
||||||
|
}, |
||||||
|
); |
||||||
|
const webHidIsConnected = connectedDevices.some( |
||||||
|
(device) => |
||||||
|
device.vendorId === Number(LEDGER_USB_VENDOR_ID), |
||||||
|
); |
||||||
|
dispatch( |
||||||
|
setLedgerWebHidConnectedStatus({ |
||||||
|
webHidConnectedStatus: webHidIsConnected |
||||||
|
? WEBHID_CONNECTED_STATUSES.CONNECTED |
||||||
|
: WEBHID_CONNECTED_STATUSES.NOT_CONNECTED, |
||||||
|
}), |
||||||
|
); |
||||||
|
} else { |
||||||
|
global.platform.openExtensionInBrowser(null, null, true); |
||||||
|
} |
||||||
|
}} |
||||||
|
> |
||||||
|
{environmentTypeIsFullScreen |
||||||
|
? t('clickToConnectLedgerViaWebHID') |
||||||
|
: t('openFullScreenForLedgerWebHid')} |
||||||
|
</Button> |
||||||
|
</span>, |
||||||
|
usingWebHID && |
||||||
|
webHidConnectedStatus === |
||||||
|
WEBHID_CONNECTED_STATUSES.NOT_CONNECTED, |
||||||
|
COLORS.SECONDARY1, |
||||||
|
)} |
||||||
|
</div> |
||||||
|
</Dialog> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
LedgerInstructionField.propTypes = { |
||||||
|
showDataInstruction: PropTypes.bool, |
||||||
|
}; |
Loading…
Reference in new issue