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
4 changes: 4 additions & 0 deletions packages/transaction-pay-controller/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Fixed

- Fix fiat strategy never being selected by routing fiat payment method through `getStrategyOrder` and allowing quote retrieval when no crypto payment token is set ([#8720](https://github.com/MetaMask/core/pull/8720))

## [21.1.0]

### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,48 @@ describe('TransactionPayController', () => {
CHAIN_ID_MOCK,
TOKEN_ADDRESS_MOCK,
'perpsDeposit',
undefined,
);
});

it('passes fiat payment method ID into getStrategyOrder', async () => {
const controller = createController();

controller.updatePaymentToken({
transactionId: TRANSACTION_ID_MOCK,
tokenAddress: TOKEN_ADDRESS_MOCK,
chainId: CHAIN_ID_MOCK,
});

const { updateTransactionData } = updatePaymentTokenMock.mock.calls[0][1];

updateTransactionData(TRANSACTION_ID_MOCK, (data) => {
data.paymentToken = {
address: TOKEN_ADDRESS_MOCK,
balanceFiat: '1',
balanceHuman: '1',
balanceRaw: '1',
balanceUsd: '1',
chainId: CHAIN_ID_MOCK,
decimals: 6,
symbol: 'USDC',
};
data.fiatPayment = { selectedPaymentMethodId: 'card-123' };
});

const transactionMeta = {
id: TRANSACTION_ID_MOCK,
type: 'perpsDeposit',
} as TransactionMeta;

messenger.call('TransactionPayController:getStrategy', transactionMeta);

expect(getStrategyOrderMock).toHaveBeenCalledWith(
messenger,
CHAIN_ID_MOCK,
TOKEN_ADDRESS_MOCK,
'perpsDeposit',
'card-123',
);
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,14 +324,15 @@ export class TransactionPayController extends BaseController<
return validStrategies;
}

const paymentToken =
this.state.transactionData[transaction.id]?.paymentToken;
const transactionData = this.state.transactionData[transaction.id];
const paymentToken = transactionData?.paymentToken;

return getStrategyOrder(
this.messenger,
paymentToken?.chainId,
paymentToken?.address,
transaction.type,
transactionData?.fiatPayment?.selectedPaymentMethodId,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,49 @@ describe('Feature Flags Utils', () => {
TransactionPayStrategy.Relay,
]);
});

it('returns only Fiat strategy when fiatPaymentMethodId is provided', () => {
const strategyOrder = getStrategyOrder(
messenger,
undefined,
undefined,
undefined,
'card-123',
);

expect(strategyOrder).toStrictEqual([TransactionPayStrategy.Fiat]);
});

it('returns only Fiat strategy regardless of other routing config when fiatPaymentMethodId is provided', () => {
getRemoteFeatureFlagControllerStateMock.mockReturnValue({
...getDefaultRemoteFeatureFlagControllerState(),
remoteFeatureFlags: {
confirmations_pay: {
strategyOrder: [
TransactionPayStrategy.Relay,
TransactionPayStrategy.Across,
],
strategyOverrides: {
default: {
chains: {
[CHAIN_ID_MOCK]: [TransactionPayStrategy.Bridge],
},
},
},
},
},
});

const strategyOrder = getStrategyOrder(
messenger,
CHAIN_ID_MOCK,
TOKEN_ADDRESS_MOCK,
'perpsDeposit',
'/payments/debit-credit-card',
);

expect(strategyOrder).toStrictEqual([TransactionPayStrategy.Fiat]);
});
});

describe('getStrategyOrder route-aware resolution', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,14 +312,21 @@ function getDefaultOverrideStrategies(
* @param tokenAddress - Optional token address used to match route overrides.
* @param transactionType - Optional transaction type used to match route
* overrides.
* @param fiatPaymentMethodId - Optional fiat payment method ID used to match route overrides.
* @returns Ordered strategy list.
*/
export function getStrategyOrder(
messenger: TransactionPayControllerMessenger,
chainId?: Hex,
tokenAddress?: Hex,
transactionType?: string,
fiatPaymentMethodId?: string,
): StrategyOrder {
// If fiat payment method is selected, use Fiat strategy only
if (fiatPaymentMethodId) {
return [TransactionPayStrategy.Fiat];
}

const routingConfig = getStrategyRoutingConfig(messenger);
const normalizedChainId = normalizeHex(chainId);
const normalizedTokenAddress = normalizeHex(tokenAddress);
Expand Down
36 changes: 36 additions & 0 deletions packages/transaction-pay-controller/src/utils/quotes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,42 @@ describe('Quotes Utils', () => {
});
});

it('still invokes strategies when no payment token but fiat payment method is set', async () => {
await run({
transactionData: {
...TRANSACTION_DATA_MOCK,
paymentToken: undefined,
fiatPayment: { selectedPaymentMethodId: 'card-123' },
},
});

expect(getQuotesMock).toHaveBeenCalled();
});

it('does not invoke strategies when no payment token and no fiat payment method', async () => {
await run({
transactionData: {
...TRANSACTION_DATA_MOCK,
paymentToken: undefined,
fiatPayment: {},
},
});

const transactionDataMock = {
quotes: [QUOTE_MOCK],
quotesLastUpdated: undefined,
};

updateTransactionDataMock.mock.calls.map((call) =>
call[1](transactionDataMock),
);

expect(transactionDataMock).toMatchObject({
quotes: [],
quotesLastUpdated: expect.any(Number),
});
});

it('gets quotes from strategy', async () => {
await run();

Expand Down
2 changes: 1 addition & 1 deletion packages/transaction-pay-controller/src/utils/quotes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ async function getQuotes(
},
);

if (!requests?.length) {
if (!requests?.length && !fiatPaymentMethod) {
return {
batchTransactions: [],
quotes: [],
Expand Down
Loading