Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
6e407d3
feature flag validation
bergarces Mar 10, 2026
5b84ccc
addd wrappers
bergarces Mar 10, 2026
1e09ec5
fix feature flag check
bergarces Mar 10, 2026
e8ab445
refactor to simplify callbacks
bergarces Mar 10, 2026
73586b3
coverage
bergarces Mar 10, 2026
3b760ee
Merge branch 'main' into transaction-pay-controller-new-assets-state
bergarces Mar 10, 2026
3769141
changelog
bergarces Mar 10, 2026
dc87022
Merge branch 'main' into transaction-pay-controller-new-assets-state
bergarces Mar 10, 2026
199802c
missing dependency
bergarces Mar 10, 2026
deb5f05
tsconfig
bergarces Mar 10, 2026
d883110
Merge branch 'main' into transaction-pay-controller-new-assets-state
bergarces Mar 10, 2026
d8a0c2f
Merge branch 'main' into transaction-pay-controller-new-assets-state
bergarces Mar 11, 2026
61e0890
correct conversion
bergarces Mar 11, 2026
20e274c
fix types
bergarces Mar 11, 2026
0f01832
test fix
bergarces Mar 11, 2026
6638faa
remove assertion
bergarces Mar 11, 2026
9b43494
revert type change
bergarces Mar 11, 2026
61aec42
merge changes
bergarces Mar 11, 2026
ab40146
remove functions
bergarces Mar 11, 2026
fa786f6
Merge branch 'main' into transaction-pay-controller-new-assets-state
bergarces Mar 11, 2026
e2f2a16
tests
bergarces Mar 12, 2026
81d2a9d
fix tests
bergarces Mar 12, 2026
40d4ea0
test
bergarces Mar 12, 2026
6e94df1
Merge branch 'main' into transaction-pay-controller-new-assets-state
bergarces Mar 12, 2026
be776f1
app metadata dependency
bergarces Mar 12, 2026
9323849
tsconfig
bergarces Mar 12, 2026
f28af7a
changelog
bergarces Mar 12, 2026
5d0b20b
remove dependency reference
bergarces Mar 12, 2026
e7f200b
Merge branch 'main' into transaction-pay-controller-new-assets-state
bergarces Mar 12, 2026
d2899f6
fix changelog
bergarces Mar 12, 2026
1f5463f
minor changelog change
bergarces Mar 12, 2026
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
8 changes: 8 additions & 0 deletions packages/transaction-pay-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- **BREAKING:** Add `AssetsControllerGetStateForTransactionPayAction` to the `AllowedActions` messenger type ([#8163](https://github.com/MetaMask/core/pull/8163))

### Changed

- `getTokenBalance`, `getTokenInfo`, and `getTokenFiatRate` now source token metadata, balances, and pricing from `AssetsControllerGetStateForTransactionPayAction` when the `assetsUnifyState` remote feature flag is enabled, falling back to individual controller state calls otherwise ([#8163](https://github.com/MetaMask/core/pull/8163))

## [16.5.0]

### Added
Expand Down
1 change: 1 addition & 0 deletions packages/transaction-pay-controller/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"@ethersproject/abi": "^5.7.0",
"@ethersproject/contracts": "^5.7.0",
"@ethersproject/providers": "^5.7.0",
"@metamask/assets-controller": "^2.3.0",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assets-controller is the new controller that we are migrating towards and that will hold the state.

Once the flag is fully turned on, we will come back here and delete all the old code and the old dependency.

"@metamask/assets-controllers": "^100.2.1",
"@metamask/base-controller": "^9.0.0",
"@metamask/bridge-controller": "^69.1.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ export function getMessengerMock({
TransactionControllerEstimateGasBatchAction['handler']
> = jest.fn();

const getAssetsControllerStateMock = jest.fn();

const messenger: RootMessenger = new Messenger({
namespace: MOCK_ANY_NAMESPACE,
});
Expand Down Expand Up @@ -241,12 +243,18 @@ export function getMessengerMock({
'TransactionController:estimateGasBatch',
estimateGasBatchMock,
);

messenger.registerActionHandler(
'AssetsController:getStateForTransactionPay',
getAssetsControllerStateMock,
);
}

const publish = messenger.publish.bind(messenger);

return {
addTransactionMock,
getAssetsControllerStateMock,
addTransactionBatchMock,
estimateGasMock,
estimateGasBatchMock,
Expand Down
2 changes: 2 additions & 0 deletions packages/transaction-pay-controller/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { AssetsControllerGetStateForTransactionPayAction } from '@metamask/assets-controller';
import type {
CurrencyRateControllerActions,
TokenBalancesControllerGetStateAction,
Expand Down Expand Up @@ -38,6 +39,7 @@ import type { CONTROLLER_NAME, TransactionPayStrategy } from './constants';

export type AllowedActions =
| AccountTrackerControllerGetStateAction
| AssetsControllerGetStateForTransactionPayAction
| BridgeControllerActions
| BridgeStatusControllerActions
| CurrencyRateControllerActions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
DEFAULT_RELAY_QUOTE_URL,
DEFAULT_SLIPPAGE,
DEFAULT_STRATEGY_ORDER,
getAssetsUnifyStateFeature,
getFallbackGas,
DEFAULT_RELAY_EXECUTE_URL,
getRelayOriginGasOverhead,
Expand Down Expand Up @@ -562,6 +563,85 @@ describe('Feature Flags Utils', () => {
});
});

describe('getAssetsUnifyStateFeature', () => {
type AssetsUnifyingState =
| {
enabled: boolean;
featureVersion: string | null;
}
| undefined;

const failureCases: {
description: string;
assetsUnifyingState: AssetsUnifyingState;
}[] = [
{
description: 'returns false when assetsUnifyState is not set',
assetsUnifyingState: undefined,
},
{
description: 'returns false when assetsUnifyState.enabled is false',
assetsUnifyingState: {
enabled: false,
featureVersion: '1',
},
},
{
description:
'returns false when featureVersion does not match expected version',
assetsUnifyingState: {
enabled: true,
featureVersion: '2',
},
},
];

const successCases = [
{
description:
'returns true when assetsUnifyState is enabled and featureVersion matches',
assetsUnifyingState: {
enabled: true,
featureVersion: '1',
},
},
];

const arrangeMocks = (assetsUnifyState: AssetsUnifyingState): void => {
const defaultRemoteFeatureFlagsState =
getDefaultRemoteFeatureFlagControllerState();
getRemoteFeatureFlagControllerStateMock.mockReturnValue({
...defaultRemoteFeatureFlagsState,
remoteFeatureFlags: {
...defaultRemoteFeatureFlagsState.remoteFeatureFlags,
...(assetsUnifyState ? { assetsUnifyState } : {}),
},
});
};

it.each(failureCases)(
'$description',
({ assetsUnifyingState }: (typeof failureCases)[number]) => {
arrangeMocks(assetsUnifyingState);

const result = getAssetsUnifyStateFeature(messenger);

expect(result).toBe(false);
},
);

it.each(successCases)(
'$description',
({ assetsUnifyingState }: (typeof successCases)[number]) => {
arrangeMocks(assetsUnifyingState);

const result = getAssetsUnifyStateFeature(messenger);

expect(result).toBe(true);
},
);
});

describe('getStrategyOrder', () => {
it('returns default strategy order when none is set', () => {
const strategyOrder = getStrategyOrder(messenger);
Expand Down
25 changes: 25 additions & 0 deletions packages/transaction-pay-controller/src/utils/feature-flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,31 @@ export function getSlippage(
return slippage;
}

/**
* Get the AssetsUnifyState feature flag state.
*
* @param messenger - Controller messenger.
* @returns True if the assets unify state feature is enabled, false otherwise.
*/
export function getAssetsUnifyStateFeature(
Copy link
Member

@matthewwalsh0 matthewwalsh0 Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to pivot on a feature flag at this point?

Could we not test the new controller for a time with standard client flows, and refactor this in one go when stable?

I'm naturally concerned given the risk to key in-development flows like MUSD, Perps, and Predict.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We cannot migrate in one go. There is a very complex network of selectors using other selectors that depend on our state, and we want to be able to work in manageable PR sizes.

What we have done so far is create a root selector for every single legacy piece of state and make it return the old state if the flag is off, and the new state in the shape of the old if the flag is on. We have hunted every single reference to legacy stated and replaced it with those selectors (in extension). We are now doing the same for the two controllers that fetch from those controller states as well.

Once that is done, we can safely turn the feature flag on in dev for QA to test. Once we are confident there are no regressions we can then start turning it on in other environments.

Not only that, but before we turn it on in dev we need to ensure all fixtures and tests that use legacy state also have the new controller state, otherwise those tests will fail.

This would have been very difficult to achieve in one PR without feature flags.

messenger: TransactionPayControllerMessenger,
): boolean {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QQ if we want to test this , what is the way to turn this to true ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently only controlled by remote feature flags, unsure how much work would be required if we want to force to true.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By doing black magic cjs updates in the client.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With that said, if that cjs change is done with a patch and directly at the feature flag controller, it can be used to test selector changes too.

const state = messenger.call('RemoteFeatureFlagController:getState');
const assetsUnifyState = state.remoteFeatureFlags.assetsUnifyState as
| {
enabled: boolean;
featureVersion: string | null;
}
| undefined;

const AssetsUnifyStateFeatureVersion = '1';

return (
Boolean(assetsUnifyState?.enabled) &&
assetsUnifyState?.featureVersion === AssetsUnifyStateFeatureVersion
);
}

/**
* Get a value from a record using a case-insensitive key lookup.
*
Expand Down
Loading
Loading