Skip to content

Commit d83c450

Browse files
fix(transaction-pay-controller): vault musd via sentinel (MetaMask#9161)
## Explanation Direct mUSD fiat quotes for Money Account deposits can settle directly into the Money Account, so they do not need a Relay quote or Relay execute path. This PR moves that path to a Ramps-funded, sponsored Money Account vault batch. After fiat settlement, the Fiat strategy refreshes the vault calldata through `getAmountData`, submits the nested approve/deposit calls through `TransactionController:addTransactionBatch`, and lets the client publish hook handle 7702/Sentinel sponsorship. It also keeps a local QA funding path behind client-provided fiat test options so the settlement step can be exercised without creating a real on-ramp order. ## References ## Checklist - [x] I've updated the test suite for new or updated code as appropriate - [x] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - [x] I've communicated my changes to consumers by [updating changelogs for packages I've changed](https://github.com/MetaMask/core/tree/main/docs/processes/updating-changelogs.md) - [ ] I've introduced [breaking changes](https://github.com/MetaMask/core/tree/main/docs/processes/breaking-changes.md) in this PR and have prepared draft pull requests for clients and consumer packages to resolve them
1 parent b0c7eca commit d83c450

24 files changed

Lines changed: 2289 additions & 303 deletions

packages/transaction-pay-controller/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Add test-only fiat execution options to bypass fiat on-ramp settlement during local QA by funding the expected fiat asset from a configured account before continuing the normal MM Pay fiat submit flow ([#9161](https://github.com/MetaMask/core/pull/9161))
13+
1014
### Fixed
1115

1216
- Sync transaction metadata when fiat payment is selected but no payment token is present ([#9158](https://github.com/MetaMask/core/pull/9158))
17+
- Fix direct mUSD fiat Money Account deposits by submitting a sponsored Money Account vault batch after fiat settlement instead of requiring Relay execute ([#9161](https://github.com/MetaMask/core/pull/9161))
1318

1419
## [23.8.0]
1520

packages/transaction-pay-controller/src/TransactionPayController-method-action-types.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,18 @@ export type TransactionPayControllerGetAmountDataAction = {
8181
handler: TransactionPayController['getAmountData'];
8282
};
8383

84+
/**
85+
* Returns optional fiat execution configuration.
86+
*
87+
* This is intentionally not stored in controller state.
88+
*
89+
* @returns Fiat execution options, if configured.
90+
*/
91+
export type TransactionPayControllerGetFiatOptionsAction = {
92+
type: `TransactionPayController:getFiatOptions`;
93+
handler: TransactionPayController['getFiatOptions'];
94+
};
95+
8496
export type TransactionPayControllerGetPaymentOverrideDataAction = {
8597
type: `TransactionPayController:getPaymentOverrideData`;
8698
handler: TransactionPayController['getPaymentOverrideData'];
@@ -134,6 +146,7 @@ export type TransactionPayControllerMethodActions =
134146
| TransactionPayControllerUpdateFiatPaymentAction
135147
| TransactionPayControllerGetDelegationTransactionAction
136148
| TransactionPayControllerGetAmountDataAction
149+
| TransactionPayControllerGetFiatOptionsAction
137150
| TransactionPayControllerGetPaymentOverrideDataAction
138151
| TransactionPayControllerGetStrategyAction
139152
| TransactionPayControllerPolymarketGetDepositWalletAddressAction

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

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { deriveFiatAssetForFiatPayment } from './strategy/fiat/utils';
1111
import { getMessengerMock } from './tests/messenger-mock';
1212
import type {
1313
TransactionPayControllerMessenger,
14+
TransactionPayControllerOptions,
1415
TransactionPaySourceAmount,
1516
UpdateTransactionDataCallback,
1617
} from './types';
@@ -35,7 +36,6 @@ const TRANSACTION_ID_MOCK = '123-456';
3536
const TRANSACTION_META_MOCK = { id: TRANSACTION_ID_MOCK } as TransactionMeta;
3637
const TOKEN_ADDRESS_MOCK = '0xabc' as Hex;
3738
const CHAIN_ID_MOCK = '0x1' as Hex;
38-
3939
describe('TransactionPayController', () => {
4040
const updateFiatPaymentMock = jest.mocked(updateFiatPayment);
4141
const updatePaymentTokenMock = jest.mocked(updatePaymentToken);
@@ -56,10 +56,14 @@ describe('TransactionPayController', () => {
5656
/**
5757
* Create a TransactionPayController.
5858
*
59+
* @param options - Controller options.
5960
* @returns The created controller.
6061
*/
61-
function createController(): TransactionPayController {
62+
function createController(
63+
options: Partial<TransactionPayControllerOptions> = {},
64+
): TransactionPayController {
6265
return new TransactionPayController({
66+
...options,
6367
getDelegationTransaction: jest.fn(),
6468
messenger,
6569
});
@@ -563,6 +567,29 @@ describe('TransactionPayController', () => {
563567
});
564568
});
565569

570+
describe('getFiatOptions', () => {
571+
it('returns configured fiat options', () => {
572+
const fiatOptions = {
573+
testFundingSource: '0x1111111111111111111111111111111111111111' as Hex,
574+
testAmountOverride: '0.1',
575+
};
576+
577+
createController({ fiatOptions });
578+
579+
const result = messenger.call('TransactionPayController:getFiatOptions');
580+
581+
expect(result).toBe(fiatOptions);
582+
});
583+
584+
it('returns undefined when no fiat options are configured', () => {
585+
createController();
586+
587+
const result = messenger.call('TransactionPayController:getFiatOptions');
588+
589+
expect(result).toBeUndefined();
590+
});
591+
});
592+
566593
describe('polymarket callbacks', () => {
567594
const EOA_MOCK = '0x1111111111111111111111111111111111111111' as Hex;
568595
const DEPOSIT_WALLET_MOCK =

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import type {
2020
TransactionConfigCallback,
2121
TransactionData,
2222
TransactionPayControllerMessenger,
23+
TransactionPayFiatOptions,
2324
TransactionPayControllerOptions,
2425
TransactionPayControllerState,
2526
UpdateFiatPaymentRequest,
@@ -36,6 +37,7 @@ import {
3637
const MESSENGER_EXPOSED_METHODS = [
3738
'getAmountData',
3839
'getDelegationTransaction',
40+
'getFiatOptions',
3941
'getPaymentOverrideData',
4042
'getStrategy',
4143
'polymarketGetDepositWalletAddress',
@@ -67,6 +69,8 @@ export class TransactionPayController extends BaseController<
6769

6870
readonly #getDelegationTransaction: GetDelegationTransactionCallback;
6971

72+
readonly #fiatOptions?: TransactionPayFiatOptions;
73+
7074
readonly #getPaymentOverrideData?: GetPaymentOverrideDataCallback;
7175

7276
readonly #getStrategy?: (
@@ -80,6 +84,7 @@ export class TransactionPayController extends BaseController<
8084
readonly #polymarket?: PolymarketCallbacks;
8185

8286
constructor({
87+
fiatOptions,
8388
getAmountData,
8489
getDelegationTransaction,
8590
getPaymentOverrideData,
@@ -98,6 +103,7 @@ export class TransactionPayController extends BaseController<
98103

99104
this.#getAmountData = getAmountData;
100105
this.#getDelegationTransaction = getDelegationTransaction;
106+
this.#fiatOptions = fiatOptions;
101107
this.#getPaymentOverrideData = getPaymentOverrideData;
102108
this.#getStrategy = getStrategy;
103109
this.#getStrategies = getStrategies;
@@ -244,6 +250,17 @@ export class TransactionPayController extends BaseController<
244250
return this.#getAmountData?.(...args) ?? Promise.resolve({ updates: [] });
245251
}
246252

253+
/**
254+
* Returns optional fiat execution configuration.
255+
*
256+
* This is intentionally not stored in controller state.
257+
*
258+
* @returns Fiat execution options, if configured.
259+
*/
260+
getFiatOptions(): TransactionPayFiatOptions | undefined {
261+
return this.#fiatOptions;
262+
}
263+
247264
getPaymentOverrideData(
248265
...args: Parameters<GetPaymentOverrideDataCallback>
249266
): ReturnType<GetPaymentOverrideDataCallback> {

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export type {
99
TransactionData,
1010
TransactionFiatPayment,
1111
TransactionFiatPaymentCallback,
12+
TransactionPayFiatOptions,
1213
TransactionPayControllerActions,
1314
TransactionPayControllerEvents,
1415
TransactionPayControllerGetStateAction,
@@ -28,6 +29,7 @@ export type {
2829
export type {
2930
TransactionPayControllerGetAmountDataAction,
3031
TransactionPayControllerGetDelegationTransactionAction,
32+
TransactionPayControllerGetFiatOptionsAction,
3133
TransactionPayControllerGetStrategyAction,
3234
TransactionPayControllerPolymarketGetDepositWalletAddressAction,
3335
TransactionPayControllerPolymarketSubmitDepositWalletBatchAction,

packages/transaction-pay-controller/src/strategy/fiat/constants.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,3 @@ export const MUSD_MONAD_FIAT_ASSET: TransactionPayFiatAsset = {
5151
address: MUSD_MONAD_ADDRESS,
5252
chainId: CHAIN_ID_MONAD,
5353
};
54-
55-
/** Fixed USD amount used to probe whether any fiat provider can sell mUSD on Monad. */
56-
export const MUSD_PROBE_AMOUNT_USD = 50;

0 commit comments

Comments
 (0)