mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 09:57:02 +01:00
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>
This commit is contained in:
parent
39f6042e65
commit
5892acab81
@ -16,7 +16,8 @@
|
||||
"name": null
|
||||
}
|
||||
},
|
||||
"warning": null
|
||||
"warning": null,
|
||||
"customTokenAmount": "10"
|
||||
},
|
||||
"history": {
|
||||
"mostRecentOverviewPage": "/mostRecentOverviewPage"
|
||||
|
@ -0,0 +1,107 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`CustomSpendingCap should match snapshot 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="box custom-spending-cap box--padding-top-2 box--padding-right-6 box--padding-left-6 box--display-flex box--gap-2 box--flex-direction-column box--align-items-flex-start box--background-color-background-alternative box--rounded-sm"
|
||||
>
|
||||
<div
|
||||
class="box custom-spending-cap__input box--display-block box--flex-direction-row box--justify-content-center"
|
||||
>
|
||||
<label
|
||||
for="custom-spending-cap"
|
||||
>
|
||||
<div
|
||||
class="form-field"
|
||||
>
|
||||
<div
|
||||
class="box box--flex-direction-row"
|
||||
>
|
||||
<div
|
||||
class="form-field__heading"
|
||||
>
|
||||
<div
|
||||
class="box form-field__heading-title box--display-flex box--flex-direction-row box--align-items-baseline"
|
||||
>
|
||||
<h6
|
||||
class="box box--margin-top-1 box--margin-bottom-1 box--display-inline-block box--flex-direction-row typography typography--h6 typography--weight-bold typography--style-normal typography--color-text-default"
|
||||
>
|
||||
Custom spending cap
|
||||
</h6>
|
||||
|
||||
<div
|
||||
class="box box--display-inline-block box--flex-direction-row"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
aria-describedby="tippy-tooltip-1"
|
||||
class=""
|
||||
data-original-title="null"
|
||||
data-tooltipped=""
|
||||
style="display: inline;"
|
||||
tabindex="0"
|
||||
>
|
||||
<span
|
||||
class="box mm-icon mm-icon--size-md box--display-inline-block box--flex-direction-row box--color-inherit"
|
||||
style="mask-image: url('./images/icons/question.svg');"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="box form-field__heading-detail box--margin-bottom-2 box--flex-direction-row box--text-align-end"
|
||||
>
|
||||
<button
|
||||
class="box mm-text mm-button-base mm-button-link mm-button-link--size-auto mm-text--body-md box--display-inline-flex box--flex-direction-row box--justify-content-center box--align-items-center box--color-primary-default box--background-color-transparent"
|
||||
>
|
||||
Use default
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
class="form-field__input"
|
||||
data-testid="custom-spending-cap-input"
|
||||
id="custom-spending-cap"
|
||||
placeholder="Enter a number"
|
||||
type="text"
|
||||
value="10"
|
||||
/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="box custom-spending-cap__max box--margin-left-auto box--padding-right-4 box--padding-bottom-2 box--flex-direction-row box--text-align-end box--width-max"
|
||||
>
|
||||
<button
|
||||
class="box mm-text mm-button-base mm-button-link mm-button-link--size-auto mm-text--body-md box--display-inline-flex box--flex-direction-row box--justify-content-center box--align-items-center box--color-primary-default box--background-color-transparent"
|
||||
>
|
||||
Max
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="box custom-spending-cap__description box--flex-direction-row"
|
||||
>
|
||||
<h6
|
||||
class="box mm-text mm-text--body-sm box--padding-top-2 box--padding-bottom-2 box--flex-direction-row box--color-text-default"
|
||||
>
|
||||
<span>
|
||||
|
||||
This allows the third party to spend
|
||||
<h6
|
||||
class="box mm-text custom-spending-cap__input-value-and-token-name mm-text--body-sm-bold box--flex-direction-row box--color-text-default"
|
||||
>
|
||||
10
|
||||
|
||||
TST
|
||||
</h6>
|
||||
from your current balance.
|
||||
|
||||
</span>
|
||||
</h6>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -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,
|
||||
};
|
||||
|
@ -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(
|
||||
<CustomSpendingCap {...props} />,
|
||||
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(
|
||||
<CustomSpendingCap {...props} />,
|
||||
store,
|
||||
);
|
||||
await user.type(getByRole('textbox'), '5');
|
||||
|
||||
expect(spyEstimateGas).toHaveBeenCalledTimes(1);
|
||||
expect(updateTransactionMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
@ -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({
|
||||
<Box margin={[4, 4, 3, 4]}>
|
||||
{isFirstPage ? (
|
||||
<CustomSpendingCap
|
||||
txParams={txData?.txParams}
|
||||
tokenName={tokenSymbol}
|
||||
currentTokenBalance={currentTokenBalance}
|
||||
dappProposedValue={dappProposedTokenAmount}
|
||||
siteOrigin={origin}
|
||||
passTheErrorText={(value) => setErrorText(value)}
|
||||
decimals={decimals}
|
||||
setInputChangeInProgress={setInputChangeInProgress}
|
||||
/>
|
||||
) : (
|
||||
<ReviewSpendingCap
|
||||
@ -525,7 +528,9 @@ export default function TokenAllowance({
|
||||
submitText={isFirstPage ? t('next') : t('approveButtonText')}
|
||||
onCancel={() => handleReject()}
|
||||
onSubmit={() => (isFirstPage ? handleNextClick() : handleApprove())}
|
||||
disabled={disableNextButton || disableApproveButton}
|
||||
disabled={
|
||||
inputChangeInProgress || disableNextButton || disableApproveButton
|
||||
}
|
||||
>
|
||||
{unapprovedTxCount > 1 && (
|
||||
<Button
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import configureMockStore from 'redux-mock-store';
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import { act, fireEvent } from '@testing-library/react';
|
||||
import { renderWithProvider } from '../../../test/lib/render-helpers';
|
||||
import { KeyringType } from '../../../shared/constants/keyring';
|
||||
import TokenAllowance from './token-allowance';
|
||||
@ -96,12 +96,14 @@ jest.mock('../../store/actions', () => ({
|
||||
updatePreviousGasParams: () => ({ type: 'UPDATE_TRANSACTION_PARAMS' }),
|
||||
createTransactionEventFragment: jest.fn(),
|
||||
updateCustomNonce: () => ({ type: 'UPDATE_TRANSACTION_PARAMS' }),
|
||||
estimateGas: jest.fn().mockImplementation(() => Promise.resolve()),
|
||||
}));
|
||||
|
||||
jest.mock('../../contexts/gasFee', () => ({
|
||||
useGasFeeContext: () => ({
|
||||
maxPriorityFeePerGas: '0.1',
|
||||
maxFeePerGas: '0.1',
|
||||
updateTransaction: jest.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
@ -215,8 +217,10 @@ describe('TokenAllowancePage', () => {
|
||||
store,
|
||||
);
|
||||
|
||||
const useDefault = getByText('Use default');
|
||||
fireEvent.click(useDefault);
|
||||
act(() => {
|
||||
const useDefault = getByText('Use default');
|
||||
fireEvent.click(useDefault);
|
||||
});
|
||||
|
||||
const input = getByTestId('custom-spending-cap-input');
|
||||
expect(input.value).toBe('1');
|
||||
|
Loading…
Reference in New Issue
Block a user