Skip to content

Commit 54d7046

Browse files
feat: save custom gas settings
1 parent 7593eaa commit 54d7046

22 files changed

Lines changed: 534 additions & 38 deletions

app/components/Views/confirmations/components/modals/advanced-eip1559-modal/advanced-eip1559-modal.test.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@ import { simpleSendTransaction } from '../../../__mocks__/controllers/transactio
77
import { GasModalType } from '../../../constants/gas';
88
import { AdvancedEIP1559Modal } from './advanced-eip1559-modal';
99

10+
const mockPersistGasFeePreference = jest.fn();
11+
1012
jest.mock('../../../../../../util/transaction-controller');
13+
jest.mock('../../../hooks/gas/usePersistGasFeePreference', () => ({
14+
usePersistGasFeePreference: jest.fn(() => mockPersistGasFeePreference),
15+
}));
1116
jest.mock('../../../hooks/transactions/useTransactionMetadataRequest', () => {
1217
const { simpleSendTransaction: actualSimpleSendTransaction } =
1318
jest.requireActual(
@@ -73,6 +78,14 @@ describe('AdvancedEIP1559Modal', () => {
7378
userFeeLevel: 'custom',
7479
}),
7580
);
81+
expect(mockPersistGasFeePreference).toHaveBeenCalledWith(
82+
simpleSendTransaction,
83+
{
84+
userFeeLevel: 'custom',
85+
maxBaseFee: simpleSendTransaction.txParams.maxFeePerGas,
86+
priorityFee: simpleSendTransaction.txParams.maxPriorityFeePerGas,
87+
},
88+
);
7689
expect(mockHandleCloseModals).toHaveBeenCalledTimes(1);
7790
});
7891

@@ -108,6 +121,14 @@ describe('AdvancedEIP1559Modal', () => {
108121
userFeeLevel: 'custom',
109122
}),
110123
);
124+
expect(mockPersistGasFeePreference).toHaveBeenCalledWith(
125+
simpleSendTransaction,
126+
{
127+
userFeeLevel: 'custom',
128+
maxBaseFee: '0x174876e800',
129+
priorityFee: '0x12a05f200',
130+
},
131+
);
111132
});
112133

113134
it('calls navigateToEstimatesModal when the back button is pressed', () => {

app/components/Views/confirmations/components/modals/advanced-eip1559-modal/advanced-eip1559-modal.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { GasInput } from '../../../components/gas/gas-input';
2323
import { MaxBaseFeeInput } from '../../../components/gas/max-base-fee-input';
2424
import { PriorityFeeInput } from '../../../components/gas/priority-fee-input';
2525
import styleSheet from './advanced-eip1559-modal.styles';
26+
import { usePersistGasFeePreference } from '../../../hooks/gas/usePersistGasFeePreference';
2627

2728
export const AdvancedEIP1559Modal = ({
2829
setActiveModal,
@@ -33,6 +34,7 @@ export const AdvancedEIP1559Modal = ({
3334
}) => {
3435
const { styles } = useStyles(styleSheet, {});
3536
const transactionMeta = useTransactionMetadataRequest() as TransactionMeta;
37+
const persistGasFeePreference = usePersistGasFeePreference();
3638

3739
const { gas, maxFeePerGas, maxPriorityFeePerGas } =
3840
transactionMeta?.txParams || {};
@@ -61,8 +63,18 @@ export const AdvancedEIP1559Modal = ({
6163
userFeeLevel: UserFeeLevel.CUSTOM,
6264
...pickBy(gasParams, Boolean),
6365
});
66+
persistGasFeePreference(transactionMeta, {
67+
userFeeLevel: UserFeeLevel.CUSTOM,
68+
...pickBy(
69+
{
70+
maxBaseFee: gasParams.maxFeePerGas,
71+
priorityFee: gasParams.maxPriorityFeePerGas,
72+
},
73+
Boolean,
74+
),
75+
});
6476
handleCloseModals();
65-
}, [transactionMeta.id, gasParams, handleCloseModals]);
77+
}, [transactionMeta, gasParams, persistGasFeePreference, handleCloseModals]);
6678

6779
const navigateToEstimatesModal = useCallback(() => {
6880
setActiveModal(GasModalType.ESTIMATES);

app/components/Views/confirmations/components/modals/advanced-gas-price-modal/advanced-gas-price-modal.test.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@ import { simpleSendTransaction } from '../../../__mocks__/controllers/transactio
66
import { GasModalType } from '../../../constants/gas';
77
import { AdvancedGasPriceModal } from './advanced-gas-price-modal';
88

9+
const mockPersistGasFeePreference = jest.fn();
10+
911
jest.mock('../../../../../../util/transaction-controller');
12+
jest.mock('../../../hooks/gas/usePersistGasFeePreference', () => ({
13+
usePersistGasFeePreference: jest.fn(() => mockPersistGasFeePreference),
14+
}));
1015
jest.mock('../../../hooks/transactions/useTransactionMetadataRequest', () => {
1116
const { simpleSendTransaction: actualSimpleSendTransaction } =
1217
jest.requireActual(
@@ -60,6 +65,12 @@ describe('AdvancedGasPriceModal', () => {
6065
userFeeLevel: 'custom',
6166
}),
6267
);
68+
expect(mockPersistGasFeePreference).toHaveBeenCalledWith(
69+
simpleSendTransaction,
70+
{
71+
userFeeLevel: 'custom',
72+
},
73+
);
6374
expect(mockHandleCloseModals).toHaveBeenCalledTimes(1);
6475
});
6576

@@ -91,6 +102,13 @@ describe('AdvancedGasPriceModal', () => {
91102
userFeeLevel: 'custom',
92103
}),
93104
);
105+
expect(mockPersistGasFeePreference).toHaveBeenCalledWith(
106+
simpleSendTransaction,
107+
{
108+
userFeeLevel: 'custom',
109+
gasPrice: '0x37e11d600',
110+
},
111+
);
94112
});
95113

