Skip to content

Commit b59a521

Browse files
feat(transaction-pay-controller): add route-based strategy resolution
1 parent 57daf93 commit b59a521

7 files changed

Lines changed: 588 additions & 7 deletions

File tree

packages/transaction-pay-controller/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111

1212
- Add `FiatStrategy` to retrieve quotes via `RampsController` ([#8121](https://github.com/MetaMask/core/pull/8121))
13+
- Add route-based `confirmations_pay` strategy resolution via the `getStrategyRouteContext` controller option and `getStrategyOrderForRouteFromFeatureFlags` helper ([#8282](https://github.com/MetaMask/core/pull/8282))
1314

1415
### Changed
1516

packages/transaction-pay-controller/src/TransactionPayController.test.ts

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ import type {
1212
TransactionPayControllerMessenger,
1313
TransactionPaySourceAmount,
1414
} from './types';
15-
import { getStrategyOrder } from './utils/feature-flags';
15+
import type { TransactionPayRouteContext } from './utils/feature-flags';
16+
import {
17+
getStrategyOrder,
18+
getStrategyOrderForRoute,
19+
} from './utils/feature-flags';
1620
import { updateQuotes } from './utils/quotes';
1721
import { updateSourceAmounts } from './utils/source-amounts';
1822
import { pollTransactionChanges } from './utils/transaction';
@@ -36,6 +40,7 @@ describe('TransactionPayController', () => {
3640
const updateQuotesMock = jest.mocked(updateQuotes);
3741
const pollTransactionChangesMock = jest.mocked(pollTransactionChanges);
3842
const getStrategyOrderMock = jest.mocked(getStrategyOrder);
43+
const getStrategyOrderForRouteMock = jest.mocked(getStrategyOrderForRoute);
3944
let messenger: TransactionPayControllerMessenger;
4045

4146
/**
@@ -55,6 +60,9 @@ describe('TransactionPayController', () => {
5560

5661
messenger = getMessengerMock({ skipRegister: true }).messenger;
5762
getStrategyOrderMock.mockReturnValue([TransactionPayStrategy.Relay]);
63+
getStrategyOrderForRouteMock.mockReturnValue([
64+
TransactionPayStrategy.Relay,
65+
]);
5866
updateQuotesMock.mockResolvedValue(true);
5967
});
6068

@@ -324,6 +332,62 @@ describe('TransactionPayController', () => {
324332
).toBe(TransactionPayStrategy.Bridge);
325333
});
326334

335+
it('uses route-based feature flag resolution when getStrategyRouteContext is provided', async () => {
336+
const getStrategyRouteContext = jest.fn(() => ({
337+
chainId: '0xa4b1' as Hex,
338+
tokenAddress: '0xabc' as Hex,
339+
transactionType: 'perpsDeposit',
340+
}));
341+
342+
getStrategyOrderForRouteMock.mockReturnValue([
343+
TransactionPayStrategy.Across,
344+
]);
345+
346+
new TransactionPayController({
347+
getDelegationTransaction: jest.fn(),
348+
getStrategyRouteContext,
349+
messenger,
350+
});
351+
352+
expect(
353+
messenger.call(
354+
'TransactionPayController:getStrategy',
355+
TRANSACTION_META_MOCK,
356+
),
357+
).toBe(TransactionPayStrategy.Across);
358+
expect(getStrategyRouteContext).toHaveBeenCalledWith(
359+
TRANSACTION_META_MOCK,
360+
);
361+
expect(getStrategyOrderForRouteMock).toHaveBeenCalledWith(messenger, {
362+
chainId: '0xa4b1',
363+
tokenAddress: '0xabc',
364+
transactionType: 'perpsDeposit',
365+
});
366+
});
367+
368+
it('prefers explicit getStrategies values over route-based feature flag resolution', async () => {
369+
new TransactionPayController({
370+
getDelegationTransaction: jest.fn(),
371+
getStrategies: (): TransactionPayStrategy[] => [
372+
TransactionPayStrategy.Test,
373+
],
374+
getStrategyRouteContext: (): TransactionPayRouteContext => ({
375+
chainId: '0xa4b1' as Hex,
376+
tokenAddress: '0xabc' as Hex,
377+
transactionType: 'perpsDeposit',
378+
}),
379+
messenger,
380+
});
381+
382+
expect(
383+
messenger.call(
384+
'TransactionPayController:getStrategy',
385+
TRANSACTION_META_MOCK,
386+
),
387+
).toBe(TransactionPayStrategy.Test);
388+
expect(getStrategyOrderForRouteMock).not.toHaveBeenCalled();
389+
});
390+
327391
it('returns default strategy order when no callbacks and no strategy order feature flag', async () => {
328392
getStrategyOrderMock.mockReturnValue([TransactionPayStrategy.Relay]);
329393

packages/transaction-pay-controller/src/TransactionPayController.ts

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ import type {
2222
UpdateFiatPaymentRequest,
2323
UpdatePaymentTokenRequest,
2424
} from './types';
25-
import { getStrategyOrder } from './utils/feature-flags';
25+
import {
26+
getStrategyOrder,
27+
getStrategyOrderForRoute,
28+
} from './utils/feature-flags';
29+
import type { TransactionPayRouteContext } from './utils/feature-flags';
2630
import { updateQuotes } from './utils/quotes';
2731
import { updateSourceAmounts } from './utils/source-amounts';
2832
import { pollTransactionChanges } from './utils/transaction';
@@ -63,10 +67,15 @@ export class TransactionPayController extends BaseController<
6367
transaction: TransactionMeta,
6468
) => TransactionPayStrategy[];
6569

70+
readonly #getStrategyRouteContext?: (
71+
transaction: TransactionMeta,
72+
) => TransactionPayRouteContext;
73+
6674
constructor({
6775
getDelegationTransaction,
6876
getStrategy,
6977
getStrategies,
78+
getStrategyRouteContext,
7079
messenger,
7180
state,
7281
}: TransactionPayControllerOptions) {
@@ -80,6 +89,7 @@ export class TransactionPayController extends BaseController<
8089
this.#getDelegationTransaction = getDelegationTransaction;
8190
this.#getStrategy = getStrategy;
8291
this.#getStrategies = getStrategies;
92+
this.#getStrategyRouteContext = getStrategyRouteContext;
8393

8494
this.messenger.registerMethodActionHandlers(
8595
this,
@@ -270,8 +280,17 @@ export class TransactionPayController extends BaseController<
270280
isTransactionPayStrategy(strategy),
271281
);
272282

273-
return validStrategies.length
274-
? validStrategies
275-
: getStrategyOrder(this.messenger);
283+
if (validStrategies.length) {
284+
return validStrategies;
285+
}
286+
287+
if (this.#getStrategyRouteContext) {
288+
return getStrategyOrderForRoute(
289+
this.messenger,
290+
this.#getStrategyRouteContext(transaction),
291+
);
292+
}
293+
294+
return getStrategyOrder(this.messenger);
276295
}
277296
}

packages/transaction-pay-controller/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,5 @@ export { TransactionPayStrategy } from './constants';
2929
export { TransactionPayController } from './TransactionPayController';
3030
export { TransactionPayPublishHook } from './helpers/TransactionPayPublishHook';
3131
export type { TransactionPayBridgeQuote } from './strategy/bridge/types';
32+
export type { TransactionPayRouteContext } from './utils/feature-flags';
33+
export { getStrategyOrderForRouteFromFeatureFlags } from './utils/feature-flags';

packages/transaction-pay-controller/src/types.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import type { Draft } from 'immer';
3838

3939
import type { CONTROLLER_NAME, TransactionPayStrategy } from './constants';
4040
import type { TransactionPayControllerMethodActions } from './TransactionPayController-method-action-types';
41+
import type { TransactionPayRouteContext } from './utils/feature-flags';
4142

4243
export type AllowedActions =
4344
| AccountTrackerControllerGetStateAction
@@ -131,6 +132,14 @@ export type TransactionPayControllerOptions = {
131132
/** Callback to select ordered PayStrategies for a transaction. */
132133
getStrategies?: (transaction: TransactionMeta) => TransactionPayStrategy[];
133134

135+
/**
136+
* Callback to derive route context for generic feature-flag based strategy selection.
137+
* Used when no custom strategy callback returns a valid ordered strategy list.
138+
*/
139+
getStrategyRouteContext?: (
140+
transaction: TransactionMeta,
141+
) => TransactionPayRouteContext;
142+
134143
/** Controller messenger. */
135144
messenger: TransactionPayControllerMessenger;
136145

0 commit comments

Comments
 (0)