Skip to content

Commit 0164046

Browse files
Address PR feedback
1 parent 0beff28 commit 0164046

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+2512
-1857
lines changed

packages/transaction-controller/CHANGELOG.md

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

88
## [Unreleased]
99

10+
### Changed
11+
12+
- Add `perpsAcrossDeposit` and `predictAcrossDeposit` transaction types ([#7806](https://github.com/MetaMask/core/pull/7806))
13+
1014
### Added
1115

1216
- Add optional `requiredAssets` to `TransactionMeta` ([#7820](https://github.com/MetaMask/core/pull/7820))

packages/transaction-controller/src/types.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -836,9 +836,14 @@ export enum TransactionType {
836836
relayDeposit = 'relayDeposit',
837837

838838
/**
839-
* Deposit funds for Across quote.
839+
* Deposit funds for Across quote via Perps.
840840
*/
841-
acrossDeposit = 'acrossDeposit',
841+
perpsAcrossDeposit = 'perpsAcrossDeposit',
842+
843+
/**
844+
* Deposit funds for Across quote via Predict.
845+
*/
846+
predictAcrossDeposit = 'predictAcrossDeposit',
842847

843848
/**
844849
* When a transaction is failed it can be retried by
@@ -2101,6 +2106,9 @@ export type MetamaskPayMetadata = {
21012106
/** Chain ID of the payment token. */
21022107
chainId?: Hex;
21032108

2109+
/** Total time spent submitting the MetaMask Pay flow, in milliseconds. */
2110+
executionLatencyMs?: number;
2111+
21042112
/** Total network fee in fiat currency, including the original and bridge transactions. */
21052113
networkFeeFiat?: string;
21062114

@@ -2112,9 +2120,6 @@ export type MetamaskPayMetadata = {
21122120

21132121
/** Total cost of the transaction in fiat currency, including gas, fees, and the funds themselves. */
21142122
totalFiat?: string;
2115-
2116-
/** Total time spent executing the MetaMask Pay flow, in milliseconds. */
2117-
executionLatencyMs?: number;
21182123
};
21192124

21202125
/**

packages/transaction-pay-controller/ARCHITECTURE.md

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -44,22 +44,20 @@ Quotes are retrieved from the [Relay API](https://docs.relay.link/what-is-relay)
4444

4545
The resulting transaction deposits the necessary funds (on the source network), then a Relayer on the target chain immediately transfers the necessary funds and optionally executes any requested call data.
4646

47-
### TokenPay (provider routing)
47+
### Across
4848

49-
The `TokenPayStrategy` routes quote and execution requests through provider adapters (currently Relay and Across).
49+
The `AcrossStrategy` retrieves quotes from the Across API and submits approvals and deposit transactions via the `TransactionController`.
5050

51-
Provider selection is determined by feature flags:
51+
### Strategy Selection and Fallback
5252

53-
- `tokenPay.providerOrder` controls priority (default: `[primaryProvider, 'relay', 'across']`).
54-
- Each provider can be enabled/disabled via `tokenPay.providers.<id>.enabled`.
55-
- Providers may also implement capability gating in `supports(...)` (e.g., Across rejects same-chain swaps).
53+
Strategy order and configuration are determined by feature flags:
5654

57-
Routing behavior:
55+
- `payStrategies.strategyOrder` controls the ordered strategy list (default: `[Relay, Across]`).
56+
- Each strategy can be enabled/disabled via `payStrategies.<strategy>.enabled`.
57+
- Strategies may implement capability gating in `supports(...)` (e.g., Across rejects same-chain swaps).
5858

59-
- The strategy selects the **first** provider in order that is enabled and returns `supports(...) === true`.
60-
- If no provider supports the request, the strategy throws, and the controller returns no quotes.
61-
- There is **no automatic fallback** if the selected provider throws during quote retrieval or execution; errors surface and quotes are left empty. (Future work could introduce fallback on specific error types.)
62-
- Current limitation: provider-specific capability checks that happen during quote building (e.g., Across rejecting type-4/EIP-7702 transactions) do not fall back to lower-priority providers. If we add a third provider after Across that supports type-4, we should consider adding fallback logic or moving that check into `supports(...)` to avoid returning no quotes.
59+
- The controller selects the **first** strategy in order that returns `supports(...) === true`.
60+
- If the selected strategy fails during quote retrieval or execution, the next compatible strategy is attempted.
6361

6462
## Lifecycle
6563

@@ -71,11 +69,11 @@ The high level interaction with the `TransactionPayController` is as follows:
7169
4. Controller identifies any required tokens and adds them to its state.
7270
5. If a client confirmation is using `MetaMask Pay`, the user selects a payment token (or it is done automatically) which invokes the `updatePaymentToken` action.
7371
- The below steps are also triggered if the transaction `data` is updated.
74-
6. Controller selects an appropriate `PayStrategy` using the `getStrategy` action.
75-
7. Controller requests quotes from the `PayStrategy` and persists them in state, including associated totals.
72+
6. Controller requests an ordered list of strategies via the `getStrategies` action.
73+
7. Controller selects the first compatible strategy and requests quotes, falling back to the next strategy if quote retrieval fails.
7674
8. Resulting fees and totals are presented in the client transaction confirmation.
7775
9. If approved by the user, the target transaction is signed and published.
78-
10. The `TransactionPayPublishHook` is invoked and submits the relevant quotes via the same `PayStrategy`.
76+
10. The `TransactionPayPublishHook` is invoked and submits the relevant quotes via the strategy indicated by the quotes, with fallback on execution errors.
7977
11. The hook waits for any transactions and quotes to complete.
8078
12. Depending on the pay strategy and required tokens, the original target transaction is also published as the required funds are now in place on the user's account on the target chain.
8179
13. Target transaction is finalized and any related controller state is removed.

packages/transaction-pay-controller/CHANGELOG.md

Lines changed: 5 additions & 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
- Generate required tokens using `requiredAssets` from transaction metadata ([#7820](https://github.com/MetaMask/core/pull/7820))
13+
- Add standalone Across pay strategy with core-level selection and fallback ([#7806](https://github.com/MetaMask/core/pull/7806))
1314

1415
### Changed
1516

@@ -24,6 +25,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2425
- Bump `@metamask/bridge-controller` from `^65.1.0` to `^65.2.0` ([#7802](https://github.com/MetaMask/core/pull/7802))
2526
- Poll Relay status for same-chain quotes with a single deposit step ([#7761](https://github.com/MetaMask/core/pull/7761))
2627

28+
### Removed
29+
30+
- Remove TokenPay strategy and provider adapters ([#0000](https://github.com/MetaMask/core/pull/0000))
31+
2732
## [12.0.2]
2833

2934
### Changed

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

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,54 @@ describe('TransactionPayController', () => {
112112
),
113113
).toBe(TransactionPayStrategy.Test);
114114
});
115+
116+
it('returns relay if getStrategies callback returns empty', async () => {
117+
new TransactionPayController({
118+
getDelegationTransaction: jest.fn(),
119+
getStrategies: (): TransactionPayStrategy[] => [],
120+
messenger,
121+
});
122+
123+
expect(
124+
messenger.call(
125+
'TransactionPayController:getStrategy',
126+
TRANSACTION_META_MOCK,
127+
),
128+
).toBe(TransactionPayStrategy.Relay);
129+
});
130+
});
131+
132+
describe('getStrategies Action', () => {
133+
it('returns relay by default', async () => {
134+
createController();
135+
136+
expect(
137+
messenger.call(
138+
'TransactionPayController:getStrategies',
139+
TRANSACTION_META_MOCK,
140+
),
141+
).toStrictEqual([
142+
TransactionPayStrategy.Relay,
143+
TransactionPayStrategy.Across,
144+
]);
145+
});
146+
147+
it('returns callback list if provided', async () => {
148+
new TransactionPayController({
149+
getDelegationTransaction: jest.fn(),
150+
getStrategies: (): TransactionPayStrategy[] => [
151+
TransactionPayStrategy.Test,
152+
],
153+
messenger,
154+
});
155+
156+
expect(
157+
messenger.call(
158+
'TransactionPayController:getStrategies',
159+
TRANSACTION_META_MOCK,
160+
),
161+
).toStrictEqual([TransactionPayStrategy.Test]);
162+
});
115163
});
116164

117165
describe('transaction data update', () => {

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

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import type {
1616
UpdatePaymentTokenRequest,
1717
} from './types';
1818
import { updateQuotes } from './utils/quotes';
19+
import { getStrategyOrder } from './utils/feature-flags';
1920
import { updateSourceAmounts } from './utils/source-amounts';
2021
import { pollTransactionChanges } from './utils/transaction';
2122

@@ -43,9 +44,14 @@ export class TransactionPayController extends BaseController<
4344
transaction: TransactionMeta,
4445
) => TransactionPayStrategy;
4546

47+
readonly #getStrategies?: (
48+
transaction: TransactionMeta,
49+
) => TransactionPayStrategy[];
50+
4651
constructor({
4752
getDelegationTransaction,
4853
getStrategy,
54+
getStrategies,
4955
messenger,
5056
state,
5157
}: TransactionPayControllerOptions) {
@@ -58,6 +64,7 @@ export class TransactionPayController extends BaseController<
5864

5965
this.#getDelegationTransaction = getDelegationTransaction;
6066
this.#getStrategy = getStrategy;
67+
this.#getStrategies = getStrategies;
6168

6269
this.#registerActionHandlers();
6370

@@ -141,15 +148,31 @@ export class TransactionPayController extends BaseController<
141148
}
142149

143150
#registerActionHandlers(): void {
151+
const getStrategies =
152+
this.#getStrategies ??
153+
((transaction: TransactionMeta): TransactionPayStrategy[] => {
154+
if (this.#getStrategy) {
155+
return [this.#getStrategy(transaction)];
156+
}
157+
158+
return getStrategyOrder(this.messenger);
159+
});
160+
144161
this.messenger.registerActionHandler(
145162
'TransactionPayController:getDelegationTransaction',
146163
this.#getDelegationTransaction.bind(this),
147164
);
148165

149166
this.messenger.registerActionHandler(
150167
'TransactionPayController:getStrategy',
151-
this.#getStrategy ??
152-
((): TransactionPayStrategy => TransactionPayStrategy.Relay),
168+
(transaction: TransactionMeta): TransactionPayStrategy =>
169+
getStrategies(transaction)[0] ?? TransactionPayStrategy.Relay,
170+
);
171+
172+
this.messenger.registerActionHandler(
173+
'TransactionPayController:getStrategies',
174+
(transaction: TransactionMeta): TransactionPayStrategy[] =>
175+
getStrategies(transaction),
153176
);
154177

155178
this.messenger.registerActionHandler(

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ export const POLYGON_USDCE_ADDRESS =
1414
'0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174' as Hex;
1515

1616
export enum TransactionPayStrategy {
17+
Across = 'across',
1718
Bridge = 'bridge',
1819
Relay = 'relay',
19-
TokenPay = 'tokenPay',
2020
Test = 'test',
2121
}

0 commit comments

Comments
 (0)