mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Swaps: Improve hardware wallet UX (#10987)
This commit is contained in:
parent
4016bb535b
commit
92a79904f7
@ -1804,6 +1804,10 @@
|
||||
"swapAggregator": {
|
||||
"message": "Aggregator"
|
||||
},
|
||||
"swapAllowSwappingOf": {
|
||||
"message": "Allow swapping of $1",
|
||||
"description": "Shows a user that they need to allow a token for swapping on their hardware wallet"
|
||||
},
|
||||
"swapAmountReceived": {
|
||||
"message": "Guaranteed amount"
|
||||
},
|
||||
@ -1829,6 +1833,9 @@
|
||||
"message": "Checking $1",
|
||||
"description": "Shown to the user during quote loading. $1 is the name of an aggregator. The message indicates that metamask is currently checking if that aggregator has a trade/quote for their requested swap."
|
||||
},
|
||||
"swapConfirmWithHwWallet": {
|
||||
"message": "Confirm with your hardware wallet"
|
||||
},
|
||||
"swapCustom": {
|
||||
"message": "custom"
|
||||
},
|
||||
@ -1874,6 +1881,13 @@
|
||||
"swapFinalizing": {
|
||||
"message": "Finalizing..."
|
||||
},
|
||||
"swapFromTo": {
|
||||
"message": "The swap of $1 to $2",
|
||||
"description": "Tells a user that they need to confirm on their hardware wallet a swap of 2 tokens. $1 is a source token and $2 is a destination token"
|
||||
},
|
||||
"swapGasFeesSplit": {
|
||||
"message": "Gas fees on the previous screen are split between these two transactions."
|
||||
},
|
||||
"swapHighSlippageWarning": {
|
||||
"message": "Slippage amount is very high."
|
||||
},
|
||||
@ -2015,6 +2029,9 @@
|
||||
"swapThisWillAllowApprove": {
|
||||
"message": "This will allow $1 to be swapped."
|
||||
},
|
||||
"swapToConfirmWithHwWallet": {
|
||||
"message": "to confirm with your hardware wallet"
|
||||
},
|
||||
"swapTokenAvailable": {
|
||||
"message": "Your $1 has been added to your account.",
|
||||
"description": "This message is shown after a swap is successful and communicates the exact amount of tokens the user has received for a swap. The $1 is a decimal number of tokens followed by the token symbol."
|
||||
@ -2044,6 +2061,9 @@
|
||||
"swapTransactionComplete": {
|
||||
"message": "Transaction complete"
|
||||
},
|
||||
"swapTwoTransactions": {
|
||||
"message": "2 transactions"
|
||||
},
|
||||
"swapUnknown": {
|
||||
"message": "Unknown"
|
||||
},
|
||||
|
@ -26,6 +26,7 @@ import {
|
||||
cancelTx,
|
||||
} from '../../store/actions';
|
||||
import {
|
||||
AWAITING_SIGNATURES_ROUTE,
|
||||
AWAITING_SWAP_ROUTE,
|
||||
BUILD_QUOTE_ROUTE,
|
||||
LOADING_QUOTES_ROUTE,
|
||||
@ -52,6 +53,7 @@ import {
|
||||
getUSDConversionRate,
|
||||
getSwapsDefaultToken,
|
||||
getCurrentChainId,
|
||||
isHardwareWallet,
|
||||
} from '../../selectors';
|
||||
import {
|
||||
ERROR_FETCHING_QUOTES,
|
||||
@ -73,6 +75,7 @@ export const FALLBACK_GAS_MULTIPLIER = 1.5;
|
||||
const initialState = {
|
||||
aggregatorMetadata: null,
|
||||
approveTxId: null,
|
||||
tradeTxId: null,
|
||||
balanceError: false,
|
||||
fetchingQuotes: false,
|
||||
fromToken: null,
|
||||
@ -95,6 +98,7 @@ const slice = createSlice({
|
||||
clearSwapsState: () => initialState,
|
||||
navigatedBackToBuildQuote: (state) => {
|
||||
state.approveTxId = null;
|
||||
state.tradeTxId = null;
|
||||
state.balanceError = false;
|
||||
state.fetchingQuotes = false;
|
||||
state.customGas.limit = null;
|
||||
@ -585,7 +589,7 @@ export const signAndSendTransactions = (history, metaMetricsEvent) => {
|
||||
return async (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const chainId = getCurrentChainId(state);
|
||||
|
||||
const hardwareWalletUsed = isHardwareWallet(state);
|
||||
let swapsFeatureIsLive = false;
|
||||
try {
|
||||
swapsFeatureIsLive = await fetchSwapsFeatureLiveness(chainId);
|
||||
@ -605,7 +609,10 @@ export const signAndSendTransactions = (history, metaMetricsEvent) => {
|
||||
const { sourceTokenInfo = {}, destinationTokenInfo = {} } = metaData;
|
||||
await dispatch(setBackgroundSwapRouteState('awaiting'));
|
||||
await dispatch(stopPollingForQuotes());
|
||||
history.push(AWAITING_SWAP_ROUTE);
|
||||
|
||||
if (!hardwareWalletUsed) {
|
||||
history.push(AWAITING_SWAP_ROUTE);
|
||||
}
|
||||
|
||||
const { fast: fastGasEstimate } = getSwapGasPriceEstimateData(state);
|
||||
|
||||
@ -694,6 +701,13 @@ export const signAndSendTransactions = (history, metaMetricsEvent) => {
|
||||
|
||||
let finalApproveTxMeta;
|
||||
const approveTxParams = getApproveTxParams(state);
|
||||
|
||||
// For hardware wallets we go to the Awaiting Signatures page first and only after a user
|
||||
// completes 1 or 2 confirmations, we redirect to the Awaiting Swap page.
|
||||
if (hardwareWalletUsed) {
|
||||
history.push(AWAITING_SIGNATURES_ROUTE);
|
||||
}
|
||||
|
||||
if (approveTxParams) {
|
||||
const approveTxMeta = await dispatch(
|
||||
addUnapprovedTransaction(
|
||||
@ -765,6 +779,12 @@ export const signAndSendTransactions = (history, metaMetricsEvent) => {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only after a user confirms swapping on a hardware wallet (second `updateAndApproveTx` call above),
|
||||
// we redirect to the Awaiting Swap page.
|
||||
if (hardwareWalletUsed) {
|
||||
history.push(AWAITING_SWAP_ROUTE);
|
||||
}
|
||||
|
||||
await forceUpdateMetamaskState(dispatch);
|
||||
};
|
||||
};
|
||||
|
@ -32,6 +32,7 @@ const SWAPS_ROUTE = '/swaps';
|
||||
const BUILD_QUOTE_ROUTE = '/swaps/build-quote';
|
||||
const VIEW_QUOTE_ROUTE = '/swaps/view-quote';
|
||||
const LOADING_QUOTES_ROUTE = '/swaps/loading-quotes';
|
||||
const AWAITING_SIGNATURES_ROUTE = '/swaps/awaiting-signatures';
|
||||
const AWAITING_SWAP_ROUTE = '/swaps/awaiting-swap';
|
||||
const SWAPS_ERROR_ROUTE = '/swaps/swaps-error';
|
||||
const SWAPS_MAINTENANCE_ROUTE = '/swaps/maintenance';
|
||||
@ -191,6 +192,7 @@ export {
|
||||
VIEW_QUOTE_ROUTE,
|
||||
LOADING_QUOTES_ROUTE,
|
||||
AWAITING_SWAP_ROUTE,
|
||||
AWAITING_SIGNATURES_ROUTE,
|
||||
SWAPS_ERROR_ROUTE,
|
||||
SWAPS_MAINTENANCE_ROUTE,
|
||||
};
|
||||
|
@ -0,0 +1,26 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`AwaitingSignatures renders the component with initial props for 1 confirmation 1`] = `
|
||||
<div
|
||||
class="swaps-footer"
|
||||
>
|
||||
<div
|
||||
class="swaps-footer__buttons"
|
||||
>
|
||||
<div
|
||||
class="page-container__footer swaps-footer__custom-page-container-footer-class"
|
||||
>
|
||||
<footer>
|
||||
<button
|
||||
class="button btn-primary page-container__footer-button swaps-footer__custom-page-container-footer-button-class swaps-footer__custom-page-container-footer-button-class--single"
|
||||
data-testid="page-container-footer-next"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -0,0 +1,25 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`SwapStepIcon renders the component 1`] = `
|
||||
<div>
|
||||
<svg
|
||||
fill="none"
|
||||
height="14"
|
||||
viewBox="0 0 14 14"
|
||||
width="14"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle
|
||||
cx="7"
|
||||
cy="7"
|
||||
r="6.25"
|
||||
stroke="#037DD6"
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
<path
|
||||
d="M6.50983 5.192H5.27783L6.14183 4H7.71783V9.68H6.50983V5.192Z"
|
||||
fill="#037DD6"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
`;
|
139
ui/pages/swaps/awaiting-signatures/awaiting-signatures.js
Normal file
139
ui/pages/swaps/awaiting-signatures/awaiting-signatures.js
Normal file
@ -0,0 +1,139 @@
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { I18nContext } from '../../../contexts/i18n';
|
||||
import { useNewMetricEvent } from '../../../hooks/useMetricEvent';
|
||||
import {
|
||||
getFetchParams,
|
||||
getApproveTxParams,
|
||||
prepareToLeaveSwaps,
|
||||
} from '../../../ducks/swaps/swaps';
|
||||
import {
|
||||
DEFAULT_ROUTE,
|
||||
BUILD_QUOTE_ROUTE,
|
||||
} from '../../../helpers/constants/routes';
|
||||
import PulseLoader from '../../../components/ui/pulse-loader';
|
||||
import Typography from '../../../components/ui/typography';
|
||||
import Box from '../../../components/ui/box';
|
||||
import {
|
||||
BLOCK_SIZES,
|
||||
COLORS,
|
||||
TYPOGRAPHY,
|
||||
FONT_WEIGHT,
|
||||
JUSTIFY_CONTENT,
|
||||
DISPLAY,
|
||||
} from '../../../helpers/constants/design-system';
|
||||
import SwapsFooter from '../swaps-footer';
|
||||
import SwapStepIcon from './swap-step-icon';
|
||||
|
||||
export default function AwaitingSignatures() {
|
||||
const t = useContext(I18nContext);
|
||||
const history = useHistory();
|
||||
const dispatch = useDispatch();
|
||||
const fetchParams = useSelector(getFetchParams);
|
||||
const { destinationTokenInfo, sourceTokenInfo } = fetchParams?.metaData || {};
|
||||
const approveTxParams = useSelector(getApproveTxParams);
|
||||
const needsTwoConfirmations = Boolean(approveTxParams);
|
||||
|
||||
const awaitingSignaturesEvent = useNewMetricEvent({
|
||||
event: 'Awaiting Signature(s) on a HW wallet',
|
||||
sensitiveProperties: {
|
||||
needs_two_confirmations: needsTwoConfirmations,
|
||||
token_from: sourceTokenInfo?.symbol,
|
||||
token_from_amount: fetchParams?.value,
|
||||
token_to: destinationTokenInfo?.symbol,
|
||||
request_type: fetchParams?.balanceError ? 'Quote' : 'Order',
|
||||
slippage: fetchParams?.slippage,
|
||||
custom_slippage: fetchParams?.slippage === 2,
|
||||
},
|
||||
category: 'swaps',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
awaitingSignaturesEvent();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const headerText = needsTwoConfirmations
|
||||
? t('swapTwoTransactions')
|
||||
: t('swapConfirmWithHwWallet');
|
||||
|
||||
return (
|
||||
<div className="awaiting-signatures">
|
||||
<Box
|
||||
paddingLeft={8}
|
||||
paddingRight={8}
|
||||
height={BLOCK_SIZES.FULL}
|
||||
justifyContent={JUSTIFY_CONTENT.CENTER}
|
||||
display={DISPLAY.FLEX}
|
||||
className="awaiting-signatures__content"
|
||||
>
|
||||
<Box marginTop={3} marginBottom={4}>
|
||||
<PulseLoader />
|
||||
</Box>
|
||||
<Typography color={COLORS.BLACK} variant={TYPOGRAPHY.H3}>
|
||||
{headerText}
|
||||
</Typography>
|
||||
{needsTwoConfirmations && (
|
||||
<>
|
||||
<Typography
|
||||
variant={TYPOGRAPHY.Paragraph}
|
||||
boxProps={{ marginTop: 2 }}
|
||||
fontWeight={FONT_WEIGHT.BOLD}
|
||||
>
|
||||
{t('swapToConfirmWithHwWallet')}
|
||||
</Typography>
|
||||
<ul className="awaiting-signatures__steps">
|
||||
<li>
|
||||
<SwapStepIcon stepNumber={1} />
|
||||
{t('swapAllowSwappingOf', [
|
||||
<Typography
|
||||
tag="span"
|
||||
fontWeight={FONT_WEIGHT.BOLD}
|
||||
key="allowToken"
|
||||
>
|
||||
{destinationTokenInfo?.symbol}
|
||||
</Typography>,
|
||||
])}
|
||||
</li>
|
||||
<li>
|
||||
<SwapStepIcon stepNumber={2} />
|
||||
{t('swapFromTo', [
|
||||
<Typography
|
||||
tag="span"
|
||||
fontWeight={FONT_WEIGHT.BOLD}
|
||||
key="tokenFrom"
|
||||
>
|
||||
{sourceTokenInfo?.symbol}
|
||||
</Typography>,
|
||||
<Typography
|
||||
tag="span"
|
||||
fontWeight={FONT_WEIGHT.BOLD}
|
||||
key="tokenTo"
|
||||
>
|
||||
{destinationTokenInfo?.symbol}
|
||||
</Typography>,
|
||||
])}
|
||||
</li>
|
||||
</ul>
|
||||
<Typography variant={TYPOGRAPHY.Paragraph}>
|
||||
{t('swapGasFeesSplit')}
|
||||
</Typography>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
<SwapsFooter
|
||||
onSubmit={async () => {
|
||||
await dispatch(prepareToLeaveSwaps());
|
||||
// Go to the default route and then to the build quote route in order to clean up
|
||||
// the `inputValue` local state in `pages/swaps/index.js`
|
||||
history.push(DEFAULT_ROUTE);
|
||||
history.push(BUILD_QUOTE_ROUTE);
|
||||
}}
|
||||
submitText={t('cancel')}
|
||||
hideCancel
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
import React from 'react';
|
||||
import configureMockStore from 'redux-mock-store';
|
||||
|
||||
import {
|
||||
renderWithProvider,
|
||||
createSwapsMockStore,
|
||||
} from '../../../../test/jest';
|
||||
import AwaitingSignatures from '.';
|
||||
|
||||
describe('AwaitingSignatures', () => {
|
||||
it('renders the component with initial props for 1 confirmation', () => {
|
||||
const store = configureMockStore()(createSwapsMockStore());
|
||||
const { getByText } = renderWithProvider(<AwaitingSignatures />, store);
|
||||
expect(getByText('Confirm with your hardware wallet')).toBeInTheDocument();
|
||||
expect(document.querySelector('.swaps-footer')).toMatchSnapshot();
|
||||
});
|
||||
});
|
1
ui/pages/swaps/awaiting-signatures/index.js
Normal file
1
ui/pages/swaps/awaiting-signatures/index.js
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './awaiting-signatures';
|
34
ui/pages/swaps/awaiting-signatures/index.scss
Normal file
34
ui/pages/swaps/awaiting-signatures/index.scss
Normal file
@ -0,0 +1,34 @@
|
||||
.awaiting-signatures {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
|
||||
&__content {
|
||||
flex-flow: column;
|
||||
}
|
||||
|
||||
div {
|
||||
text-align: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&__steps {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
margin: 16px auto 12px auto;
|
||||
|
||||
li {
|
||||
margin-bottom: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
svg {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
40
ui/pages/swaps/awaiting-signatures/swap-step-icon.js
Normal file
40
ui/pages/swaps/awaiting-signatures/swap-step-icon.js
Normal file
@ -0,0 +1,40 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function SwapStepIcon({ stepNumber = 1 }) {
|
||||
switch (stepNumber) {
|
||||
case 1:
|
||||
return (
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 14 14"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle cx="7" cy="7" r="6.25" stroke="#037DD6" strokeWidth="1.5" />
|
||||
<path
|
||||
d="M6.50983 5.192H5.27783L6.14183 4H7.71783V9.68H6.50983V5.192Z"
|
||||
fill="#037DD6"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
case 2:
|
||||
return (
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 14 14"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle cx="7" cy="7" r="6.25" stroke="#037DD6" strokeWidth="1.5" />
|
||||
<path
|
||||
d="M8.92 9.776H5V9.368C5 9.048 5.056 8.77067 5.168 8.536C5.28 8.296 5.42133 8.08533 5.592 7.904C5.768 7.71733 5.96267 7.54933 6.176 7.4C6.39467 7.25067 6.608 7.10133 6.816 6.952C6.928 6.872 7.03467 6.78933 7.136 6.704C7.24267 6.61867 7.33333 6.53067 7.408 6.44C7.488 6.34933 7.552 6.256 7.6 6.16C7.648 6.064 7.672 5.96533 7.672 5.864C7.672 5.67733 7.616 5.52 7.504 5.392C7.39733 5.25867 7.22933 5.192 7 5.192C6.88267 5.192 6.776 5.21333 6.68 5.256C6.584 5.29333 6.50133 5.344 6.432 5.408C6.368 5.472 6.31733 5.54667 6.28 5.632C6.248 5.71733 6.232 5.808 6.232 5.904H5.024C5.024 5.62667 5.07467 5.37067 5.176 5.136C5.27733 4.90133 5.41867 4.70133 5.6 4.536C5.78133 4.36533 5.99467 4.23467 6.24 4.144C6.48533 4.048 6.752 4 7.04 4C7.28 4 7.50933 4.03733 7.728 4.112C7.952 4.18667 8.14933 4.29867 8.32 4.448C8.49067 4.59733 8.62667 4.784 8.728 5.008C8.82933 5.22667 8.88 5.48267 8.88 5.776C8.88 6.032 8.85067 6.25867 8.792 6.456C8.73333 6.648 8.65067 6.824 8.544 6.984C8.44267 7.13867 8.32 7.28 8.176 7.408C8.032 7.536 7.87733 7.66133 7.712 7.784C7.64267 7.832 7.55733 7.888 7.456 7.952C7.36 8.016 7.26133 8.08267 7.16 8.152C7.064 8.22133 6.97333 8.29333 6.888 8.368C6.80267 8.44267 6.74133 8.51467 6.704 8.584H8.92V9.776Z"
|
||||
fill="#037DD6"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
default:
|
||||
return undefined; // Don't return any SVG if a step number is not supported.
|
||||
}
|
||||
}
|
11
ui/pages/swaps/awaiting-signatures/swap-step-icon.test.js
Normal file
11
ui/pages/swaps/awaiting-signatures/swap-step-icon.test.js
Normal file
@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
|
||||
import { renderWithProvider } from '../../../../test/jest';
|
||||
import SwapStepIcon from './swap-step-icon';
|
||||
|
||||
describe('SwapStepIcon', () => {
|
||||
it('renders the component', () => {
|
||||
const { container } = renderWithProvider(<SwapStepIcon />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
});
|
@ -33,6 +33,7 @@ import {
|
||||
fetchSwapsLiveness,
|
||||
} from '../../ducks/swaps/swaps';
|
||||
import {
|
||||
AWAITING_SIGNATURES_ROUTE,
|
||||
AWAITING_SWAP_ROUTE,
|
||||
BUILD_QUOTE_ROUTE,
|
||||
VIEW_QUOTE_ROUTE,
|
||||
@ -66,6 +67,7 @@ import {
|
||||
getSwapsTokensReceivedFromTxMeta,
|
||||
fetchAggregatorMetadata,
|
||||
} from './swaps.util';
|
||||
import AwaitingSignatures from './awaiting-signatures';
|
||||
import AwaitingSwap from './awaiting-swap';
|
||||
import LoadingQuote from './loading-swaps-quotes';
|
||||
import BuildQuote from './build-quote';
|
||||
@ -78,6 +80,7 @@ export default function Swap() {
|
||||
|
||||
const { pathname } = useLocation();
|
||||
const isAwaitingSwapRoute = pathname === AWAITING_SWAP_ROUTE;
|
||||
const isAwaitingSignaturesRoute = pathname === AWAITING_SIGNATURES_ROUTE;
|
||||
const isSwapsErrorRoute = pathname === SWAPS_ERROR_ROUTE;
|
||||
const isLoadingQuotesRoute = pathname === LOADING_QUOTES_ROUTE;
|
||||
|
||||
@ -243,7 +246,7 @@ export default function Swap() {
|
||||
<div className="swaps__container">
|
||||
<div className="swaps__header">
|
||||
<div className="swaps__title">{t('swap')}</div>
|
||||
{!isAwaitingSwapRoute && (
|
||||
{!isAwaitingSwapRoute && !isAwaitingSignaturesRoute && (
|
||||
<div
|
||||
className="swaps__header-cancel"
|
||||
onClick={async () => {
|
||||
@ -372,6 +375,13 @@ export default function Swap() {
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Route
|
||||
path={AWAITING_SIGNATURES_ROUTE}
|
||||
exact
|
||||
render={() => {
|
||||
return <AwaitingSignatures />;
|
||||
}}
|
||||
/>
|
||||
<Route
|
||||
path={AWAITING_SWAP_ROUTE}
|
||||
exact
|
||||
|
@ -1,5 +1,6 @@
|
||||
@import 'actionable-message/index';
|
||||
@import 'awaiting-swap/index';
|
||||
@import 'awaiting-signatures/index';
|
||||
@import 'build-quote/index';
|
||||
@import 'countdown-timer/index';
|
||||
@import 'dropdown-input-pair/index';
|
||||
|
@ -75,6 +75,16 @@ export function getCurrentKeyring(state) {
|
||||
return keyring;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current wallet is a hardware wallet.
|
||||
* @param {Object} state
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function isHardwareWallet(state) {
|
||||
const keyring = getCurrentKeyring(state);
|
||||
return keyring.type.includes('Hardware');
|
||||
}
|
||||
|
||||
export function getAccountType(state) {
|
||||
const currentKeyring = getCurrentKeyring(state);
|
||||
const type = currentKeyring && currentKeyring.type;
|
||||
|
Loading…
Reference in New Issue
Block a user