1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-12 12:47:14 +01:00
metamask-extension/ui/pages/confirm-approve/confirm-approve.js
Dan J Miller 9d70c60c22
Connect Ledger via WebHID (#12411)
* Connect ledger via webhid if that option is available

* Explicitly setting preference for webhid

* Use ledgerTransportType enum instead of booleans for ledger live and webhid preferences

* Use single setLEdgerTransport preference methods and property

* Temp

* Lint fix

* Unit test fix

* Remove async keyword from setLedgerTransportPreference function definition in preferences controller

* Fix ledgelive setting toggle logic

* Migrate useLedgerLive preference property to ledgerTransportType

* Use shared constants for ledger transport type enums

* Use constant for ledger usb vendor id

* Use correct property to check if ledgerLive preference is set when deciding whether to ask for webhid connection

* Update eth-ledger-bridge-keyring to v0.9.0

* Only show ledger live transaction helper messages if using ledger live

* Only show ledger live part of tutorial if ledger live setting is on

* Fix ledger related prop type errors

* Explicitly use u2f enum instead of empty string as a transport type; default transport type to webhid if available; use constants for u2f and webhid

* Cleanup

* Wrap ledger webhid device request in try/catch

* Clean up

* Lint fix

* Ensure user can easily connect their ledger wallet when they need to.

* Fix locales

* Fix/improve locales changes

* Remove unused isFirefox property from confirm-transaction-base.container.js

* Disable transaction and message signing confirmation if ledger webhid requires connection

* Ensure translation keys for ledger connection options in settings dropdown can be properly detected by verify-locales

* Drop .component from ledger-instruction-field file name

* Move renderLedgerLiveStep to module scope

* Remove ledgerLive from function and message names in ledger-instruction-field

* Wrap ledger connection logic in ledger-instruction-field in try catch

* Clean up signature-request.component.js

* Check whether the signing address, and not the selected address, is a ledger account in singature-request.container

* Ensure ledger instructions and webhid connection button are shown on signature-request-original signatures

* Improve webhid selection handling in select-ledger-transport-type onChange handler

* Move metamask redux focused ledger selectors to metamask duck

* Lint fix

* Use async await in checkWebHidStatusRef.current

* Remove unnecessary use of ref in ledger-instruction-field.js

* Lint fix

* Remove unnecessary try/catch in ledger-instruction-field.js

* Check if from address, not selected address, is from a ledger account in confirm-approve

* Move findKeyringForAddress to metamask duck

* Fix typo in function name

* Ensure isEqualCaseInsensitive handles possible differences in address casing

* Fix Learn More link size in advanced settings tab

* Update app/scripts/migrations/066.js

Co-authored-by: Mark Stacey <markjstacey@gmail.com>

* Update ui/pages/settings/advanced-tab/advanced-tab.component.test.js

Co-authored-by: Mark Stacey <markjstacey@gmail.com>

* Add jsdoc comments for new selectors

* Use jest.spyOn for mocking navigator in ledger webhid migration tests

* Use LEDGER_TRANSPORT_TYPES values to set proptype of ledgerTransportType

* Use LEDGER_TRANSPORT_TYPES values to set proptype of ledgerTransportType

* Fix font size of link in ledger connection description in advanced settings

* Fix return type in setLedgerTransportPreference comment

* Clean up connectHardware code for webhid connection in actions.js

* Update app/scripts/migrations/066.test.js

Co-authored-by: Mark Stacey <markjstacey@gmail.com>

* Update ui/ducks/metamask/metamask.js

Co-authored-by: Mark Stacey <markjstacey@gmail.com>

* Add migration test for when useLedgerLive is true in a browser that supports webhid

* Lint fix

* Fix inline-link size

Co-authored-by: Mark Stacey <markjstacey@gmail.com>
2021-10-21 16:47:03 -02:30

240 lines
7.9 KiB
JavaScript

import React, { useEffect, useRef, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useParams } from 'react-router-dom';
import ConfirmTransactionBase from '../confirm-transaction-base';
import { EDIT_GAS_MODES } from '../../../shared/constants/gas';
import {
showModal,
updateCustomNonce,
getNextNonce,
} from '../../store/actions';
import { getTokenData } from '../../helpers/utils/transactions.util';
import {
calcTokenAmount,
getTokenAddressParam,
getTokenValueParam,
} from '../../helpers/utils/token-util';
import { useTokenTracker } from '../../hooks/useTokenTracker';
import { getTokens, getNativeCurrency } from '../../ducks/metamask/metamask';
import {
transactionFeeSelector,
txDataSelector,
getCurrentCurrency,
getDomainMetadata,
getUseNonceField,
getCustomNonceValue,
getNextSuggestedNonce,
doesAddressRequireLedgerHidConnection,
} from '../../selectors';
import { useApproveTransaction } from '../../hooks/useApproveTransaction';
import { currentNetworkTxListSelector } from '../../selectors/transactions';
import Loading from '../../components/ui/loading-screen';
import EditGasPopover from '../../components/app/edit-gas-popover/edit-gas-popover.component';
import { isEqualCaseInsensitive } from '../../helpers/utils/util';
import { getCustomTxParamsData } from './confirm-approve.util';
import ConfirmApproveContent from './confirm-approve-content';
const doesAddressRequireLedgerHidConnectionByFromAddress = (address) => (
state,
) => {
return doesAddressRequireLedgerHidConnection(state, address);
};
export default function ConfirmApprove() {
const dispatch = useDispatch();
const { id: paramsTransactionId } = useParams();
const {
id: transactionId,
txParams: { to: tokenAddress, data, from } = {},
} = useSelector(txDataSelector);
const currentCurrency = useSelector(getCurrentCurrency);
const nativeCurrency = useSelector(getNativeCurrency);
const currentNetworkTxList = useSelector(currentNetworkTxListSelector);
const domainMetadata = useSelector(getDomainMetadata);
const tokens = useSelector(getTokens);
const useNonceField = useSelector(getUseNonceField);
const nextNonce = useSelector(getNextSuggestedNonce);
const customNonceValue = useSelector(getCustomNonceValue);
const ledgerWalletRequiredHidConnection = useSelector(
doesAddressRequireLedgerHidConnectionByFromAddress(from),
);
const transaction =
currentNetworkTxList.find(
({ id }) => id === (Number(paramsTransactionId) || transactionId),
) || {};
const { ethTransactionTotal, fiatTransactionTotal } = useSelector((state) =>
transactionFeeSelector(state, transaction),
);
const currentToken = (tokens &&
tokens.find(({ address }) =>
isEqualCaseInsensitive(tokenAddress, address),
)) || {
address: tokenAddress,
};
const { tokensWithBalances } = useTokenTracker([currentToken]);
const tokenTrackerBalance = tokensWithBalances[0]?.balance || '';
const tokenSymbol = currentToken?.symbol;
const decimals = Number(currentToken?.decimals);
const tokenData = getTokenData(data);
const tokenValue = getTokenValueParam(tokenData);
const toAddress = getTokenAddressParam(tokenData);
const tokenAmount =
tokenData && calcTokenAmount(tokenValue, decimals).toString(10);
const [customPermissionAmount, setCustomPermissionAmount] = useState('');
const previousTokenAmount = useRef(tokenAmount);
const {
approveTransaction,
showCustomizeGasPopover,
closeCustomizeGasPopover,
} = useApproveTransaction();
useEffect(() => {
if (customPermissionAmount && previousTokenAmount.current !== tokenAmount) {
setCustomPermissionAmount(tokenAmount);
}
previousTokenAmount.current = tokenAmount;
}, [customPermissionAmount, tokenAmount]);
const [submitWarning, setSubmitWarning] = useState('');
const prevNonce = useRef(nextNonce);
const prevCustomNonce = useRef(customNonceValue);
useEffect(() => {
if (
prevNonce.current !== nextNonce ||
prevCustomNonce.current !== customNonceValue
) {
if (nextNonce !== null && customNonceValue > nextNonce) {
setSubmitWarning(
`Nonce is higher than suggested nonce of ${nextNonce}`,
);
} else {
setSubmitWarning('');
}
}
prevCustomNonce.current = customNonceValue;
prevNonce.current = nextNonce;
}, [customNonceValue, nextNonce]);
const { origin } = transaction;
const formattedOrigin = origin
? origin[0].toUpperCase() + origin.slice(1)
: '';
const { icon: siteImage = '' } = domainMetadata[origin] || {};
const tokensText = `${Number(tokenAmount)} ${tokenSymbol}`;
const tokenBalance = tokenTrackerBalance
? calcTokenAmount(tokenTrackerBalance, decimals).toString(10)
: '';
const customData = customPermissionAmount
? getCustomTxParamsData(data, { customPermissionAmount, decimals })
: null;
return tokenSymbol === undefined ? (
<Loading />
) : (
<ConfirmTransactionBase
toAddress={toAddress}
identiconAddress={tokenAddress}
showAccountInHeader
title={tokensText}
contentComponent={
<>
<ConfirmApproveContent
decimals={decimals}
siteImage={siteImage}
setCustomAmount={setCustomPermissionAmount}
customTokenAmount={String(customPermissionAmount)}
tokenAmount={tokenAmount}
origin={formattedOrigin}
tokenSymbol={tokenSymbol}
tokenBalance={tokenBalance}
showCustomizeGasModal={approveTransaction}
showEditApprovalPermissionModal={({
/* eslint-disable no-shadow */
customTokenAmount,
decimals,
origin,
setCustomAmount,
tokenAmount,
tokenBalance,
tokenSymbol,
/* eslint-enable no-shadow */
}) =>
dispatch(
showModal({
name: 'EDIT_APPROVAL_PERMISSION',
customTokenAmount,
decimals,
origin,
setCustomAmount,
tokenAmount,
tokenBalance,
tokenSymbol,
}),
)
}
data={customData || data}
toAddress={toAddress}
currentCurrency={currentCurrency}
nativeCurrency={nativeCurrency}
ethTransactionTotal={ethTransactionTotal}
fiatTransactionTotal={fiatTransactionTotal}
useNonceField={useNonceField}
nextNonce={nextNonce}
customNonceValue={customNonceValue}
updateCustomNonce={(value) => {
dispatch(updateCustomNonce(value));
}}
getNextNonce={() => dispatch(getNextNonce())}
showCustomizeNonceModal={({
/* eslint-disable no-shadow */
useNonceField,
nextNonce,
customNonceValue,
updateCustomNonce,
getNextNonce,
/* eslint-disable no-shadow */
}) =>
dispatch(
showModal({
name: 'CUSTOMIZE_NONCE',
useNonceField,
nextNonce,
customNonceValue,
updateCustomNonce,
getNextNonce,
}),
)
}
warning={submitWarning}
txData={transaction}
ledgerWalletRequiredHidConnection={
ledgerWalletRequiredHidConnection
}
/>
{showCustomizeGasPopover && (
<EditGasPopover
onClose={closeCustomizeGasPopover}
mode={EDIT_GAS_MODES.MODIFY_IN_PLACE}
transaction={transaction}
/>
)}
</>
}
hideSenderToRecipient
customTxParamsData={customData}
/>
);
}