96114
it('calls navigateToEstimatesModal when the back button is pressed', () => {

app/components/Views/confirmations/components/modals/advanced-gas-price-modal/advanced-gas-price-modal.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ import React, { useCallback, useMemo, useState } from 'react';
22
import { View } from 'react-native';
33
import { Hex } from '@metamask/utils';
44
import { pickBy } from 'lodash';
5-
import { TransactionMeta } from '@metamask/transaction-controller';
5+
import {
6+
TransactionMeta,
7+
UserFeeLevel,
8+
} from '@metamask/transaction-controller';
69

710
import { useStyles } from '../../../../../../component-library/hooks';
811
import {
@@ -19,6 +22,7 @@ import { GasPriceInput } from '../../../components/gas/gas-price-input';
1922
import { useTransactionMetadataRequest } from '../../../hooks/transactions/useTransactionMetadataRequest';
2023
import BottomModal from '../../UI/bottom-modal';
2124
import styleSheet from './advanced-gas-price-modal.styles';
25+
import { usePersistGasFeePreference } from '../../../hooks/gas/usePersistGasFeePreference';
2226

2327
export const AdvancedGasPriceModal = ({
2428
setActiveModal,
@@ -29,6 +33,7 @@ export const AdvancedGasPriceModal = ({
2933
}) => {
3034
const { styles } = useStyles(styleSheet, {});
3135
const transactionMeta = useTransactionMetadataRequest() as TransactionMeta;
36+
const persistGasFeePreference = usePersistGasFeePreference();
3237

3338
const { gas, gasPrice } = transactionMeta?.txParams || {};
3439

@@ -48,11 +53,15 @@ export const AdvancedGasPriceModal = ({
4853

4954
const handleSaveClick = useCallback(() => {
5055
updateTransactionGasFees(transactionMeta.id, {
51-
userFeeLevel: 'custom',
56+
userFeeLevel: UserFeeLevel.CUSTOM,
5257
...pickBy(gasParams, Boolean),
5358
});
59+
persistGasFeePreference(transactionMeta, {
60+
userFeeLevel: UserFeeLevel.CUSTOM,
61+
...pickBy({ gasPrice: gasParams.gasPrice }, Boolean),
62+
});
5463
handleCloseModals();
55-
}, [transactionMeta.id, gasParams, handleCloseModals]);
64+
}, [transactionMeta, gasParams, persistGasFeePreference, handleCloseModals]);
5665

5766
const navigateToEstimatesModal = useCallback(() => {
5867
setActiveModal(GasModalType.ESTIMATES);

app/components/Views/confirmations/hooks/gas/useGasFeeEstimateLevelOptions.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,18 @@ import { updateTransactionGasFees } from '../../../../../util/transaction-contro
1414
import { useGasFeeEstimateLevelOptions } from './useGasFeeEstimateLevelOptions';
1515
import '../../utils/time';
1616

17+
const mockPersistGasFeePreference = jest.fn();
18+
1719
jest.mock('../../../../../util/transaction-controller');
1820
jest.mock('../../utils/time', () => ({
1921
toHumanEstimatedTimeRange: jest.fn().mockReturnValue('~1 min'),
2022
}));
2123
jest.mock('../transactions/useTransactionMetadataRequest');
2224
jest.mock('./useFeeCalculations');
2325
jest.mock('./useGasFeeEstimates');
26+
jest.mock('./usePersistGasFeePreference', () => ({
27+
usePersistGasFeePreference: jest.fn(() => mockPersistGasFeePreference),
28+
}));
2429

2530
describe('useGasFeeEstimateLevelOptions', () => {
2631
const mockUseTransactionMetadataRequest = jest.mocked(
@@ -425,6 +430,12 @@ describe('useGasFeeEstimateLevelOptions', () => {
425430
expect(mockUpdateTransactionGasFees).toHaveBeenCalledWith('test-id', {
426431
userFeeLevel: 'low',
427432
});
433+
expect(mockPersistGasFeePreference).toHaveBeenCalledWith(
434+
transactionWithLegacyEstimates,
435+
{
436+
userFeeLevel: 'low',
437+
},
438+
);
428439
expect(mockHandleCloseModals).toHaveBeenCalled();
429440
});
430441

@@ -499,6 +510,12 @@ describe('useGasFeeEstimateLevelOptions', () => {
499510
expect(mockUpdateTransactionGasFees).toHaveBeenCalledWith('test-id', {
500511
userFeeLevel: 'high',
501512
});
513+
expect(mockPersistGasFeePreference).toHaveBeenCalledWith(
514+
transactionWithFeeMarketEstimates,
515+
{
516+
userFeeLevel: 'high',
517+
},
518+
);
502519
expect(mockHandleCloseModals).toHaveBeenCalled();
503520
});
504521

app/components/Views/confirmations/hooks/gas/useGasFeeEstimateLevelOptions.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { useFeeCalculations } from './useFeeCalculations';
1515
import { updateTransactionGasFees } from '../../../../../util/transaction-controller';
1616
import { type GasOption } from '../../types/gas';
1717
import { EMPTY_VALUE_STRING } from '../../constants/gas';
18+
import { usePersistGasFeePreference } from './usePersistGasFeePreference';
1819

1920
const HEX_ZERO = '0x0';
2021

@@ -31,6 +32,7 @@ export const useGasFeeEstimateLevelOptions = ({
3132
gasFeeEstimates: GasFeeEstimates;
3233
};
3334
const { gasFeeEstimates, id, userFeeLevel } = transactionMeta;
35+
const persistGasFeePreference = usePersistGasFeePreference();
3436

3537
const transactionGasFeeEstimates =
3638
gasFeeEstimates as TransactionGasFeeEstimates;
@@ -48,9 +50,10 @@ export const useGasFeeEstimateLevelOptions = ({
4850
updateTransactionGasFees(id, {
4951
userFeeLevel: level,
5052
});
53+
persistGasFeePreference(transactionMeta, { userFeeLevel: level });
5154
handleCloseModals();
5255
},
53-
[id, handleCloseModals],
56+
[id, transactionMeta, persistGasFeePreference, handleCloseModals],
5457
);
5558

5659
const options: GasOption[] = [];

app/components/Views/confirmations/hooks/gas/useGasPriceEstimateOption.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,15 @@ import { useGasFeeEstimates } from './useGasFeeEstimates';
1414
import { updateTransactionGasFees } from '../../../../../util/transaction-controller';
1515
import { useGasPriceEstimateOption } from './useGasPriceEstimateOption';
1616

17+
const mockPersistGasFeePreference = jest.fn();
18+
1719
jest.mock('../../../../../util/transaction-controller');
1820
jest.mock('../transactions/useTransactionMetadataRequest');
1921
jest.mock('./useFeeCalculations');
2022
jest.mock('./useGasFeeEstimates');
23+
jest.mock('./usePersistGasFeePreference', () => ({
24+
usePersistGasFeePreference: jest.fn(() => mockPersistGasFeePreference),
25+
}));
2126

2227
describe('useGasPriceEstimateOption', () => {
2328
const mockUseTransactionMetadataRequest = jest.mocked(
@@ -296,6 +301,12 @@ describe('useGasPriceEstimateOption', () => {
296301
userFeeLevel: 'medium',
297302
gasPrice: '0x1',
298303
});
304+
expect(mockPersistGasFeePreference).toHaveBeenCalledWith(
305+
transactionWithGasPriceEstimates,
306+
{
307+
userFeeLevel: 'medium',
308+
},
309+
);
299310
expect(mockHandleCloseModals).toHaveBeenCalled();
300311
});
301312

@@ -349,6 +360,12 @@ describe('useGasPriceEstimateOption', () => {
349360
maxFeePerGas: '0x1',
350361
maxPriorityFeePerGas: '0x1',
351362
});
363+
expect(mockPersistGasFeePreference).toHaveBeenCalledWith(
364+
transactionWithGasPriceEstimates,
365+
{
366+
userFeeLevel: 'medium',
367+
},
368+
);
352369
expect(mockHandleCloseModals).toHaveBeenCalled();
353370
});
354371
});

app/components/Views/confirmations/hooks/gas/useGasPriceEstimateOption.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { useGasFeeEstimates } from './useGasFeeEstimates';
1313
import { useFeeCalculations } from './useFeeCalculations';
1414
import { type GasOption } from '../../types/gas';
1515
import { EMPTY_VALUE_STRING } from '../../constants/gas';
16+
import { usePersistGasFeePreference } from './usePersistGasFeePreference';
1617

1718
const HEX_ZERO = '0x0';
1819

@@ -23,6 +24,7 @@ export const useGasPriceEstimateOption = ({
2324
}): GasOption[] => {
2425
const transactionMeta = useTransactionMetadataRequest() as TransactionMeta;
2526
const { calculateGasEstimate } = useFeeCalculations(transactionMeta);
27+
const persistGasFeePreference = usePersistGasFeePreference();
2628

2729
const {
2830
gasFeeEstimates,
@@ -71,11 +73,14 @@ export const useGasPriceEstimateOption = ({
7173
userFeeLevel: 'medium',
7274
...gasPropertiesToUpdate,
7375
});
76+
persistGasFeePreference(transactionMeta, { userFeeLevel: 'medium' });
7477
handleCloseModals();
7578
}, [
7679
id,
80+
transactionMeta,
7781
transactionGasFeeEstimates,
7882
transactionEnvelopeType,
83+
persistGasFeePreference,
7984
handleCloseModals,
8085
]);
8186

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { renderHook } from '@testing-library/react-hooks';
2+
import type { TransactionMeta } from '@metamask/transaction-controller';
3+
4+
import { usePersistGasFeePreference } from './usePersistGasFeePreference';
5+
import Engine from '../../../../../core/Engine';
6+
7+
const mockSetAdvancedGasFee = jest.mocked(
8+
Engine.context.PreferencesController.setAdvancedGasFee,
9+
);
10+
11+
describe('usePersistGasFeePreference', () => {
12+
beforeEach(() => {
13+
jest.clearAllMocks();
14+
});
15+
16+
it('persists gas fee preferences for the transaction account and chain', () => {
17+
const transactionMeta = {
18+
chainId: '0x1',
19+
txParams: {
20+
from: '0x123',
21+
},
22+
} as unknown as TransactionMeta;
23+
24+
const { result } = renderHook(() => usePersistGasFeePreference());
25+
26+
result.current(transactionMeta, {
27+
userFeeLevel: 'custom',
28+
maxBaseFee: '0x1',
29+
priorityFee: '0x2',
30+
});
31+
32+
expect(mockSetAdvancedGasFee).toHaveBeenCalledWith({
33+
account: '0x123',
34+
chainId: '0x1',
35+
gasFeePreferences: {
36+
userFeeLevel: 'custom',
37+
maxBaseFee: '0x1',
38+
priorityFee: '0x2',
39+
},
40+
});
41+
});
42+
43+
it('does not persist without an account', () => {
44+
const transactionMeta = {
45+
chainId: '0x1',
46+
txParams: {},
47+
} as unknown as TransactionMeta;
48+
49+
const { result } = renderHook(() => usePersistGasFeePreference());
50+
51+
result.current(transactionMeta, {
52+
userFeeLevel: 'medium',
53+
});
54+
55+
expect(mockSetAdvancedGasFee).not.toHaveBeenCalled();
56+
});
57+
});
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { useCallback } from 'react';
2+
import type { TransactionMeta } from '@metamask/transaction-controller';
3+
import type { Hex } from '@metamask/utils';
4+
5+
import Engine from '../../../../../core/Engine';
6+
import type { AdvancedGasFeePreferences } from '../../../../../core/Engine/controllers/preferences-controller-types';
7+
8+
export function usePersistGasFeePreference() {
9+
return useCallback(
10+
(
11+
transactionMeta: TransactionMeta | undefined,
12+
gasFeePreferences: AdvancedGasFeePreferences,
13+
) => {
14+
const account = transactionMeta?.txParams?.from as Hex | undefined;
15+
const chainId = transactionMeta?.chainId;
16+
17+
if (!account || !chainId) {
18+
return;
19+
}
20+
21+
Engine.context.PreferencesController.setAdvancedGasFee({
22+
account,
23+
chainId,
24+
gasFeePreferences,
25+
});
26+
},
27+
[],
28+
);
29+
}

0 commit comments

Comments
 (0)