@metamask-previews/transaction-pay-controller 29.0.0-preview-d43f7b9f4 → 29.0.0-preview-2731bd7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -10,9 +10,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
10
10
  ### Changed
11
11
 
12
12
  - Bump `@metamask/transaction-controller` from `^70.0.1` to `^70.1.0` ([#10262](https://github.com/MetaMask/core/pull/10262))
13
- - Subsidized max Money Account Relay deposits now use atomic `EXACT_OUTPUT` quotes with vault calls embedded. The output amount is discovered from the source-token budget first, even when the client hints atomic execution, rather than reusing the original deposit's required amount. ([#10224](https://github.com/MetaMask/core/pull/10224))
14
- - Failed atomic promotions are terminal `no-quotes` errors prefixed with `Atomic promotion failed`, and will block rather than silently fall back.
15
- - Non-subsidized max behavior is unchanged.
16
13
 
17
14
  ## [29.0.0]
18
15
 
@@ -2,7 +2,6 @@ import { TransactionType } from '@metamask/transaction-controller';
2
2
  import type { Hex } from '@metamask/utils';
3
3
  import type { RelayStatus } from './types.js';
4
4
  export declare const FALLBACK_HASH: Hex;
5
- export declare const ATOMIC_PROMOTION_FAILURE_PREFIX = "Atomic promotion failed: ";
6
5
  export declare const RELAY_URL_BASE = "https://api.relay.link";
7
6
  export declare const RELAY_AUTHORIZE_URL = "https://api.relay.link/authorize";
8
7
  export declare const RELAY_EXECUTE_URL = "https://api.relay.link/execute";
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/strategy/relay/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAG3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,eAAO,MAAM,aAAa,EAAY,GAAG,CAAC;AAE1C,eAAO,MAAM,+BAA+B,8BAA8B,CAAC;AAE3E,eAAO,MAAM,cAAc,2BAA2B,CAAC;AACvD,eAAO,MAAM,mBAAmB,qCAAgC,CAAC;AACjE,eAAO,MAAM,iBAAiB,mCAA8B,CAAC;AAC7D,eAAO,MAAM,eAAe,iCAA4B,CAAC;AACzD,eAAO,MAAM,gBAAgB,6CAAwC,CAAC;AACtE,eAAO,MAAM,oBAAoB,gCAAgC,CAAC;AAClE,eAAO,MAAM,oBAAoB,qCAAiC,CAAC;AACnE,eAAO,MAAM,wBAAwB,yCAAqC,CAAC;AAC3E,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAC3C,eAAO,MAAM,wBAAwB,eAAe,CAAC;AAErD,eAAO,MAAM,sBAAsB,EAAE,WAAW,EAI/C,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,WAAW,EAM/C,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAS/D,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/strategy/relay/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAG3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,eAAO,MAAM,aAAa,EAAY,GAAG,CAAC;AAE1C,eAAO,MAAM,cAAc,2BAA2B,CAAC;AACvD,eAAO,MAAM,mBAAmB,qCAAgC,CAAC;AACjE,eAAO,MAAM,iBAAiB,mCAA8B,CAAC;AAC7D,eAAO,MAAM,eAAe,iCAA4B,CAAC;AACzD,eAAO,MAAM,gBAAgB,6CAAwC,CAAC;AACtE,eAAO,MAAM,oBAAoB,gCAAgC,CAAC;AAClE,eAAO,MAAM,oBAAoB,qCAAiC,CAAC;AACnE,eAAO,MAAM,wBAAwB,yCAAqC,CAAC;AAC3E,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAC3C,eAAO,MAAM,wBAAwB,eAAe,CAAC;AAErD,eAAO,MAAM,sBAAsB,EAAE,WAAW,EAI/C,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,WAAW,EAM/C,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAS/D,CAAC"}
@@ -1,7 +1,6 @@
1
1
  import { TransactionType } from '@metamask/transaction-controller';
2
2
  import { PERPS_DEPOSIT_TYPES } from '../../constants.js';
3
3
  export const FALLBACK_HASH = '0x0';
4
- export const ATOMIC_PROMOTION_FAILURE_PREFIX = 'Atomic promotion failed: ';
5
4
  export const RELAY_URL_BASE = 'https://api.relay.link';
6
5
  export const RELAY_AUTHORIZE_URL = `${RELAY_URL_BASE}/authorize`;
7
6
  export const RELAY_EXECUTE_URL = `${RELAY_URL_BASE}/execute`;
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/strategy/relay/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAGnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAGzD,MAAM,CAAC,MAAM,aAAa,GAAG,KAAY,CAAC;AAE1C,MAAM,CAAC,MAAM,+BAA+B,GAAG,2BAA2B,CAAC;AAE3E,MAAM,CAAC,MAAM,cAAc,GAAG,wBAAwB,CAAC;AACvD,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,cAAc,YAAY,CAAC;AACjE,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,cAAc,UAAU,CAAC;AAC7D,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,cAAc,QAAQ,CAAC;AACzD,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,cAAc,oBAAoB,CAAC;AACtE,MAAM,CAAC,MAAM,oBAAoB,GAAG,6BAA6B,CAAC;AAClE,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,oBAAoB,OAAO,CAAC;AACnE,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,oBAAoB,WAAW,CAAC;AAC3E,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAC,CAAC,WAAW;AACvD,MAAM,CAAC,MAAM,wBAAwB,GAAG,YAAY,CAAC;AAErD,MAAM,CAAC,MAAM,sBAAsB,GAAkB;IACnD,SAAS;IACT,QAAQ;IACR,UAAU;CACX,CAAC;AAEF,MAAM,CAAC,MAAM,sBAAsB,GAAkB;IACnD,SAAS;IACT,YAAY;IACZ,SAAS;IACT,WAAW;IACX,SAAS;CACV,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAoC;IAClE,CAAC,eAAe,CAAC,cAAc,CAAC,EAAE,eAAe,CAAC,gBAAgB;IAClE,CAAC,eAAe,CAAC,cAAc,CAAC,EAAE,eAAe,CAAC,mBAAmB;IACrE,GAAG,MAAM,CAAC,WAAW,CACnB,mBAAmB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAChC,IAAI;QACJ,eAAe,CAAC,iBAAiB;KAClC,CAAC,CACH;CACF,CAAC","sourcesContent":["import { TransactionType } from '@metamask/transaction-controller';\nimport type { Hex } from '@metamask/utils';\n\nimport { PERPS_DEPOSIT_TYPES } from '../../constants.js';\nimport type { RelayStatus } from './types.js';\n\nexport const FALLBACK_HASH = '0x0' as Hex;\n\nexport const ATOMIC_PROMOTION_FAILURE_PREFIX = 'Atomic promotion failed: ';\n\nexport const RELAY_URL_BASE = 'https://api.relay.link';\nexport const RELAY_AUTHORIZE_URL = `${RELAY_URL_BASE}/authorize`;\nexport const RELAY_EXECUTE_URL = `${RELAY_URL_BASE}/execute`;\nexport const RELAY_QUOTE_URL = `${RELAY_URL_BASE}/quote`;\nexport const RELAY_STATUS_URL = `${RELAY_URL_BASE}/intents/status/v3`;\nexport const HYPERLIQUID_URL_BASE = 'https://api.hyperliquid.xyz';\nexport const HYPERLIQUID_INFO_URL = `${HYPERLIQUID_URL_BASE}/info`;\nexport const HYPERLIQUID_EXCHANGE_URL = `${HYPERLIQUID_URL_BASE}/exchange`;\nexport const RELAY_POLLING_INTERVAL = 1000; // 1 Second\nexport const TOKEN_TRANSFER_FOUR_BYTE = '0xa9059cbb';\n\nexport const RELAY_FAILURE_STATUSES: RelayStatus[] = [\n 'failure',\n 'refund',\n 'refunded',\n];\n\nexport const RELAY_PENDING_STATUSES: RelayStatus[] = [\n 'delayed',\n 'depositing',\n 'pending',\n 'submitted',\n 'waiting',\n];\n\nexport const RELAY_DEPOSIT_TYPES: Record<string, TransactionType> = {\n [TransactionType.musdConversion]: TransactionType.musdRelayDeposit,\n [TransactionType.predictDeposit]: TransactionType.predictRelayDeposit,\n ...Object.fromEntries(\n PERPS_DEPOSIT_TYPES.map((type) => [\n type,\n TransactionType.perpsRelayDeposit,\n ]),\n ),\n};\n"]}
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/strategy/relay/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAGnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAGzD,MAAM,CAAC,MAAM,aAAa,GAAG,KAAY,CAAC;AAE1C,MAAM,CAAC,MAAM,cAAc,GAAG,wBAAwB,CAAC;AACvD,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,cAAc,YAAY,CAAC;AACjE,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,cAAc,UAAU,CAAC;AAC7D,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,cAAc,QAAQ,CAAC;AACzD,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,cAAc,oBAAoB,CAAC;AACtE,MAAM,CAAC,MAAM,oBAAoB,GAAG,6BAA6B,CAAC;AAClE,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,oBAAoB,OAAO,CAAC;AACnE,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,oBAAoB,WAAW,CAAC;AAC3E,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAC,CAAC,WAAW;AACvD,MAAM,CAAC,MAAM,wBAAwB,GAAG,YAAY,CAAC;AAErD,MAAM,CAAC,MAAM,sBAAsB,GAAkB;IACnD,SAAS;IACT,QAAQ;IACR,UAAU;CACX,CAAC;AAEF,MAAM,CAAC,MAAM,sBAAsB,GAAkB;IACnD,SAAS;IACT,YAAY;IACZ,SAAS;IACT,WAAW;IACX,SAAS;CACV,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAoC;IAClE,CAAC,eAAe,CAAC,cAAc,CAAC,EAAE,eAAe,CAAC,gBAAgB;IAClE,CAAC,eAAe,CAAC,cAAc,CAAC,EAAE,eAAe,CAAC,mBAAmB;IACrE,GAAG,MAAM,CAAC,WAAW,CACnB,mBAAmB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAChC,IAAI;QACJ,eAAe,CAAC,iBAAiB;KAClC,CAAC,CACH;CACF,CAAC","sourcesContent":["import { TransactionType } from '@metamask/transaction-controller';\nimport type { Hex } from '@metamask/utils';\n\nimport { PERPS_DEPOSIT_TYPES } from '../../constants.js';\nimport type { RelayStatus } from './types.js';\n\nexport const FALLBACK_HASH = '0x0' as Hex;\n\nexport const RELAY_URL_BASE = 'https://api.relay.link';\nexport const RELAY_AUTHORIZE_URL = `${RELAY_URL_BASE}/authorize`;\nexport const RELAY_EXECUTE_URL = `${RELAY_URL_BASE}/execute`;\nexport const RELAY_QUOTE_URL = `${RELAY_URL_BASE}/quote`;\nexport const RELAY_STATUS_URL = `${RELAY_URL_BASE}/intents/status/v3`;\nexport const HYPERLIQUID_URL_BASE = 'https://api.hyperliquid.xyz';\nexport const HYPERLIQUID_INFO_URL = `${HYPERLIQUID_URL_BASE}/info`;\nexport const HYPERLIQUID_EXCHANGE_URL = `${HYPERLIQUID_URL_BASE}/exchange`;\nexport const RELAY_POLLING_INTERVAL = 1000; // 1 Second\nexport const TOKEN_TRANSFER_FOUR_BYTE = '0xa9059cbb';\n\nexport const RELAY_FAILURE_STATUSES: RelayStatus[] = [\n 'failure',\n 'refund',\n 'refunded',\n];\n\nexport const RELAY_PENDING_STATUSES: RelayStatus[] = [\n 'delayed',\n 'depositing',\n 'pending',\n 'submitted',\n 'waiting',\n];\n\nexport const RELAY_DEPOSIT_TYPES: Record<string, TransactionType> = {\n [TransactionType.musdConversion]: TransactionType.musdRelayDeposit,\n [TransactionType.predictDeposit]: TransactionType.predictRelayDeposit,\n ...Object.fromEntries(\n PERPS_DEPOSIT_TYPES.map((type) => [\n type,\n TransactionType.perpsRelayDeposit,\n ]),\n ),\n};\n"]}
@@ -18,13 +18,5 @@ type GetSingleQuoteFn = (request: QuoteRequest, fullRequest: PayStrategyGetQuote
18
18
  * @returns The validated adjusted phase-2 quote, or the original phase-1 quote on fallback.
19
19
  */
20
20
  export declare function getRelayMaxGasStationQuote(request: QuoteRequest, fullRequest: PayStrategyGetQuotesRequest, getSingleQuote: GetSingleQuoteFn): Promise<TransactionPayQuote<RelayQuote>>;
21
- export declare function maybePromoteSubsidizedMaxMoneyAccountQuote({ discoveryQuote, fullRequest, getSingleQuote, request, }: {
22
- discoveryQuote: TransactionPayQuote<RelayQuote>;
23
- fullRequest: PayStrategyGetQuotesRequest;
24
- getSingleQuote: GetSingleQuoteFn;
25
- request: QuoteRequest;
26
- }): Promise<TransactionPayQuote<RelayQuote>>;
27
- export declare function throwAtomicPromotionFailed(error: unknown): never;
28
- export declare function isSubsidizedAtomicMaxQuote(quote: TransactionPayQuote<RelayQuote>): boolean;
29
21
  export {};
30
- //# sourceMappingURL=relay-max.d.ts.map
22
+ //# sourceMappingURL=relay-max-gas-station.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay-max-gas-station.d.ts","sourceRoot":"","sources":["../../../src/strategy/relay/relay-max-gas-station.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,2BAA2B,EAC3B,YAAY,EAEZ,mBAAmB,EACpB,MAAM,gBAAgB,CAAC;AAUxB,OAAO,KAAK,EAAE,UAAU,EAAwB,MAAM,YAAY,CAAC;AAuBnE,KAAK,gBAAgB,GAAG,CACtB,OAAO,EAAE,YAAY,EACrB,WAAW,EAAE,2BAA2B,KACrC,OAAO,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAC,CAAC;AAS9C;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,YAAY,EACrB,WAAW,EAAE,2BAA2B,EACxC,cAAc,EAAE,gBAAgB,GAC/B,OAAO,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAC,CAgJ1C"}
@@ -1,15 +1,9 @@
1
- import { toHex } from '@metamask/controller-utils';
2
1
  import { createModuleLogger } from '@metamask/utils';
3
2
  import { BigNumber } from 'bignumber.js';
4
3
  import { projectLogger } from '../../logger.js';
5
- import { prefixError } from '../../utils/error-prefix.js';
6
- import { isAtomicMaxPromotionEnabled } from '../../utils/feature-flags.js';
7
4
  import { getGasStationEligibility, getGasStationCostInSourceTokenRaw, } from '../../utils/gas-station.js';
8
5
  import { getNativeToken, getTokenBalance, getTokenInfo, } from '../../utils/token.js';
9
- import { QuoteError } from '../../utils/validation.js';
10
- import { ATOMIC_PROMOTION_FAILURE_PREFIX } from './constants.js';
11
- import { isSubsidizedRelayQuote } from './relay-submit-execute.js';
12
- const log = createModuleLogger(projectLogger, 'relay-max');
6
+ const log = createModuleLogger(projectLogger, 'relay-max-gas-station');
13
7
  const PROBE_AMOUNT_PERCENTAGE = 0.25;
14
8
  var GasCostEstimateSource;
15
9
  (function (GasCostEstimateSource) {
@@ -245,100 +239,4 @@ function markQuoteAsMaxGasStation(quote) {
245
239
  isMaxGasStation: true,
246
240
  };
247
241
  }
248
- export async function maybePromoteSubsidizedMaxMoneyAccountQuote({ discoveryQuote, fullRequest, getSingleQuote, request, }) {
249
- if (!shouldAttemptAtomicPromotion(request, fullRequest.transaction, discoveryQuote, fullRequest.messenger)) {
250
- return discoveryQuote;
251
- }
252
- try {
253
- const targetAmount = validatePositiveIntegerString(discoveryQuote.original.details.currencyOut.amount);
254
- const promotionTransaction = await applyAmountDataUpdates({
255
- amount: targetAmount,
256
- messenger: fullRequest.messenger,
257
- transaction: cloneTransactionForPromotion(fullRequest.transaction),
258
- });
259
- const promotedQuote = await getSingleQuote({
260
- ...request,
261
- atomic: true,
262
- targetAmountMinimum: targetAmount,
263
- }, { ...fullRequest, transaction: promotionTransaction });
264
- if (!isSubsidizedRelayQuote(promotedQuote.original)) {
265
- throw new Error('Promoted quote lost subsidy');
266
- }
267
- return promotedQuote;
268
- }
269
- catch (error) {
270
- return throwAtomicPromotionFailed(error);
271
- }
272
- }
273
- export function throwAtomicPromotionFailed(error) {
274
- const prefixed = prefixError(error, ATOMIC_PROMOTION_FAILURE_PREFIX);
275
- throw new QuoteError({
276
- detail: [prefixed.message],
277
- message: prefixed.message,
278
- reason: 'no-quotes',
279
- });
280
- }
281
- export function isSubsidizedAtomicMaxQuote(quote) {
282
- return (quote.request.isMaxAmount === true &&
283
- quote.request.atomic === true &&
284
- isSubsidizedRelayQuote(quote.original));
285
- }
286
- function shouldAttemptAtomicPromotion(request, transaction, discoveryQuote, messenger) {
287
- return (isAtomicMaxPromotionEnabled(messenger, transaction) &&
288
- request.isMaxAmount === true &&
289
- request.isPostQuote !== true &&
290
- request.atomic !== true &&
291
- isSubsidizedRelayQuote(discoveryQuote.original));
292
- }
293
- function validatePositiveIntegerString(value) {
294
- const amount = new BigNumber(value);
295
- if (!amount.isFinite() || !amount.isInteger() || !amount.isGreaterThan(0)) {
296
- throw new Error(`Invalid target amount: ${value}`);
297
- }
298
- return amount.toFixed(0);
299
- }
300
- function cloneTransactionForPromotion(transaction) {
301
- return {
302
- ...transaction,
303
- nestedTransactions: transaction.nestedTransactions?.map((nestedTransaction) => ({
304
- ...nestedTransaction,
305
- })),
306
- requiredAssets: transaction.requiredAssets?.map((requiredAsset) => ({
307
- ...requiredAsset,
308
- })),
309
- };
310
- }
311
- async function applyAmountDataUpdates({ amount, messenger, transaction, }) {
312
- const { updates } = await messenger.call('TransactionPayController:getAmountData', {
313
- amount,
314
- transaction,
315
- });
316
- if (!updates.length) {
317
- throw new Error('getAmountData returned no updates for atomic promotion');
318
- }
319
- const nestedTransactions = transaction.nestedTransactions?.map((nestedTransaction) => ({
320
- ...nestedTransaction,
321
- }));
322
- if (!nestedTransactions?.length) {
323
- throw new Error('Missing nested transactions for atomic promotion');
324
- }
325
- for (const { nestedTransactionIndex, data } of updates) {
326
- if (!nestedTransactions[nestedTransactionIndex]) {
327
- throw new Error('getAmountData returned an unusable nested transaction update');
328
- }
329
- nestedTransactions[nestedTransactionIndex].data = data;
330
- }
331
- const requiredAssets = transaction.requiredAssets?.map((requiredAsset) => ({
332
- ...requiredAsset,
333
- }));
334
- if (!requiredAssets?.[0]) {
335
- throw new Error('Missing required assets for atomic promotion');
336
- }
337
- requiredAssets[0].amount = toHex(BigInt(amount));
338
- return {
339
- ...transaction,
340
- nestedTransactions,
341
- requiredAssets,
342
- };
343
- }
344
- //# sourceMappingURL=relay-max.js.map
242
+ //# sourceMappingURL=relay-max-gas-station.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay-max-gas-station.js","sourceRoot":"","sources":["../../../src/strategy/relay/relay-max-gas-station.ts"],"names":[],"mappings":"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","sourcesContent":["import { createModuleLogger } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport { projectLogger } from '../../logger.js';\nimport type {\n PayStrategyGetQuotesRequest,\n QuoteRequest,\n TransactionPayControllerMessenger,\n TransactionPayQuote,\n} from '../../types.js';\nimport {\n getGasStationEligibility,\n getGasStationCostInSourceTokenRaw,\n} from '../../utils/gas-station.js';\nimport {\n getNativeToken,\n getTokenBalance,\n getTokenInfo,\n} from '../../utils/token.js';\nimport type { RelayQuote, RelayTransactionStep } from './types.js';\n\nconst log = createModuleLogger(projectLogger, 'relay-max-gas-station');\n\nconst PROBE_AMOUNT_PERCENTAGE = 0.25;\n\nenum GasCostEstimateSource {\n GasStation = 'gas-station',\n Probe = 'probe',\n Quote = 'quote',\n}\n\ntype GasCostEstimate = {\n amount: BigNumber;\n source: GasCostEstimateSource;\n};\n\ntype NativeBalanceCheckResult = {\n hasEnoughNativeBalance: boolean;\n nativeBalance?: string;\n nativeGasCostRaw?: string;\n};\n\ntype GetSingleQuoteFn = (\n request: QuoteRequest,\n fullRequest: PayStrategyGetQuotesRequest,\n) => Promise<TransactionPayQuote<RelayQuote>>;\n\ntype MaxAmountQuoteContext = {\n fullRequest: PayStrategyGetQuotesRequest;\n getSingleQuote: GetSingleQuoteFn;\n messenger: TransactionPayControllerMessenger;\n request: QuoteRequest;\n};\n\n/**\n * Returns a Relay max-amount quote using a two-phase gas-station fallback.\n *\n * It first requests a standard max quote (phase 1), then when needed estimates\n * gas in source-token units (directly or via a probe quote), requests an\n * adjusted max quote (phase 2), and accepts phase 2 only if validation passes\n * (source gas fee token selected, gas-limit checks, affordability).\n *\n * If any step fails or validation is unsafe, it safely falls back to phase 1.\n * Successful phase-2 quotes are tagged with `metamask.isMaxGasStation = true`.\n *\n * @param request - Relay quote request for a max-amount flow.\n * @param fullRequest - Full quote request context including messenger and transaction.\n * @param getSingleQuote - Quote fetcher used for phase-1, phase-2, and probe quotes.\n * @returns The validated adjusted phase-2 quote, or the original phase-1 quote on fallback.\n */\nexport async function getRelayMaxGasStationQuote(\n request: QuoteRequest,\n fullRequest: PayStrategyGetQuotesRequest,\n getSingleQuote: GetSingleQuoteFn,\n): Promise<TransactionPayQuote<RelayQuote>> {\n const { messenger } = fullRequest;\n const { sourceChainId, sourceTokenAmount } = request;\n const context: MaxAmountQuoteContext = {\n fullRequest,\n getSingleQuote,\n messenger,\n request,\n };\n\n const phase1Quote = await getSingleQuote(request, fullRequest);\n\n if (phase1Quote.original.metamask?.isExecute) {\n return phase1Quote;\n }\n\n const nativeBalanceCheck = checkEnoughNativeBalanceIfSourceGasFeeTokenNotUsed(\n phase1Quote,\n messenger,\n request,\n );\n\n if (nativeBalanceCheck.hasEnoughNativeBalance) {\n return fallbackToPhase1(\n phase1Quote,\n 'Native balance is sufficient for gas',\n {\n nativeBalance: nativeBalanceCheck.nativeBalance,\n nativeGasCost: nativeBalanceCheck.nativeGasCostRaw,\n },\n );\n }\n\n const gasStationEligibility = getGasStationEligibility(\n messenger,\n sourceChainId,\n );\n\n if (!gasStationEligibility.isEligible) {\n return fallbackToPhase1(\n phase1Quote,\n 'Gas station is disabled or unsupported',\n );\n }\n\n const sourceAmountBN = new BigNumber(sourceTokenAmount);\n const probeCost = await getProbeGasCostInSourceTokenRaw(context);\n\n if (!probeCost) {\n return fallbackToPhase1(\n phase1Quote,\n 'Unable to estimate gas-station source token cost',\n );\n }\n\n const initialGasEstimate: GasCostEstimate = {\n amount: probeCost,\n source: GasCostEstimateSource.Probe,\n };\n\n const adjustedSourceAmount = getAdjustedSourceAmount(\n sourceAmountBN,\n initialGasEstimate.amount,\n );\n\n if (!adjustedSourceAmount.isGreaterThan(0)) {\n return fallbackToPhase1(\n phase1Quote,\n 'Insufficient balance for gas station after max adjustment',\n );\n }\n\n const phase2Quote = await getAdjustedPhase2Quote(\n adjustedSourceAmount,\n initialGasEstimate,\n context,\n );\n\n if (!phase2Quote) {\n return fallbackToPhase1(\n phase1Quote,\n 'Adjusted phase-2 quote request failed',\n );\n }\n\n if (!phase2Quote.fees.isSourceGasFeeToken) {\n return fallbackToPhase1(\n phase1Quote,\n 'Adjusted quote did not return source gas fee token pricing',\n );\n }\n\n const phase1GasLimits = phase1Quote.original.metamask.gasLimits ?? [];\n const phase2GasLimits = phase2Quote.original.metamask.gasLimits ?? [];\n\n if (!hasMatchingGasLimitShape(phase1GasLimits, phase2GasLimits)) {\n return fallbackToPhase1(\n phase1Quote,\n 'Adjusted quote gas limit shape changed between phases',\n {\n phase1GasLimits,\n phase2GasLimits,\n },\n );\n }\n\n const phase1TotalGasLimit = getTotalGasLimit(phase1GasLimits);\n const phase2TotalGasLimit = getTotalGasLimit(phase2GasLimits);\n\n if (phase2TotalGasLimit > phase1TotalGasLimit) {\n return fallbackToPhase1(\n phase1Quote,\n 'Adjusted quote total gas limit increased between phases',\n {\n phase1TotalGasLimit,\n phase2TotalGasLimit,\n },\n );\n }\n\n const validationGasEstimate = new BigNumber(\n phase2Quote.fees.sourceNetwork.max.raw,\n );\n\n if (\n !isAdjustedAmountAffordable(\n adjustedSourceAmount,\n validationGasEstimate,\n sourceAmountBN,\n )\n ) {\n return fallbackToPhase1(\n phase1Quote,\n 'Adjusted quote fails affordability validation',\n {\n adjustedSourceAmount: adjustedSourceAmount.toString(10),\n validationGasCost: validationGasEstimate.toString(10),\n },\n );\n }\n\n markQuoteAsMaxGasStation(phase2Quote);\n\n return phase2Quote;\n}\n\nfunction checkEnoughNativeBalanceIfSourceGasFeeTokenNotUsed(\n quote: TransactionPayQuote<RelayQuote>,\n messenger: TransactionPayControllerMessenger,\n request: QuoteRequest,\n): NativeBalanceCheckResult {\n if (quote.fees.isSourceGasFeeToken) {\n return { hasEnoughNativeBalance: false };\n }\n\n const nativeGasCostRaw = quote.fees.sourceNetwork.max.raw;\n const nativeBalance = getTokenBalance(\n messenger,\n request.from,\n request.sourceChainId,\n getNativeToken(request.sourceChainId),\n );\n\n return {\n hasEnoughNativeBalance: new BigNumber(nativeBalance).isGreaterThanOrEqualTo(\n nativeGasCostRaw,\n ),\n nativeBalance,\n nativeGasCostRaw,\n };\n}\n\nfunction getAdjustedSourceAmount(\n sourceAmount: BigNumber,\n estimatedGasCost: BigNumber,\n): BigNumber {\n return sourceAmount\n .minus(estimatedGasCost)\n .integerValue(BigNumber.ROUND_DOWN);\n}\n\nfunction isAdjustedAmountAffordable(\n adjustedSourceAmount: BigNumber,\n gasCost: BigNumber,\n originalSourceAmount: BigNumber,\n): boolean {\n return adjustedSourceAmount\n .plus(gasCost)\n .isLessThanOrEqualTo(originalSourceAmount);\n}\n\nasync function getAdjustedPhase2Quote(\n adjustedSourceAmount: BigNumber,\n initialGasEstimate: GasCostEstimate,\n context: MaxAmountQuoteContext,\n): Promise<TransactionPayQuote<RelayQuote> | undefined> {\n const { fullRequest, getSingleQuote, request } = context;\n\n log('Requesting adjusted max quote', {\n adjustedAmount: adjustedSourceAmount.toString(10),\n gasCostInSourceToken: initialGasEstimate.amount.toString(10),\n gasEstimateSource: initialGasEstimate.source,\n originalAmount: request.sourceTokenAmount,\n });\n\n try {\n return await getSingleQuote(\n {\n ...request,\n sourceTokenAmount: adjustedSourceAmount.toFixed(\n 0,\n BigNumber.ROUND_DOWN,\n ),\n },\n fullRequest,\n );\n } catch (error) {\n log('Adjusted quote request failed, falling back to phase-1 quote', {\n error,\n });\n return undefined;\n }\n}\n\nasync function getGasCostFromQuoteOrGasStation(\n quote: TransactionPayQuote<RelayQuote>,\n messenger: TransactionPayControllerMessenger,\n request: QuoteRequest,\n): Promise<GasCostEstimate | undefined> {\n const gasCost = quote.fees.sourceNetwork.max;\n\n if (quote.fees.isSourceGasFeeToken) {\n log('Gas cost already in source token units', { raw: gasCost.raw });\n return {\n amount: new BigNumber(gasCost.raw),\n source: GasCostEstimateSource.Quote,\n };\n }\n\n const firstTxStep = quote.original.steps.find(\n (step): step is RelayTransactionStep => step.kind === 'transaction',\n );\n const firstStepData = firstTxStep?.items[0]?.data;\n\n if (!firstStepData) {\n return undefined;\n }\n\n const totalItemCount = quote.original.steps.reduce(\n (count, step) => count + step.items.length,\n 0,\n );\n\n const totalGasEstimate = (quote.original.metamask.gasLimits ?? []).reduce(\n (acc, gasLimit) => acc + gasLimit,\n 0,\n );\n\n const gasStationCost = await getGasStationCostInSourceTokenRaw({\n firstStepData,\n messenger,\n request: {\n from: request.from,\n sourceChainId: request.sourceChainId,\n sourceTokenAddress: request.sourceTokenAddress,\n },\n totalGasEstimate,\n totalItemCount,\n });\n\n if (!gasStationCost) {\n return undefined;\n }\n\n return {\n amount: new BigNumber(gasStationCost.raw),\n source: GasCostEstimateSource.GasStation,\n };\n}\n\nasync function getProbeGasCostInSourceTokenRaw(\n context: MaxAmountQuoteContext,\n): Promise<BigNumber | undefined> {\n const { fullRequest, getSingleQuote, messenger, request } = context;\n\n const sourceTokenInfo = getTokenInfo(\n messenger,\n request.sourceTokenAddress,\n request.sourceChainId,\n );\n\n if (!sourceTokenInfo) {\n return undefined;\n }\n\n const probeAmount = getProbeSourceAmountRaw(\n request.sourceTokenAmount,\n sourceTokenInfo.decimals,\n );\n\n if (!probeAmount || probeAmount === request.sourceTokenAmount) {\n return undefined;\n }\n\n log('Requesting probe quote for gas station estimation', {\n originalSourceAmount: request.sourceTokenAmount,\n probeAmount,\n });\n\n let probeQuote: TransactionPayQuote<RelayQuote> | undefined;\n\n try {\n probeQuote = await getSingleQuote(\n {\n ...request,\n sourceTokenAmount: probeAmount,\n },\n fullRequest,\n );\n } catch (error) {\n log('Probe quote request failed', { error });\n return undefined;\n }\n\n if (!probeQuote) {\n return undefined;\n }\n\n const probeEstimate = await getGasCostFromQuoteOrGasStation(\n probeQuote,\n messenger,\n request,\n );\n\n return probeEstimate?.amount;\n}\n\nfunction getProbeSourceAmountRaw(\n sourceAmountRaw: string,\n sourceDecimals: number,\n): string | undefined {\n const sourceAmount = new BigNumber(sourceAmountRaw);\n\n if (sourceAmount.isLessThanOrEqualTo(0)) {\n return undefined;\n }\n\n const probeRawAmount = sourceAmount\n .multipliedBy(PROBE_AMOUNT_PERCENTAGE)\n .integerValue(BigNumber.ROUND_FLOOR);\n\n // Minimum probe size: ~0.01 token for tokens with >=2 decimals,\n // otherwise one raw unit for low-decimal tokens.\n const minimumProbeRaw = new BigNumber(1).shiftedBy(\n Math.max(sourceDecimals - 2, 0),\n );\n\n const probeRaw = BigNumber.minimum(\n sourceAmount,\n BigNumber.maximum(probeRawAmount, minimumProbeRaw),\n ).integerValue(BigNumber.ROUND_FLOOR);\n\n if (probeRaw.isLessThanOrEqualTo(0)) {\n return undefined;\n }\n\n return probeRaw.toFixed(0);\n}\n\nfunction fallbackToPhase1(\n phase1Quote: TransactionPayQuote<RelayQuote>,\n message: string,\n paramsToLog?: Record<string, unknown>,\n): TransactionPayQuote<RelayQuote> {\n log(message, paramsToLog);\n return phase1Quote;\n}\n\nfunction hasMatchingGasLimitShape(phase1: number[], phase2: number[]): boolean {\n return phase1.length === phase2.length;\n}\n\nfunction getTotalGasLimit(gasLimits: number[]): number {\n return gasLimits.reduce((total, gasLimit) => total + gasLimit, 0);\n}\n\nfunction markQuoteAsMaxGasStation(\n quote: TransactionPayQuote<RelayQuote>,\n): void {\n quote.original.metamask = {\n ...quote.original.metamask,\n isMaxGasStation: true,\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"relay-quotes.d.ts","sourceRoot":"","sources":["../../../src/strategy/relay/relay-quotes.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAGV,2BAA2B,EAG3B,mBAAmB,EACpB,MAAM,gBAAgB,CAAC;AAwCxB,OAAO,KAAK,EACV,UAAU,EAIX,MAAM,YAAY,CAAC;AAuBpB;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,mBAAmB,CAAC,UAAU,CAAC,EAAE,CAAC,CAsE5C"}
1
+ {"version":3,"file":"relay-quotes.d.ts","sourceRoot":"","sources":["../../../src/strategy/relay/relay-quotes.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAGV,2BAA2B,EAG3B,mBAAmB,EACpB,MAAM,gBAAgB,CAAC;AAkCxB,OAAO,KAAK,EACV,UAAU,EAIX,MAAM,YAAY,CAAC;AAuBpB;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,mBAAmB,CAAC,UAAU,CAAC,EAAE,CAAC,CAkD5C"}
@@ -8,7 +8,7 @@ import { ARBITRUM_USDC_ADDRESS, CHAIN_ID_ARBITRUM, CHAIN_ID_HYPERCORE, CHAIN_ID_
8
8
  import { TransactionPayStrategy } from '../../index.js';
9
9
  import { projectLogger } from '../../logger.js';
10
10
  import { getFiatValueFromUsd } from '../../utils/amounts.js';
11
- import { getFeatureFlags, getRelayOriginGasOverhead, getSlippage, getStablecoins, isAtomicMaxPromotionEnabled, isEIP7702Chain, isRelayExecuteEnabled, } from '../../utils/feature-flags.js';
11
+ import { getFeatureFlags, getRelayOriginGasOverhead, getSlippage, getStablecoins, isEIP7702Chain, isRelayExecuteEnabled, } from '../../utils/feature-flags.js';
12
12
  import { getGasStationCostInSourceTokenRaw, getGasStationEligibility, } from '../../utils/gas-station.js';
13
13
  import { calculateGasCost } from '../../utils/gas.js';
14
14
  import { estimateQuoteGasLimits } from '../../utils/quote-gas.js';
@@ -17,7 +17,7 @@ import { TOKEN_TRANSFER_FOUR_BYTE } from './constants.js';
17
17
  import { applyHyperliquidActivationFee } from './hyperliquid-activation.js';
18
18
  import { applyPolymarketDepositWalletOverrides, getPredictWithdrawSafeAddress, isPredictWithdraw, } from './polymarket/withdraw.js';
19
19
  import { fetchRelayQuote } from './relay-api.js';
20
- import { getRelayMaxGasStationQuote, isSubsidizedAtomicMaxQuote, maybePromoteSubsidizedMaxMoneyAccountQuote, throwAtomicPromotionFailed, } from './relay-max.js';
20
+ import { getRelayMaxGasStationQuote } from './relay-max-gas-station.js';
21
21
  import { validateRelayQuotes } from './relay-validation.js';
22
22
  const log = createModuleLogger(projectLogger, 'relay-strategy');
23
23
  // Buffer applied to the gas cost when reserving native tokens for gas in
@@ -48,29 +48,12 @@ export async function getRelayQuotes(request) {
48
48
  .map((normalizedRequest) => applyHyperliquidActivationFee(normalizedRequest, request.messenger, request.transaction, request.signal)));
49
49
  log('Normalized requests', normalizedRequests);
50
50
  const quotes = await Promise.all(normalizedRequests.map((singleRequest) => getQuoteWithMaxAmountHandling(singleRequest, request)));
51
- const atomicMaxQuotes = quotes.filter(isSubsidizedAtomicMaxQuote);
52
- const otherQuotes = quotes.filter((quote) => !isSubsidizedAtomicMaxQuote(quote));
53
- if (otherQuotes.length > 0) {
54
- await validateRelayQuotes({
55
- messenger: request.messenger,
56
- quotes: otherQuotes,
57
- signal: request.signal,
58
- transaction: request.transaction,
59
- });
60
- }
61
- if (atomicMaxQuotes.length > 0) {
62
- try {
63
- await validateRelayQuotes({
64
- messenger: request.messenger,
65
- quotes: atomicMaxQuotes,
66
- signal: request.signal,
67
- transaction: request.transaction,
68
- });
69
- }
70
- catch (error) {
71
- throwAtomicPromotionFailed(error);
72
- }
73
- }
51
+ await validateRelayQuotes({
52
+ messenger: request.messenger,
53
+ quotes,
54
+ signal: request.signal,
55
+ transaction: request.transaction,
56
+ });
74
57
  return quotes;
75
58
  }
76
59
  catch (error) {
@@ -83,24 +66,7 @@ async function getQuoteWithMaxAmountHandling(request, fullRequest) {
83
66
  if (!isMaxAmount) {
84
67
  return getQuoteWithPostQuoteGasHandling(request, fullRequest);
85
68
  }
86
- const isAtomicPromotionEligible = isAtomicMaxPromotionEnabled(fullRequest.messenger, fullRequest.transaction);
87
- // The required target amount may predate Max. An atomic hint does not
88
- // establish the output obtainable from the source budget; discover it first.
89
- const discoveryRequest = request.atomic !== false &&
90
- request.isPostQuote !== true &&
91
- (isAtomicPromotionEligible ||
92
- hasTransactionType(fullRequest.transaction, [
93
- TransactionType.moneyAccountDeposit,
94
- ]))
95
- ? { ...request, atomic: false }
96
- : request;
97
- const discoveryQuote = await getRelayMaxGasStationQuote(discoveryRequest, fullRequest, getSingleQuote);
98
- return maybePromoteSubsidizedMaxMoneyAccountQuote({
99
- discoveryQuote,
100
- fullRequest,
101
- getSingleQuote,
102
- request: discoveryRequest,
103
- });
69
+ return getRelayMaxGasStationQuote(request, fullRequest, getSingleQuote);
104
70
  }
105
71
  /**
106
72
  * For post-quote flows, fetch an initial quote to compute gas cost in source
@@ -326,11 +292,7 @@ async function processTransactions(transaction, request, requestBody, messenger)
326
292
  log('Skipping delegation as token transfer or Hypercore deposit');
327
293
  return true;
328
294
  }
329
- // Eligible atomic max requests carry a destination amount, so the calls
330
- // below use EXACT_OUTPUT rather than the usual max EXACT_INPUT quote.
331
- if (isMaxAmount &&
332
- (request.isPostQuote === true ||
333
- !isAtomicMaxPromotionEnabled(messenger, transaction))) {
295
+ if (isMaxAmount) {
334
296
  throw new Error('Max amount quotes do not support included transactions');
335
297
  }
336
298
  const delegation = await messenger.call('TransactionPayController:getDelegationTransaction', { transaction });