Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions packages/transaction-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed

- Bump `@metamask/network-controller` from `^35.0.0` to `^35.0.1` ([#9758](https://github.com/MetaMask/core/pull/9758))
- Replace underpriced dapp-suggested gas fees with the wallet's suggested estimates ([#9704](https://github.com/MetaMask/core/pull/9704))
- If the new `replaceUnderpricedDappGasFees` feature flag is enabled for the chain, dapp-suggested EIP-1559 fees with a `maxFeePerGas` below the current low estimate are replaced with the suggested medium values, as they are unlikely to result in inclusion in a block before fee values change.
- The `userFeeLevel` for such transactions is set to `medium` instead of `dappSuggested`, so the values are kept updated while the transaction is unapproved.
- The original dapp-suggested values remain available via `dappSuggestedGasFees` on the transaction metadata.
- Disabled by default.
- Ignore underpriced saved (advanced) gas fee preferences in favour of the wallet's suggested estimates ([#9704](https://github.com/MetaMask/core/pull/9704))
- If the new `replaceUnderpricedSavedGasFees` feature flag is enabled for the chain, saved custom fees with a `maxBaseFee` below the current low estimate are ignored and the suggested medium values are used instead, as they are unlikely to result in inclusion in a block before fee values change.
- Level-based saved preferences track current estimates and are never ignored.
- The `userFeeLevel` for such transactions is set to `medium` instead of `custom`, so the values are kept updated while the transaction is unapproved.
- Disabled by default.

### Fixed

Expand Down
48 changes: 48 additions & 0 deletions packages/transaction-controller/src/utils/feature-flags.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import {
getTransactionHistoryLimit,
FeatureFlag,
getTimeoutAttempts,
getReplaceUnderpricedDappGasFeesEnabled,
getReplaceUnderpricedSavedGasFeesEnabled,
} from './feature-flags.js';
import { isValidSignature } from './signature.js';

Expand Down Expand Up @@ -822,6 +824,52 @@ describe('Feature Flags Utils', () => {
});
});

describe.each([
[
'getReplaceUnderpricedDappGasFeesEnabled',
getReplaceUnderpricedDappGasFeesEnabled,
'replaceUnderpricedDappGasFees' as const,
],
[
'getReplaceUnderpricedSavedGasFeesEnabled',
getReplaceUnderpricedSavedGasFeesEnabled,
'replaceUnderpricedSavedGasFees' as const,
],
])('%s', (_name, getter, flagKey) => {
it('returns false if no feature flags set', () => {
mockFeatureFlags({});

expect(getter(CHAIN_ID_MOCK, controllerMessenger)).toBe(false);
});

it('returns default value if no chain-specific config', () => {
mockFeatureFlags({
[FeatureFlag.Transactions]: {
[flagKey]: {
default: true,
},
},
});

expect(getter(CHAIN_ID_MOCK, controllerMessenger)).toBe(true);
});

it('returns chain-specific value when available', () => {
mockFeatureFlags({
[FeatureFlag.Transactions]: {
[flagKey]: {
default: true,
perChainConfig: {
[CHAIN_ID_MOCK]: false,
},
},
},
});

expect(getter(CHAIN_ID_MOCK, controllerMessenger)).toBe(false);
});
});

describe('getTimeoutAttempts', () => {
it('returns undefined if no feature flags set', () => {
mockFeatureFlags({});
Expand Down
88 changes: 88 additions & 0 deletions packages/transaction-controller/src/utils/feature-flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,46 @@ export type TransactionControllerFeatureFlags = {
*/
default?: number;
};

/**
* Replacement of underpriced dapp-suggested gas fees.
* If enabled, dapp-suggested EIP-1559 fees with a `maxFeePerGas` below the
* current low estimate are replaced with the wallet's suggested fees, as
* they are unlikely to be included in a block before fee values change.
*/
replaceUnderpricedDappGasFees?: {
/** Enablement on a per-chain basis. */
perChainConfig?: {
[chainId: Hex]: boolean;
};

/**
* Default enablement.
* This value is used when no specific value is found for a chain ID.
*/
default?: boolean;
};

/**
* Replacement of underpriced saved (advanced) gas fee preferences.
* If enabled, saved custom fees with a `maxBaseFee` below the current low
* estimate are ignored in favour of the wallet's suggested fees, as they
* are unlikely to be included in a block before fee values change.
* Level-based saved preferences track current estimates and are never
* ignored.
*/
replaceUnderpricedSavedGasFees?: {
/** Enablement on a per-chain basis. */
perChainConfig?: {
[chainId: Hex]: boolean;
};

/**
* Default enablement.
* This value is used when no specific value is found for a chain ID.
*/
default?: boolean;
};
};
};

Expand Down Expand Up @@ -465,6 +505,54 @@ export function getTimeoutAttempts(
);
}

/**
* Retrieves whether underpriced dapp-suggested gas fees should be replaced
* with the wallet's suggested fees.
*
* @param chainId - The chain ID.
* @param messenger - The controller messenger instance.
* @returns Whether the replacement is enabled.
*/
export function getReplaceUnderpricedDappGasFeesEnabled(
chainId: Hex,
messenger: TransactionControllerMessenger,
): boolean {
const featureFlags = getFeatureFlags(messenger);

const replaceUnderpricedDappGasFeesFlags =
featureFlags?.[FeatureFlag.Transactions]?.replaceUnderpricedDappGasFees;

return (
replaceUnderpricedDappGasFeesFlags?.perChainConfig?.[chainId] ??
replaceUnderpricedDappGasFeesFlags?.default ??
false
);
}

/**
* Retrieves whether underpriced saved (advanced) gas fee preferences should be
* ignored in favour of the wallet's suggested fees.
*
* @param chainId - The chain ID.
* @param messenger - The controller messenger instance.
* @returns Whether the replacement is enabled.
*/
export function getReplaceUnderpricedSavedGasFeesEnabled(
chainId: Hex,
messenger: TransactionControllerMessenger,
): boolean {
const featureFlags = getFeatureFlags(messenger);

const replaceUnderpricedSavedGasFeesFlags =
featureFlags?.[FeatureFlag.Transactions]?.replaceUnderpricedSavedGasFees;

return (
replaceUnderpricedSavedGasFeesFlags?.perChainConfig?.[chainId] ??
replaceUnderpricedSavedGasFeesFlags?.default ??
false
);
}

/**
* Retrieves the relevant feature flags from the remote feature flag controller.
*
Expand Down
Loading