1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-22 09:23:21 +01:00

split advancedGasFee by network and erase previous options (#20576)

* Split out advanced gas fees by network and delete old values

* use arrow functions in preferences test

* changes

* added back priorityFeeProperCase to en messages

* update types

* remove case change
This commit is contained in:
Brad Decker 2023-08-26 19:58:26 -05:00 committed by GitHub
parent a35df21b7f
commit 64aef2a1c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 413 additions and 252 deletions

View File

@ -296,12 +296,6 @@
"advancedConfiguration": {
"message": "Erweiterte Einstellungen"
},
"advancedGasFeeDefaultOptIn": {
"message": "Speichern Sie diese $1 als Standard für \"Erweitert\""
},
"advancedGasFeeDefaultOptOut": {
"message": "Immer diese Werte und erweiterte Einstellung als Standard verwenden."
},
"advancedGasFeeModalTitle": {
"message": "Erweiterte Gasgebühr"
},
@ -2495,9 +2489,6 @@
"newTokensImportedTitle": {
"message": "Token importiert"
},
"newValues": {
"message": "neue Werte"
},
"next": {
"message": "Weiter"
},

View File

@ -296,12 +296,6 @@
"advancedConfiguration": {
"message": "Προηγμένη ρύθμιση παραμέτρων"
},
"advancedGasFeeDefaultOptIn": {
"message": "Αποθηκεύστε αυτά τα $1 ως προεπιλογή μου για το \"Προηγμένο\""
},
"advancedGasFeeDefaultOptOut": {
"message": "Να χρησιμοποιούνται πάντα αυτές τις τιμές και η ρύθμιση για προχωρημένους."
},
"advancedGasFeeModalTitle": {
"message": "Προηγμένη χρέωση τελών συναλλαγής"
},
@ -2495,9 +2489,6 @@
"newTokensImportedTitle": {
"message": "Τα token εισήχθησαν"
},
"newValues": {
"message": "νέες τιμές"
},
"next": {
"message": "Επόμενο"
},

View File

@ -300,10 +300,8 @@
"message": "Advanced configuration"
},
"advancedGasFeeDefaultOptIn": {
"message": "Save these $1 as my default for \"Advanced\""
},
"advancedGasFeeDefaultOptOut": {
"message": "Always use these values and advanced setting as default."
"message": "Save these values as my default for the $1 network.",
"description": "$1 is the current network name."
},
"advancedGasFeeModalTitle": {
"message": "Advanced gas fee"
@ -2501,9 +2499,6 @@
"newTokensImportedTitle": {
"message": "Token imported"
},
"newValues": {
"message": "new values"
},
"next": {
"message": "Next"
},

View File

@ -296,12 +296,6 @@
"advancedConfiguration": {
"message": "Configuración avanzada"
},
"advancedGasFeeDefaultOptIn": {
"message": "Guarda estos 1$ como mi valor predeterminado para \"Avanzado\""
},
"advancedGasFeeDefaultOptOut": {
"message": "Usar siempre estos valores y la configuración avanzada como valores predeterminados."
},
"advancedGasFeeModalTitle": {
"message": "Tarifa de gas avanzada"
},
@ -2495,9 +2489,6 @@
"newTokensImportedTitle": {
"message": "Token importado"
},
"newValues": {
"message": "nuevos valores"
},
"next": {
"message": "Siguiente"
},

View File

@ -145,12 +145,6 @@
"advancedBaseGasFeeToolTip": {
"message": "Cuando su transacción se incluya en el bloque, se reembolsará cualquier diferencia entre su tarifa base máxima y la tarifa base real. El importe total se calcula como tarifa base máxima (en GWEI) * límite de gas."
},
"advancedGasFeeDefaultOptIn": {
"message": "Guarda estos 1$ como mi valor predeterminado para \"Avanzado\""
},
"advancedGasFeeDefaultOptOut": {
"message": "Usar siempre estos valores y la configuración avanzada como valores predeterminados."
},
"advancedGasFeeModalTitle": {
"message": "Tarifa de gas avanzada"
},
@ -1410,9 +1404,6 @@
"newPassword": {
"message": "Contraseña nueva (mín. de 8 caracteres)"
},
"newValues": {
"message": "nuevos valores"
},
"next": {
"message": "Siguiente"
},

View File

@ -296,12 +296,6 @@
"advancedConfiguration": {
"message": "Configuration avancée"
},
"advancedGasFeeDefaultOptIn": {
"message": "Enregistrer ces $1 comme valeur par défaut pour «Avancé»"
},
"advancedGasFeeDefaultOptOut": {
"message": "Toujours utiliser par défaut ces valeurs et les paramètres avancés."
},
"advancedGasFeeModalTitle": {
"message": "Frais de carburant avancés"
},
@ -2495,9 +2489,6 @@
"newTokensImportedTitle": {
"message": "Jeton importé"
},
"newValues": {
"message": "nouvelles valeurs"
},
"next": {
"message": "Suivant"
},

View File

@ -296,12 +296,6 @@
"advancedConfiguration": {
"message": "उन्नत कंफिगुरेशन"
},
"advancedGasFeeDefaultOptIn": {
"message": "इन $1 को \"एडवांस\" के लिए मेरे डिफॉल्ट के रूप में सहेजें"
},
"advancedGasFeeDefaultOptOut": {
"message": "हमेशा इन मूल्यों और एडवांस सेटिंग को डिफॉल्ट के रूप में उपयोग करें।"
},
"advancedGasFeeModalTitle": {
"message": "एडवांस गैस शुल्क"
},
@ -2495,9 +2489,6 @@
"newTokensImportedTitle": {
"message": "टोकन इम्पोर्ट हो गया"
},
"newValues": {
"message": "नए मान"
},
"next": {
"message": "अगला"
},

View File

@ -296,12 +296,6 @@
"advancedConfiguration": {
"message": "Konfigurasi lanjutan"
},
"advancedGasFeeDefaultOptIn": {
"message": "Simpan $1 ini sebagai default saya untuk \"Lanjutan\""
},
"advancedGasFeeDefaultOptOut": {
"message": "Selalu gunakan nilai ini dan pengaturan lanjutan sebagai default."
},
"advancedGasFeeModalTitle": {
"message": "Biaya gas lanjutan"
},
@ -2495,9 +2489,6 @@
"newTokensImportedTitle": {
"message": "Token diimpor"
},
"newValues": {
"message": "nilai baru"
},
"next": {
"message": "Berikutnya"
},

View File

@ -210,12 +210,6 @@
"advancedBaseGasFeeToolTip": {
"message": "Quando la tua transazione viene inclusa nel blocco, ogni differenza tra la tua offerta massima di gas e il gas effettivamente utilizzato viene restituita a te. Il totale viene calcolato come offerta massima di gas (in GEWI) * limite di gas."
},
"advancedGasFeeDefaultOptIn": {
"message": "Salva queste $1 come mie preferite per \"Avanzate\""
},
"advancedGasFeeDefaultOptOut": {
"message": "Utilizzare sempre questi valori e l'impostazione avanzata come predefiniti."
},
"advancedGasFeeModalTitle": {
"message": "Tariffa gas avanzata"
},

View File

@ -296,12 +296,6 @@
"advancedConfiguration": {
"message": "詳細設定"
},
"advancedGasFeeDefaultOptIn": {
"message": "これらの$1を「高度な設定」のデフォルトとして保存"
},
"advancedGasFeeDefaultOptOut": {
"message": "常にこれらの値と高度な設定をデフォルトとして使用します。"
},
"advancedGasFeeModalTitle": {
"message": "高度なガス代"
},
@ -2495,9 +2489,6 @@
"newTokensImportedTitle": {
"message": "トークンがインポートされました"
},
"newValues": {
"message": "新しい値"
},
"next": {
"message": "次へ"
},

View File

@ -296,12 +296,6 @@
"advancedConfiguration": {
"message": "고급 옵션"
},
"advancedGasFeeDefaultOptIn": {
"message": "이 $1 옵션을 \"고급\"의 기본값으로 저장합니다"
},
"advancedGasFeeDefaultOptOut": {
"message": "항상 이 값과 고급 설정을 기본값으로 사용합니다."
},
"advancedGasFeeModalTitle": {
"message": "고급 가스 요금"
},
@ -2495,9 +2489,6 @@
"newTokensImportedTitle": {
"message": "불러온 토큰"
},
"newValues": {
"message": "새로운 가치"
},
"next": {
"message": "다음"
},

View File

@ -296,12 +296,6 @@
"advancedConfiguration": {
"message": "Configurações avançadas"
},
"advancedGasFeeDefaultOptIn": {
"message": "Salvar estes $1 como meu padrão para \"Avançado\""
},
"advancedGasFeeDefaultOptOut": {
"message": "Sempre utilizar esses valores e a configuração avançada por padrão."
},
"advancedGasFeeModalTitle": {
"message": "Taxa de gás avançada"
},
@ -2495,9 +2489,6 @@
"newTokensImportedTitle": {
"message": "Token importado"
},
"newValues": {
"message": "novos valores"
},
"next": {
"message": "Próximo"
},

View File

@ -145,12 +145,6 @@
"advancedBaseGasFeeToolTip": {
"message": "Quando a sua transação for incluída no bloco, qualquer diferença entre a sua taxa de base máxima e a taxa de base real será reembolsada. O cálculo do valor total é feito da seguinte forma: taxa de base máxima (em GWEI) * limite de gás."
},
"advancedGasFeeDefaultOptIn": {
"message": "Salvar estes $1 como meu padrão para \"Avançado\""
},
"advancedGasFeeDefaultOptOut": {
"message": "Sempre utilizar esses valores e a configuração avançada por padrão."
},
"advancedGasFeeModalTitle": {
"message": "Taxa de gás avançada"
},
@ -1410,9 +1404,6 @@
"newPassword": {
"message": "Nova senha (no mínimo 8 caracteres)"
},
"newValues": {
"message": "novos valores"
},
"next": {
"message": "Seguinte"
},

View File

@ -296,12 +296,6 @@
"advancedConfiguration": {
"message": "Расширенная конфигурация"
},
"advancedGasFeeDefaultOptIn": {
"message": "Сохранить этот $1 в качестве моего значения по умолчанию для «Дополнительной» настройки"
},
"advancedGasFeeDefaultOptOut": {
"message": "Всегда использовать эти значения и дополнительную настройку по умолчанию."
},
"advancedGasFeeModalTitle": {
"message": "Дополнительная плата за газ"
},
@ -2495,9 +2489,6 @@
"newTokensImportedTitle": {
"message": "Токен импортирован"
},
"newValues": {
"message": "новые значения"
},
"next": {
"message": "Далее"
},

View File

@ -296,12 +296,6 @@
"advancedConfiguration": {
"message": "Advanced na pagsasaayos"
},
"advancedGasFeeDefaultOptIn": {
"message": "I-save itong mga $1bilang aking default para sa \"Advanced\""
},
"advancedGasFeeDefaultOptOut": {
"message": "Laging gamitin ang mga value na ito at advanced setting bilang default."
},
"advancedGasFeeModalTitle": {
"message": "Advanced na gas fee"
},
@ -2495,9 +2489,6 @@
"newTokensImportedTitle": {
"message": "Na-import ang token"
},
"newValues": {
"message": "bagong value"
},
"next": {
"message": "Susunod"
},

View File

@ -296,12 +296,6 @@
"advancedConfiguration": {
"message": "Gelişmiş yapılandırma"
},
"advancedGasFeeDefaultOptIn": {
"message": "\"Gelişmiş\" için şunları varsayılanım olarak kaydet: $1"
},
"advancedGasFeeDefaultOptOut": {
"message": "Varsayılan olarak her zaman bu değerleri ve gelişmiş ayarı kullan."
},
"advancedGasFeeModalTitle": {
"message": "Gelişmiş gaz ücreti"
},
@ -2495,9 +2489,6 @@
"newTokensImportedTitle": {
"message": "Token içe aktarıldı"
},
"newValues": {
"message": "yeni değerler"
},
"next": {
"message": "Sonraki"
},

View File

@ -296,12 +296,6 @@
"advancedConfiguration": {
"message": "Cấu hình nâng cao"
},
"advancedGasFeeDefaultOptIn": {
"message": "Lưu $1 này làm mặc định của tôi cho \"Nâng cao\""
},
"advancedGasFeeDefaultOptOut": {
"message": "Luôn sử dụng các giá trị và thiết lập nâng cao này làm mặc định."
},
"advancedGasFeeModalTitle": {
"message": "Phí gas nâng cao"
},
@ -2495,9 +2489,6 @@
"newTokensImportedTitle": {
"message": "Đã nhập token"
},
"newValues": {
"message": "giá trị mới"
},
"next": {
"message": "Tiếp theo"
},

View File

@ -296,12 +296,6 @@
"advancedConfiguration": {
"message": "高级配置"
},
"advancedGasFeeDefaultOptIn": {
"message": "将这些 $1 保存为“高级”默认值"
},
"advancedGasFeeDefaultOptOut": {
"message": "始终使用这些值和高级设置作为默认值。"
},
"advancedGasFeeModalTitle": {
"message": "高级燃料费"
},
@ -2495,9 +2489,6 @@
"newTokensImportedTitle": {
"message": "已导入代币"
},
"newValues": {
"message": "新的值"
},
"next": {
"message": "下一步"
},

View File

@ -52,6 +52,10 @@ export default class AppStateController extends EventEmitter {
...initState,
qrHardware: {},
nftsDropdownState: {},
// This key is only used for checking if the user had set advancedGasFee
// prior to Migration 92.3 where we split out the setting to support
// multiple networks.
hadAdvancedGasFeesSetPriorToMigration92_3: false,
usedNetworks: {
'0x1': true,
'0x5': true,

View File

@ -66,7 +66,7 @@ export default class PreferencesController {
///: BEGIN:ONLY_INCLUDE_IN(blockaid)
securityAlertsEnabled: false,
///: END:ONLY_INCLUDE_IN
advancedGasFee: null,
advancedGasFee: {},
// WARNING: Do not use feature flags for security-sensitive things.
// Feature flag toggling is available in the global namespace
@ -240,10 +240,18 @@ export default class PreferencesController {
/**
* Setter for the `advancedGasFee` property
*
* @param {object} val - holds the maxBaseFee and PriorityFee that the user set as default advanced settings.
* @param {object} options
* @param {string} options.chainId - The chainId the advancedGasFees should be set on
* @param {object} options.gasFeePreferences - The advancedGasFee options to set
*/
setAdvancedGasFee(val) {
this.store.updateState({ advancedGasFee: val });
setAdvancedGasFee({ chainId, gasFeePreferences }) {
const { advancedGasFee } = this.store.getState();
this.store.updateState({
advancedGasFee: {
...advancedGasFee,
[chainId]: gasFeePreferences,
},
});
}
/**

View File

@ -228,14 +228,14 @@ describe('preferences controller', () => {
});
});
describe('setUse4ByteResolution', function () {
it('should default to true', function () {
describe('setUse4ByteResolution', () => {
it('should default to true', () => {
expect(
preferencesController.store.getState().use4ByteResolution,
).toStrictEqual(true);
});
it('should set the use4ByteResolution property in state', function () {
it('should set the use4ByteResolution property in state', () => {
preferencesController.setUse4ByteResolution(false);
expect(
preferencesController.store.getState().use4ByteResolution,
@ -259,22 +259,27 @@ describe('preferences controller', () => {
});
describe('setAdvancedGasFee', () => {
it('should default to null', () => {
it('should default to an empty object', () => {
expect(
preferencesController.store.getState().advancedGasFee,
).toStrictEqual(null);
).toStrictEqual({});
});
it('should set the setAdvancedGasFee property in state', () => {
preferencesController.setAdvancedGasFee({
maxBaseFee: '1.5',
priorityFee: '2',
chainId: CHAIN_IDS.GOERLI,
gasFeePreferences: {
maxBaseFee: '1.5',
priorityFee: '2',
},
});
expect(
preferencesController.store.getState().advancedGasFee.maxBaseFee,
preferencesController.store.getState().advancedGasFee[CHAIN_IDS.GOERLI]
.maxBaseFee,
).toStrictEqual('1.5');
expect(
preferencesController.store.getState().advancedGasFee.priorityFee,
preferencesController.store.getState().advancedGasFee[CHAIN_IDS.GOERLI]
.priorityFee,
).toStrictEqual('2');
});
});

View File

@ -1938,9 +1938,13 @@ export default class TransactionController extends EventEmitter {
*/
this.getTransactions = (opts) => this.txStateManager.getTransactions(opts);
/** @returns {object} the saved default values for advancedGasFee */
/**
* @returns {object} the saved default values for advancedGasFee
*/
this.getAdvancedGasFee = () =>
this.preferencesStore.getState().advancedGasFee;
this.preferencesStore.getState().advancedGasFee[
this._getCurrentChainId()
];
}
// called once on startup

View File

@ -73,6 +73,7 @@ describe('Transaction Controller', function () {
fromAccount,
fragmentExists,
networkStatusStore,
preferencesStore,
getCurrentChainId,
messengerMock,
resultCallbacksMock,
@ -97,6 +98,7 @@ describe('Transaction Controller', function () {
}).provider;
networkStatusStore = new ObservableStore(currentNetworkStatus);
preferencesStore = new ObservableStore({ advancedGasFee: {} });
fromAccount = getTestAccounts()[0];
const blockTrackerStub = new EventEmitter();
@ -155,6 +157,7 @@ describe('Transaction Controller', function () {
getAccountType: () => 'MetaMask',
getDeviceModel: () => 'N/A',
securityProviderRequest: () => undefined,
preferencesStore,
messenger: messengerMock,
});

View File

@ -0,0 +1,184 @@
import { migrate } from './092.3';
const PREFERENCES_CONTROLLER_MOCK = {
useBlockie: false,
useNonceField: false,
usePhishDetect: true,
dismissSeedBackUpReminder: false,
disabledRpcMethodPreferences: {
eth_sign: false,
},
useMultiAccountBalanceChecker: true,
useTokenDetection: false,
useNftDetection: false,
use4ByteResolution: true,
useCurrencyRateCheck: true,
openSeaEnabled: false,
advancedGasFee: null,
featureFlags: {
showIncomingTransactions: true,
},
knownMethodData: {},
currentLocale: 'EN',
identities: {},
lostIdentities: {},
forgottenPassword: false,
preferences: {
autoLockTimeLimit: undefined,
showFiatInTestnets: false,
showTestNetworks: false,
useNativeCurrencyAsPrimaryCurrency: true,
hideZeroBalanceTokens: false,
},
// ENS decentralized website resolution
ipfsGateway: '',
useAddressBarEnsResolution: true,
infuraBlocked: null,
ledgerTransportType: 'U2F',
snapRegistryList: {},
transactionSecurityCheckEnabled: false,
theme: 'OS',
isLineaMainnetReleased: false,
};
describe('migration #92.3', () => {
it('updates the version metadata', async () => {
const oldStorage = {
meta: { version: 92.2 },
data: {},
};
const newStorage = await migrate(oldStorage);
expect(newStorage.meta).toStrictEqual({ version: 92.3 });
});
it('does nothing if no PreferencesController state', async () => {
const oldData = {
some: 'data',
};
const oldStorage = {
meta: { version: 92.2 },
data: oldData,
};
const newStorage = await migrate(oldStorage);
expect(newStorage.data).toStrictEqual(oldData);
});
it('does nothing if no AppStateController state', async () => {
const oldData = {
some: 'data',
};
const oldStorage = {
meta: { version: 92.2 },
data: oldData,
};
const newStorage = await migrate(oldStorage);
expect(newStorage.data).toStrictEqual(oldData);
});
it('changes advancedGasFee from null to an empty object, and sets hadAdvancedGasFeesSetPriorToMigration92_3 to false', async () => {
const oldData = {
some: 'data',
PreferencesController: {
...PREFERENCES_CONTROLLER_MOCK,
},
AppStateController: {},
};
const oldStorage = {
meta: { version: 92.2 },
data: oldData,
};
const newStorage = await migrate(oldStorage);
expect(newStorage.data).toStrictEqual({
some: oldData.some,
PreferencesController: {
...PREFERENCES_CONTROLLER_MOCK,
advancedGasFee: {},
},
AppStateController: {
hadAdvancedGasFeesSetPriorToMigration92_3: false,
},
});
});
it('changes advancedGasFee from an object of values to an empty object and sets hadAdvancedGasFeesSetPriorToMigration92_3 to true', async () => {
const oldData = {
some: 'data',
PreferencesController: {
...PREFERENCES_CONTROLLER_MOCK,
advancedGasFee: {
priorityFee: '0x1',
maxBaseFee: '0x1',
},
},
AppStateController: {},
};
const oldStorage = {
meta: { version: 92.2 },
data: oldData,
};
const newStorage = await migrate(oldStorage);
expect(newStorage.data).toStrictEqual({
some: oldData.some,
PreferencesController: {
...PREFERENCES_CONTROLLER_MOCK,
advancedGasFee: {},
},
AppStateController: {
hadAdvancedGasFeesSetPriorToMigration92_3: true,
},
});
});
it('does not erase advancedGasFee if it does not contain the expected data prior to this migration', async () => {
const oldData = {
some: 'data',
PreferencesController: {
...PREFERENCES_CONTROLLER_MOCK,
advancedGasFee: {
'0x5': {
priorityFee: '0x1',
maxBaseFee: '0x1',
},
},
},
AppStateController: {},
};
const oldStorage = {
meta: { version: 92.2 },
data: oldData,
};
const newStorage = await migrate(oldStorage);
expect(newStorage.data).toStrictEqual({
some: oldData.some,
PreferencesController: {
...PREFERENCES_CONTROLLER_MOCK,
advancedGasFee: {
'0x5': {
priorityFee: '0x1',
maxBaseFee: '0x1',
},
},
},
AppStateController: {
hadAdvancedGasFeesSetPriorToMigration92_3: false,
},
});
});
});

View File

@ -0,0 +1,98 @@
import { hasProperty, isNullOrUndefined, isObject } from '@metamask/utils';
import { cloneDeep } from 'lodash';
import log from 'loglevel';
type VersionedData = {
meta: { version: number };
data: Record<string, unknown>;
};
export const version = 92.3;
/**
* This migration does the following:
*
* - Deletes currently stored advancedGasFee in preferences controller,
* replacing the default with an empty object
* - Sets hadAdvancedGasFeesSetPriorToMigration92_3 flag on AppStateController
* to indicate if the user had previously had advancedGasFee set in their
* preferences. This will be used to display a whats new entry to inform users
* that we wiped these settings and made them apply per network.
*
* @param originalVersionedData - Versioned MetaMask extension state, exactly what we persist to dist.
* @param originalVersionedData.meta - State metadata.
* @param originalVersionedData.meta.version - The current state version.
* @param originalVersionedData.data - The persisted MetaMask state, keyed by controller.
* @returns Updated versioned MetaMask extension state.
*/
export async function migrate(
originalVersionedData: VersionedData,
): Promise<VersionedData> {
const versionedData = cloneDeep(originalVersionedData);
versionedData.meta.version = version;
migrateData(versionedData.data);
return versionedData;
}
function migrateData(state: Record<string, unknown>): void {
changeShapeAndRemoveOldAdvancedGasFeePreference(state);
}
function changeShapeAndRemoveOldAdvancedGasFeePreference(
state: Record<string, unknown>,
) {
if (isNullOrUndefined(state.PreferencesController)) {
log.warn(
`Migration #${version}: preferences controller null or undefined, skipping migration`,
);
return;
}
if (
hasProperty(state, 'AppStateController') &&
isObject(state.AppStateController) &&
hasProperty(state, 'PreferencesController') &&
isObject(state.PreferencesController)
) {
const possibleOriginalValue = state.PreferencesController?.advancedGasFee;
// Will be false if the keys set on the object are anything other than the
// maxBaseFee or priorityFee. Essentially if the object is already keyed
// by chainId it won't show as hadFeesSet.
const hadFeesSet =
isObject(possibleOriginalValue) &&
hasFeePreferenceKeys(possibleOriginalValue);
state.AppStateController.hadAdvancedGasFeesSetPriorToMigration92_3 =
hadFeesSet;
if (
state.PreferencesController.advancedGasFee === null ||
(isObject(state.PreferencesController.advancedGasFee) &&
hasFeePreferenceKeys(state.PreferencesController.advancedGasFee))
) {
state.PreferencesController.advancedGasFee = {};
}
} else if (isObject(state.AppStateController) === false) {
global.sentry?.captureException?.(
new Error(
`typeof state.AppStateController is ${typeof state.AppStateController}`,
),
);
} else if (isObject(state.PreferencesController) === false) {
global.sentry?.captureException?.(
new Error(
`typeof state.PreferencesController is ${typeof state.PreferencesController}`,
),
);
}
}
function hasFeePreferenceKeys(objectToCheck: Record<string, unknown>): boolean {
const keys = Object.keys(objectToCheck);
if (keys.includes('maxBaseFee') || keys.includes('priorityFee')) {
return true;
}
return false;
}

View File

@ -97,6 +97,7 @@ import * as m090 from './090';
import * as m091 from './091';
import * as m092 from './092';
import * as m092point1 from './092.1';
import * as m092point3 from './092.3';
import * as m093 from './093';
import * as m094 from './094';
import * as m095 from './095';
@ -195,6 +196,7 @@ const migrations = [
m091,
m092,
m092point1,
m092point3,
m093,
m094,
m095,

View File

@ -111,6 +111,7 @@
"networkId": "5",
"providerConfig": {
"type": "rpc",
"nickname": "goerli",
"chainId": "0x5",
"ticker": "ETH",
"id": "chain5"
@ -349,8 +350,10 @@
"useNftDetection": true,
"openSeaEnabled": true,
"advancedGasFee": {
"maxBaseFee": "75",
"priorityFee": "2"
"0x5": {
"maxBaseFee": "75",
"priorityFee": "2"
}
},
"nftsDropdownState": {
"0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc": {

View File

@ -33,6 +33,7 @@
"qrHardware": "object",
"usedNetworks": "object",
"snapsInstallPrivacyWarningShown": "boolean",
"hadAdvancedGasFeesSetPriorToMigration92_3": "boolean",
"serviceWorkerLastActiveTime": "number"
},
"ApprovalController": "object",

View File

@ -21,7 +21,6 @@
"customNonceValue": "",
"useBlockie": false,
"featureFlags": {},
"incomingTransactionsPreferences": "object",
"welcomeScreenSeen": false,
"currentLocale": "en",
"preferences": {
@ -57,6 +56,7 @@
"qrHardware": "object",
"usedNetworks": "object",
"snapsInstallPrivacyWarningShown": "boolean",
"hadAdvancedGasFeesSetPriorToMigration92_3": "boolean",
"serviceWorkerLastActiveTime": "number",
"currentAppVersion": "string",
"previousAppVersion": "",
@ -87,6 +87,7 @@
"useCurrencyRateCheck": "boolean",
"openSeaEnabled": "boolean",
"advancedGasFee": "object",
"incomingTransactionsPreferences": "object",
"lostIdentities": "object",
"forgottenPassword": false,
"ipfsGateway": "dweb.link",
@ -101,7 +102,6 @@
"metaMetricsId": "fake-metrics-id",
"eventsBeforeMetricsOptIn": "object",
"traits": "object",
"transactions": "object",
"fragments": "object",
"segmentApiCalls": "object",
"previousUserTraits": "object",
@ -145,6 +145,7 @@
"notifications": "object",
"accounts": "object",
"currentNetworkTxList": "object",
"transactions": "object",
"unapprovedDecryptMsgs": "object",
"unapprovedDecryptMsgCount": 0,
"unapprovedEncryptionPublicKeyMsgs": "object",

View File

@ -1,6 +1,7 @@
import React, { useEffect, useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { capitalize } from 'lodash';
import { useTransactionEventFragment } from '../../../../hooks/useTransactionEventFragment';
import { EditGasModes } from '../../../../../shared/constants/gas';
@ -8,7 +9,11 @@ import {
Display,
FlexDirection,
} from '../../../../helpers/constants/design-system';
import { getAdvancedGasFeeValues } from '../../../../selectors';
import {
getAdvancedGasFeeValues,
getCurrentChainId,
getNetworkIdentifier,
} from '../../../../selectors';
import { setAdvancedGasFee } from '../../../../store/actions';
import { useGasFeeContext } from '../../../../contexts/gasFee';
import { useAdvancedGasFeePopoverContext } from '../context';
@ -21,6 +26,9 @@ const AdvancedGasFeeDefaults = () => {
const { gasErrors, maxBaseFee, maxPriorityFeePerGas } =
useAdvancedGasFeePopoverContext();
const advancedGasFeeValues = useSelector(getAdvancedGasFeeValues);
// This will need to use a different chainId in multinetwork
const chainId = useSelector(getCurrentChainId);
const networkIdentifier = useSelector(getNetworkIdentifier);
const { updateTransactionEventFragment } = useTransactionEventFragment();
const { editGasMode } = useGasFeeContext();
const [isDefaultSettingsSelected, setDefaultSettingsSelected] = useState(
@ -39,7 +47,7 @@ const AdvancedGasFeeDefaults = () => {
const handleUpdateDefaultSettings = () => {
if (isDefaultSettingsSelected) {
dispatch(setAdvancedGasFee(null));
dispatch(setAdvancedGasFee({ chainId, gasFeePreferences: undefined }));
setDefaultSettingsSelected(false);
updateTransactionEventFragment({
properties: {
@ -50,8 +58,11 @@ const AdvancedGasFeeDefaults = () => {
} else {
dispatch(
setAdvancedGasFee({
maxBaseFee,
priorityFee: maxPriorityFeePerGas,
chainId,
gasFeePreferences: {
maxBaseFee,
priorityFee: maxPriorityFeePerGas,
},
}),
);
updateTransactionEventFragment({
@ -82,13 +93,7 @@ const AdvancedGasFeeDefaults = () => {
isChecked={isDefaultSettingsSelected}
onChange={handleUpdateDefaultSettings}
isDisabled={gasErrors.maxFeePerGas || gasErrors.maxPriorityFeePerGas}
label={
isDefaultSettingsSelected
? t('advancedGasFeeDefaultOptOut')
: t('advancedGasFeeDefaultOptIn', [
<strong key="default-value-change">{t('newValues')}</strong>,
])
}
label={t('advancedGasFeeDefaultOptIn', [capitalize(networkIdentifier)])}
/>
</Box>
);

View File

@ -15,8 +15,11 @@ import { GasFeeContextProvider } from '../../../../contexts/gasFee';
import configureStore from '../../../../store/store';
import AdvancedGasFeeInputs from '../advanced-gas-fee-inputs';
import { CHAIN_IDS } from '../../../../../shared/constants/network';
import AdvancedGasFeeDefaults from './advanced-gas-fee-defaults';
const TEXT_SELECTOR = 'Save these values as my default for the Goerli network.';
jest.mock('../../../../store/actions', () => ({
disconnectGasFeeEstimatePoller: jest.fn(),
getGasFeeEstimatesAndStartPolling: jest
@ -62,68 +65,58 @@ const render = (defaultGasParams, contextParams) => {
};
describe('AdvancedGasFeeDefaults', () => {
it('should renders correct message when the default is not set', () => {
render({ advancedGasFee: null });
expect(screen.queryByText('new values')).toBeInTheDocument();
render({ advancedGasFee: {} });
expect(screen.queryByText(TEXT_SELECTOR)).toBeInTheDocument();
});
it('should renders correct message when the default values are set', () => {
render({
advancedGasFee: { maxBaseFee: 50, priorityFee: 2 },
advancedGasFee: {
[CHAIN_IDS.GOERLI]: { maxBaseFee: 50, priorityFee: 2 },
},
});
expect(
screen.queryByText(
'Always use these values and advanced setting as default.',
),
).toBeInTheDocument();
expect(screen.queryByText(TEXT_SELECTOR)).toBeInTheDocument();
});
it('should renders correct message when the default values are set and the maxBaseFee values are updated', () => {
render({
advancedGasFee: { maxBaseFee: 50, priorityFee: 2 },
advancedGasFee: {
[CHAIN_IDS.GOERLI]: { maxBaseFee: 50, priorityFee: 2 },
},
});
expect(document.getElementsByTagName('input')[2]).toBeChecked();
expect(
screen.queryByText(
'Always use these values and advanced setting as default.',
),
).toBeInTheDocument();
expect(screen.queryByText(TEXT_SELECTOR)).toBeInTheDocument();
fireEvent.change(document.getElementsByTagName('input')[0], {
target: { value: 75 },
});
expect(document.getElementsByTagName('input')[0]).toHaveValue(75);
expect(screen.queryByText('new values')).toBeInTheDocument();
expect(
screen.queryByText('Save these as my default for "Advanced"'),
).toBeInTheDocument();
expect(screen.queryByText(TEXT_SELECTOR)).toBeInTheDocument();
expect(screen.queryByText(TEXT_SELECTOR)).toBeInTheDocument();
});
it('should renders correct message when the default values are set and the priorityFee values are updated', () => {
render({
advancedGasFee: { maxBaseFee: 50, priorityFee: 2 },
advancedGasFee: {
[CHAIN_IDS.GOERLI]: { maxBaseFee: 50, priorityFee: 2 },
},
});
expect(document.getElementsByTagName('input')[2]).toBeChecked();
expect(
screen.queryByText(
'Always use these values and advanced setting as default.',
),
).toBeInTheDocument();
expect(screen.queryByText(TEXT_SELECTOR)).toBeInTheDocument();
fireEvent.change(document.getElementsByTagName('input')[1], {
target: { value: 5 },
});
expect(document.getElementsByTagName('input')[1]).toHaveValue(5);
expect(screen.queryByText('new values')).toBeInTheDocument();
expect(
screen.queryByText('Save these as my default for "Advanced"'),
).toBeInTheDocument();
expect(screen.queryByText(TEXT_SELECTOR)).toBeInTheDocument();
expect(screen.queryByText(TEXT_SELECTOR)).toBeInTheDocument();
});
it('should call action setAdvancedGasFee when checkbox or label text is clicked', () => {
render({
advancedGasFee: { maxBaseFee: 50, priorityFee: 2 },
advancedGasFee: {
[CHAIN_IDS.GOERLI]: { maxBaseFee: 50, priorityFee: 2 },
},
});
const mock = jest
.spyOn(Actions, 'setAdvancedGasFee')
.mockReturnValue({ type: 'test' });
const checkboxLabel = screen.queryByText(
'Always use these values and advanced setting as default.',
);
const checkboxLabel = screen.queryByText(TEXT_SELECTOR);
fireEvent.click(checkboxLabel);
expect(mock).toHaveBeenCalledTimes(1);
const checkbox = document.querySelector('input[type=checkbox]');

View File

@ -13,6 +13,7 @@ import configureStore from '../../../../../store/store';
import { AdvancedGasFeePopoverContextProvider } from '../../context';
import AdvancedGasFeeGasLimit from '../../advanced-gas-fee-gas-limit';
import { CHAIN_IDS } from '../../../../../../shared/constants/network';
import PriorityfeeInput from './priority-fee-input';
jest.mock('../../../../../store/actions', () => ({
@ -34,7 +35,7 @@ const render = (txProps, contextProps) => {
balance: '0x1F4',
},
},
advancedGasFee: { priorityFee: 100 },
advancedGasFee: { [CHAIN_IDS.GOERLI]: { priorityFee: 100 } },
featureFlags: { advancedInlineGas: true },
gasFeeEstimates:
mockEstimates[GasEstimateTypes.feeMarket].gasFeeEstimates,

View File

@ -67,6 +67,7 @@ const render = ({ txProps, contextProps } = {}) => {
selectedAddress: '0xAddress',
featureFlags: { advancedInlineGas: true },
gasFeeEstimates: MOCK_FEE_ESTIMATE,
advancedGasFee: {},
},
});

View File

@ -10,6 +10,7 @@ import { ETH } from '../../../../helpers/constants/common';
import configureStore from '../../../../store/store';
import { GasFeeContextProvider } from '../../../../contexts/gasFee';
import { CHAIN_IDS } from '../../../../../shared/constants/network';
import EditGasItem from './edit-gas-item';
jest.mock('../../../../store/actions', () => ({
@ -59,7 +60,9 @@ const renderComponent = ({
const store = configureStore({
metamask: {
nativeCurrency: ETH,
providerConfig: {},
providerConfig: {
chainId: CHAIN_IDS.GOERLI,
},
cachedBalances: {},
accounts: {
'0xAddress': {
@ -75,8 +78,10 @@ const renderComponent = ({
gasEstimateType: 'fee-market',
gasFeeEstimates: MOCK_FEE_ESTIMATE,
advancedGasFee: {
maxBaseFee: '100',
priorityFee: '2',
[CHAIN_IDS.GOERLI]: {
maxBaseFee: '100',
priorityFee: '2',
},
},
},
});

View File

@ -61,7 +61,7 @@ exports[`SignatureRequestHeader should match snapshot 1`] = `
<span
class="icon-with-fallback__fallback"
>
U
G
</span>
</div>
</div>
@ -71,7 +71,7 @@ exports[`SignatureRequestHeader should match snapshot 1`] = `
<h6
class="mm-box mm-text mm-text--body-sm mm-box--color-text-alternative"
>
Unknown private network
goerli
</h6>
<h6
class="mm-box mm-text mm-text--body-sm mm-text--font-weight-bold mm-box--color-text-default"

View File

@ -137,7 +137,7 @@ exports[`SignatureRequestOriginal should match snapshot 1`] = `
<span
class="icon-with-fallback__fallback"
>
U
G
</span>
</div>
</div>
@ -147,7 +147,7 @@ exports[`SignatureRequestOriginal should match snapshot 1`] = `
<h6
class="mm-box mm-text mm-text--body-sm mm-box--color-text-alternative"
>
Unknown private network
goerli
</h6>
<h6
class="mm-box mm-text mm-text--body-sm mm-text--font-weight-bold mm-box--color-text-default"

View File

@ -134,7 +134,7 @@ exports[`SignatureRequestSIWE (Sign in with Ethereum) should match snapshot 1`]
<span
class="icon-with-fallback__fallback"
>
U
G
</span>
</div>
</div>
@ -144,7 +144,7 @@ exports[`SignatureRequestSIWE (Sign in with Ethereum) should match snapshot 1`]
<h6
class="mm-box mm-text mm-text--body-sm mm-box--color-text-alternative"
>
Unknown private network
goerli
</h6>
<h6
class="mm-box mm-text mm-text--body-sm mm-text--font-weight-bold mm-box--color-text-default"

View File

@ -89,7 +89,7 @@ exports[`SignatureRequestHeader renders correctly with fromAccount 1`] = `
<span
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography chip__label typography--h7 typography--weight-normal typography--style-normal typography--color-text-alternative"
>
Private network
goerli
</span>
</div>
</div>
@ -127,7 +127,7 @@ exports[`SignatureRequestHeader renders correctly without fromAccount 1`] = `
<span
class="box box--margin-top-1 box--margin-bottom-1 box--flex-direction-row typography chip__label typography--h7 typography--weight-normal typography--style-normal typography--color-text-alternative"
>
Private network
goerli
</span>
</div>
</div>

View File

@ -1347,23 +1347,26 @@ export function getIsMultiLayerFeeNetwork(state) {
* To retrieve the maxBaseFee and priorityFee the user has set as default
*
* @param {*} state
* @returns Boolean
* @returns {{maxBaseFee: string, priorityFee: string} | undefined}
*/
export function getAdvancedGasFeeValues(state) {
return state.metamask.advancedGasFee;
}
/**
* To check if the user has set advanced gas fee settings as default with a non empty maxBaseFee and priotityFee.
*
* @param {*} state
* @returns Boolean
*/
export function getIsAdvancedGasFeeDefault(state) {
const { advancedGasFee } = state.metamask;
return (
Boolean(advancedGasFee?.maxBaseFee) && Boolean(advancedGasFee?.priorityFee)
);
// This will not work when we switch to supporting multi-chain.
// There are four non-test files that use this selector.
// advanced-gas-fee-defaults
// base-fee-input
// priority-fee-input
// useGasItemFeeDetails
// The first three are part of the AdvancedGasFeePopover
// The latter is used by the EditGasPopover
// Both of those are used in Confirmations as well as transaction-list-item
// All of the call sites have access to the GasFeeContext, which has a
// transaction object set on it, but there are currently no guarantees that
// the transaction has a chainId associated with it. To have this method
// support multichain we'll need a reliable way for the chainId of the
// transaction being modified to be available to all callsites and either
// pass it in to the selector as a second parameter, or access it at the
// callsite.
return state.metamask.advancedGasFee[getCurrentChainId(state)];
}
/**

View File

@ -632,11 +632,6 @@ describe('Selectors', () => {
priorityFee: '2',
});
});
it('#getIsAdvancedGasFeeDefault', () => {
const isAdvancedGasFeeDefault =
selectors.getIsAdvancedGasFeeDefault(mockState);
expect(isAdvancedGasFeeDefault).toStrictEqual(true);
});
it('#getAppIsLoading', () => {
const appIsLoading = selectors.getAppIsLoading(mockState);
expect(appIsLoading).toStrictEqual(false);

View File

@ -3084,7 +3084,7 @@ export function detectNfts(): ThunkAction<
}
export function setAdvancedGasFee(
val: { maxBaseFee?: Hex; priorityFee?: Hex } | null,
val: { chainId: Hex; maxBaseFee?: Hex; priorityFee?: Hex } | null,
): ThunkAction<void, MetaMaskReduxState, unknown, AnyAction> {
return (dispatch: MetaMaskReduxDispatch) => {
dispatch(showLoadingIndication());