From 5892acab81ea4a19cf2ab987320b497fa81b7e36 Mon Sep 17 00:00:00 2001 From: Jyoti Puri Date: Wed, 19 Apr 2023 04:53:45 +0530 Subject: [PATCH] Update gas limit on token allowance change (#18524) * Update gas limit on token allowance change * Fix unit test case * lint: remove unused var txParams * fix * fix * fix e2e * fix e2e * fix e2e * fix e2e * fix e2e * fix build * fix build * fix build * fix * fix * fix * fix * fix --------- Co-authored-by: legobeat <109787230+legobeat@users.noreply.github.com> Co-authored-by: legobt <6wbvkn0j@anonaddy.me> --- test/data/mock-state.json | 3 +- .../custom-spending-cap.test.js.snap | 107 ++++++++++++++++++ .../custom-spending-cap.js | 46 +++++++- .../custom-spending-cap.test.js | 61 ++++++++++ ui/pages/token-allowance/token-allowance.js | 7 +- .../token-allowance/token-allowance.test.js | 10 +- 6 files changed, 228 insertions(+), 6 deletions(-) create mode 100644 ui/components/app/custom-spending-cap/__snapshots__/custom-spending-cap.test.js.snap create mode 100644 ui/components/app/custom-spending-cap/custom-spending-cap.test.js diff --git a/test/data/mock-state.json b/test/data/mock-state.json index 41d995b9e..b7a08d5c5 100644 --- a/test/data/mock-state.json +++ b/test/data/mock-state.json @@ -16,7 +16,8 @@ "name": null } }, - "warning": null + "warning": null, + "customTokenAmount": "10" }, "history": { "mostRecentOverviewPage": "/mostRecentOverviewPage" diff --git a/ui/components/app/custom-spending-cap/__snapshots__/custom-spending-cap.test.js.snap b/ui/components/app/custom-spending-cap/__snapshots__/custom-spending-cap.test.js.snap new file mode 100644 index 000000000..ba7f51b89 --- /dev/null +++ b/ui/components/app/custom-spending-cap/__snapshots__/custom-spending-cap.test.js.snap @@ -0,0 +1,107 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CustomSpendingCap should match snapshot 1`] = ` +
+
+
+ +
+
+
+`; diff --git a/ui/components/app/custom-spending-cap/custom-spending-cap.js b/ui/components/app/custom-spending-cap/custom-spending-cap.js index 7f4a46c26..23e8d16ee 100644 --- a/ui/components/app/custom-spending-cap/custom-spending-cap.js +++ b/ui/components/app/custom-spending-cap/custom-spending-cap.js @@ -3,6 +3,8 @@ import { useDispatch, useSelector } from 'react-redux'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import BigNumber from 'bignumber.js'; +import { addHexPrefix } from 'ethereumjs-util'; + import { I18nContext } from '../../../contexts/i18n'; import Box from '../../ui/box'; import FormField from '../../ui/form-field'; @@ -23,24 +25,31 @@ import { import { getCustomTokenAmount } from '../../../selectors'; import { setCustomTokenAmount } from '../../../ducks/app/app'; import { calcTokenAmount } from '../../../../shared/lib/transactions-controller-utils'; +import { hexToDecimal } from '../../../../shared/modules/conversion.utils'; import { MAX_TOKEN_ALLOWANCE_AMOUNT, NUM_W_OPT_DECIMAL_COMMA_OR_DOT_REGEX, DECIMAL_REGEX, } from '../../../../shared/constants/tokens'; import { Numeric } from '../../../../shared/modules/Numeric'; +import { estimateGas } from '../../../store/actions'; +import { getCustomTxParamsData } from '../../../pages/confirm-approve/confirm-approve.util'; +import { useGasFeeContext } from '../../../contexts/gasFee'; import { CustomSpendingCapTooltip } from './custom-spending-cap-tooltip'; export default function CustomSpendingCap({ + txParams, tokenName, currentTokenBalance, dappProposedValue, siteOrigin, passTheErrorText, decimals, + setInputChangeInProgress, }) { const t = useContext(I18nContext); const dispatch = useDispatch(); + const { updateTransaction } = useGasFeeContext(); const inputRef = useRef(null); const value = useSelector(getCustomTokenAmount); @@ -97,12 +106,17 @@ export default function CustomSpendingCap({ getInputTextLogic(value).description, ); - const handleChange = (valueInput) => { + const handleChange = async (valueInput) => { + if (!txParams) { + return; + } + setInputChangeInProgress(true); let spendingCapError = ''; const inputTextLogic = getInputTextLogic(valueInput); const inputTextLogicDescription = inputTextLogic.description; const match = DECIMAL_REGEX.exec(replaceCommaToDot(valueInput)); if (match?.[1]?.length > decimals) { + setInputChangeInProgress(false); return; } @@ -128,6 +142,28 @@ export default function CustomSpendingCap({ } dispatch(setCustomTokenAmount(String(valueInput))); + + try { + const newData = getCustomTxParamsData(txParams.data, { + customPermissionAmount: valueInput, + decimals, + }); + const { from, to, value: txValue } = txParams; + const estimatedGasLimit = await estimateGas({ + from, + to, + value: txValue, + data: newData, + }); + if (estimatedGasLimit) { + await updateTransaction({ + gasLimit: hexToDecimal(addHexPrefix(estimatedGasLimit)), + }); + } + } catch (exp) { + console.error('Error in trying to update gas limit', exp); + } + setInputChangeInProgress(false); }; useEffect(() => { @@ -278,6 +314,10 @@ export default function CustomSpendingCap({ } CustomSpendingCap.propTypes = { + /** + * Transaction params + */ + txParams: PropTypes.object.isRequired, /** * Displayed the token name currently tracked in description related to the input state */ @@ -302,4 +342,8 @@ CustomSpendingCap.propTypes = { * Number of decimals */ decimals: PropTypes.string, + /** + * Updating input state to changing + */ + setInputChangeInProgress: PropTypes.func.isRequired, }; diff --git a/ui/components/app/custom-spending-cap/custom-spending-cap.test.js b/ui/components/app/custom-spending-cap/custom-spending-cap.test.js new file mode 100644 index 000000000..17f475691 --- /dev/null +++ b/ui/components/app/custom-spending-cap/custom-spending-cap.test.js @@ -0,0 +1,61 @@ +import React from 'react'; +import configureMockStore from 'redux-mock-store'; +import thunk from 'redux-thunk'; +import userEvent from '@testing-library/user-event'; + +import mockState from '../../../../test/data/mock-state.json'; +import { renderWithProvider } from '../../../../test/lib/render-helpers'; +import * as Actions from '../../../store/actions'; +import * as GasFeeContext from '../../../contexts/gasFee'; +import CustomSpendingCap from './custom-spending-cap'; + +const props = { + txParams: { + data: '0x095ea7b30000000000000000000000009bc5baf874d2da8d216ae9f137804184ee5afef40000000000000000000000000000000000000000000000000000000000011170', + from: '0x8eeee1781fd885ff5ddef7789486676961873d12', + gas: '0xb41b', + maxFeePerGas: '0x4a817c800', + maxPriorityFeePerGas: '0x4a817c800', + to: '0x665933d73375e385bef40abcccea8b4cccc32d4c', + value: '0x0', + }, + tokenName: 'TST', + currentTokenBalance: '10', + dappProposedValue: '7', + siteOrigin: 'https://metamask.github.io', + decimals: '4', + passTheErrorText: () => undefined, + setInputChangeInProgress: () => undefined, +}; + +describe('CustomSpendingCap', () => { + const store = configureMockStore([thunk])(mockState); + + it('should match snapshot', () => { + const { container } = renderWithProvider( + , + store, + ); + expect(container).toMatchSnapshot(); + }); + + it('should change in token allowance amount should call functions to update gas limit', async () => { + const user = userEvent.setup(); + const spyEstimateGas = jest + .spyOn(Actions, 'estimateGas') + .mockReturnValue(Promise.resolve('1770')); + const updateTransactionMock = jest.fn(); + jest + .spyOn(GasFeeContext, 'useGasFeeContext') + .mockImplementation(() => ({ updateTransaction: updateTransactionMock })); + + const { getByRole } = renderWithProvider( + , + store, + ); + await user.type(getByRole('textbox'), '5'); + + expect(spyEstimateGas).toHaveBeenCalledTimes(1); + expect(updateTransactionMock).toHaveBeenCalledTimes(1); + }); +}); diff --git a/ui/pages/token-allowance/token-allowance.js b/ui/pages/token-allowance/token-allowance.js index 66d44b774..10cc6ced0 100644 --- a/ui/pages/token-allowance/token-allowance.js +++ b/ui/pages/token-allowance/token-allowance.js @@ -105,6 +105,7 @@ export default function TokenAllowance({ const thisOriginIsAllowedToSkipFirstPage = ALLOWED_HOSTS.includes(hostname); const [showContractDetails, setShowContractDetails] = useState(false); + const [inputChangeInProgress, setInputChangeInProgress] = useState(false); const [showFullTxDetails, setShowFullTxDetails] = useState(false); const [isFirstPage, setIsFirstPage] = useState( dappProposedTokenAmount !== '0' && !thisOriginIsAllowedToSkipFirstPage, @@ -402,12 +403,14 @@ export default function TokenAllowance({ {isFirstPage ? ( setErrorText(value)} decimals={decimals} + setInputChangeInProgress={setInputChangeInProgress} /> ) : ( handleReject()} onSubmit={() => (isFirstPage ? handleNextClick() : handleApprove())} - disabled={disableNextButton || disableApproveButton} + disabled={ + inputChangeInProgress || disableNextButton || disableApproveButton + } > {unapprovedTxCount > 1 && (