@metamask/transaction-pay-controller 16.3.0 → 16.4.0
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 +10 -1
- package/dist/TransactionPayController.cjs +9 -0
- package/dist/TransactionPayController.cjs.map +1 -1
- package/dist/TransactionPayController.d.cts +2 -1
- package/dist/TransactionPayController.d.cts.map +1 -1
- package/dist/TransactionPayController.d.mts +2 -1
- package/dist/TransactionPayController.d.mts.map +1 -1
- package/dist/TransactionPayController.mjs +9 -0
- package/dist/TransactionPayController.mjs.map +1 -1
- package/dist/actions/update-fiat-payment.cjs +29 -0
- package/dist/actions/update-fiat-payment.cjs.map +1 -0
- package/dist/actions/update-fiat-payment.d.cts +14 -0
- package/dist/actions/update-fiat-payment.d.cts.map +1 -0
- package/dist/actions/update-fiat-payment.d.mts +14 -0
- package/dist/actions/update-fiat-payment.d.mts.map +1 -0
- package/dist/actions/update-fiat-payment.mjs +25 -0
- package/dist/actions/update-fiat-payment.mjs.map +1 -0
- package/dist/actions/update-payment-token.cjs +1 -0
- package/dist/actions/update-payment-token.cjs.map +1 -1
- package/dist/actions/update-payment-token.d.cts.map +1 -1
- package/dist/actions/update-payment-token.d.mts.map +1 -1
- package/dist/actions/update-payment-token.mjs +1 -0
- package/dist/actions/update-payment-token.mjs.map +1 -1
- package/dist/constants.cjs +1 -0
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +1 -0
- package/dist/constants.d.cts.map +1 -1
- package/dist/constants.d.mts +1 -0
- package/dist/constants.d.mts.map +1 -1
- package/dist/constants.mjs +1 -0
- package/dist/constants.mjs.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/strategy/across/AcrossStrategy.cjs +28 -0
- package/dist/strategy/across/AcrossStrategy.cjs.map +1 -0
- package/dist/strategy/across/AcrossStrategy.d.cts +8 -0
- package/dist/strategy/across/AcrossStrategy.d.cts.map +1 -0
- package/dist/strategy/across/AcrossStrategy.d.mts +8 -0
- package/dist/strategy/across/AcrossStrategy.d.mts.map +1 -0
- package/dist/strategy/across/AcrossStrategy.mjs +24 -0
- package/dist/strategy/across/AcrossStrategy.mjs.map +1 -0
- package/dist/strategy/across/across-quotes.cjs +356 -0
- package/dist/strategy/across/across-quotes.cjs.map +1 -0
- package/dist/strategy/across/across-quotes.d.cts +10 -0
- package/dist/strategy/across/across-quotes.d.cts.map +1 -0
- package/dist/strategy/across/across-quotes.d.mts +10 -0
- package/dist/strategy/across/across-quotes.d.mts.map +1 -0
- package/dist/strategy/across/across-quotes.mjs +352 -0
- package/dist/strategy/across/across-quotes.mjs.map +1 -0
- package/dist/strategy/across/across-submit.cjs +241 -0
- package/dist/strategy/across/across-submit.cjs.map +1 -0
- package/dist/strategy/across/across-submit.d.cts +13 -0
- package/dist/strategy/across/across-submit.d.cts.map +1 -0
- package/dist/strategy/across/across-submit.d.mts +13 -0
- package/dist/strategy/across/across-submit.d.mts.map +1 -0
- package/dist/strategy/across/across-submit.mjs +237 -0
- package/dist/strategy/across/across-submit.mjs.map +1 -0
- package/dist/strategy/across/types.cjs +3 -0
- package/dist/strategy/across/types.cjs.map +1 -0
- package/dist/strategy/across/types.d.cts +89 -0
- package/dist/strategy/across/types.d.cts.map +1 -0
- package/dist/strategy/across/types.d.mts +89 -0
- package/dist/strategy/across/types.d.mts.map +1 -0
- package/dist/strategy/across/types.mjs +2 -0
- package/dist/strategy/across/types.mjs.map +1 -0
- package/dist/strategy/relay/RelayStrategy.cjs +5 -0
- package/dist/strategy/relay/RelayStrategy.cjs.map +1 -1
- package/dist/strategy/relay/RelayStrategy.d.cts +1 -0
- package/dist/strategy/relay/RelayStrategy.d.cts.map +1 -1
- package/dist/strategy/relay/RelayStrategy.d.mts +1 -0
- package/dist/strategy/relay/RelayStrategy.d.mts.map +1 -1
- package/dist/strategy/relay/RelayStrategy.mjs +5 -0
- package/dist/strategy/relay/RelayStrategy.mjs.map +1 -1
- package/dist/strategy/relay/gas-station.cjs +92 -0
- package/dist/strategy/relay/gas-station.cjs.map +1 -0
- package/dist/strategy/relay/gas-station.d.cts +22 -0
- package/dist/strategy/relay/gas-station.d.cts.map +1 -0
- package/dist/strategy/relay/gas-station.d.mts +22 -0
- package/dist/strategy/relay/gas-station.d.mts.map +1 -0
- package/dist/strategy/relay/gas-station.mjs +87 -0
- package/dist/strategy/relay/gas-station.mjs.map +1 -0
- package/dist/strategy/relay/relay-max-gas-station.cjs +242 -0
- package/dist/strategy/relay/relay-max-gas-station.cjs.map +1 -0
- package/dist/strategy/relay/relay-max-gas-station.d.cts +22 -0
- package/dist/strategy/relay/relay-max-gas-station.d.cts.map +1 -0
- package/dist/strategy/relay/relay-max-gas-station.d.mts +22 -0
- package/dist/strategy/relay/relay-max-gas-station.d.mts.map +1 -0
- package/dist/strategy/relay/relay-max-gas-station.mjs +238 -0
- package/dist/strategy/relay/relay-max-gas-station.mjs.map +1 -0
- package/dist/strategy/relay/relay-quotes.cjs +32 -59
- package/dist/strategy/relay/relay-quotes.cjs.map +1 -1
- package/dist/strategy/relay/relay-quotes.d.cts.map +1 -1
- package/dist/strategy/relay/relay-quotes.d.mts.map +1 -1
- package/dist/strategy/relay/relay-quotes.mjs +29 -56
- package/dist/strategy/relay/relay-quotes.mjs.map +1 -1
- package/dist/strategy/relay/types.cjs.map +1 -1
- package/dist/strategy/relay/types.d.cts +1 -0
- package/dist/strategy/relay/types.d.cts.map +1 -1
- package/dist/strategy/relay/types.d.mts +1 -0
- package/dist/strategy/relay/types.d.mts.map +1 -1
- package/dist/strategy/relay/types.mjs.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +24 -1
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +24 -1
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs.map +1 -1
- package/dist/utils/amounts.cjs +40 -0
- package/dist/utils/amounts.cjs.map +1 -0
- package/dist/utils/amounts.d.cts +18 -0
- package/dist/utils/amounts.d.cts.map +1 -0
- package/dist/utils/amounts.d.mts +18 -0
- package/dist/utils/amounts.d.mts.map +1 -0
- package/dist/utils/amounts.mjs +35 -0
- package/dist/utils/amounts.mjs.map +1 -0
- package/dist/utils/feature-flags.cjs +45 -6
- package/dist/utils/feature-flags.cjs.map +1 -1
- package/dist/utils/feature-flags.d.cts +45 -2
- package/dist/utils/feature-flags.d.cts.map +1 -1
- package/dist/utils/feature-flags.d.mts +45 -2
- package/dist/utils/feature-flags.d.mts.map +1 -1
- package/dist/utils/feature-flags.mjs +42 -5
- package/dist/utils/feature-flags.mjs.map +1 -1
- package/dist/utils/gas.cjs +46 -1
- package/dist/utils/gas.cjs.map +1 -1
- package/dist/utils/gas.d.cts +18 -0
- package/dist/utils/gas.d.cts.map +1 -1
- package/dist/utils/gas.d.mts +18 -0
- package/dist/utils/gas.d.mts.map +1 -1
- package/dist/utils/gas.mjs +44 -0
- package/dist/utils/gas.mjs.map +1 -1
- package/dist/utils/strategy.cjs +3 -0
- package/dist/utils/strategy.cjs.map +1 -1
- package/dist/utils/strategy.d.cts.map +1 -1
- package/dist/utils/strategy.d.mts.map +1 -1
- package/dist/utils/strategy.mjs +3 -0
- package/dist/utils/strategy.mjs.map +1 -1
- package/dist/utils/totals.cjs +4 -19
- package/dist/utils/totals.cjs.map +1 -1
- package/dist/utils/totals.d.cts.map +1 -1
- package/dist/utils/totals.d.mts.map +1 -1
- package/dist/utils/totals.mjs +1 -16
- package/dist/utils/totals.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RelayStrategy.mjs","sourceRoot":"","sources":["../../../src/strategy/relay/RelayStrategy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,2BAAuB;AAChD,OAAO,EAAE,iBAAiB,EAAE,2BAAuB;
|
|
1
|
+
{"version":3,"file":"RelayStrategy.mjs","sourceRoot":"","sources":["../../../src/strategy/relay/RelayStrategy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,2BAAuB;AAChD,OAAO,EAAE,iBAAiB,EAAE,2BAAuB;AAQnD,OAAO,EAAE,sBAAsB,EAAE,sCAAkC;AAEnE,MAAM,OAAO,aAAa;IACxB,QAAQ,CAAC,OAAoC;QAC3C,MAAM,MAAM,GAAG,sBAAsB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACzD,OAAO,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,SAAS,CACb,OAAoC;QAEpC,OAAO,cAAc,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC;IAED,KAAK,CAAC,OAAO,CACX,OAA8C;QAE9C,OAAO,MAAM,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAC1C,CAAC;CACF","sourcesContent":["import { getRelayQuotes } from './relay-quotes';\nimport { submitRelayQuotes } from './relay-submit';\nimport type { RelayQuote } from './types';\nimport type {\n PayStrategy,\n PayStrategyExecuteRequest,\n PayStrategyGetQuotesRequest,\n TransactionPayQuote,\n} from '../../types';\nimport { getPayStrategiesConfig } from '../../utils/feature-flags';\n\nexport class RelayStrategy implements PayStrategy<RelayQuote> {\n supports(request: PayStrategyGetQuotesRequest): boolean {\n const config = getPayStrategiesConfig(request.messenger);\n return config.relay.enabled;\n }\n\n async getQuotes(\n request: PayStrategyGetQuotesRequest,\n ): Promise<TransactionPayQuote<RelayQuote>[]> {\n return getRelayQuotes(request);\n }\n\n async execute(\n request: PayStrategyExecuteRequest<RelayQuote>,\n ): ReturnType<PayStrategy<RelayQuote>['execute']> {\n return await submitRelayQuotes(request);\n }\n}\n"]}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getGasStationCostInSourceTokenRaw = exports.getGasStationEligibility = void 0;
|
|
4
|
+
const controller_utils_1 = require("@metamask/controller-utils");
|
|
5
|
+
const utils_1 = require("@metamask/utils");
|
|
6
|
+
const bignumber_js_1 = require("bignumber.js");
|
|
7
|
+
const logger_1 = require("../../logger.cjs");
|
|
8
|
+
const feature_flags_1 = require("../../utils/feature-flags.cjs");
|
|
9
|
+
const gas_1 = require("../../utils/gas.cjs");
|
|
10
|
+
const log = (0, utils_1.createModuleLogger)(logger_1.projectLogger, 'relay-gas-station');
|
|
11
|
+
function getGasStationEligibility(messenger, sourceChainId) {
|
|
12
|
+
const { relayDisabledGasStationChains } = (0, feature_flags_1.getFeatureFlags)(messenger);
|
|
13
|
+
const supportedChains = (0, feature_flags_1.getEIP7702SupportedChains)(messenger);
|
|
14
|
+
const chainSupportsGasStation = supportedChains.some((supportedChainId) => supportedChainId.toLowerCase() === sourceChainId.toLowerCase());
|
|
15
|
+
const isDisabledChain = relayDisabledGasStationChains.includes(sourceChainId);
|
|
16
|
+
return {
|
|
17
|
+
chainSupportsGasStation,
|
|
18
|
+
isDisabledChain,
|
|
19
|
+
isEligible: !isDisabledChain && chainSupportsGasStation,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
exports.getGasStationEligibility = getGasStationEligibility;
|
|
23
|
+
async function getGasStationCostInSourceTokenRaw({ firstStepData, messenger, request, totalGasEstimate, totalItemCount, }) {
|
|
24
|
+
const { data, to, value } = firstStepData;
|
|
25
|
+
const { from, sourceChainId, sourceTokenAddress } = request;
|
|
26
|
+
let gasFeeTokens;
|
|
27
|
+
try {
|
|
28
|
+
gasFeeTokens = await messenger.call('TransactionController:getGasFeeTokens', {
|
|
29
|
+
chainId: sourceChainId,
|
|
30
|
+
data,
|
|
31
|
+
from,
|
|
32
|
+
to,
|
|
33
|
+
value: (0, controller_utils_1.toHex)(value ?? '0'),
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
log('Failed to estimate gas fee tokens', {
|
|
38
|
+
error,
|
|
39
|
+
sourceChainId,
|
|
40
|
+
});
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
const gasFeeToken = gasFeeTokens.find((singleGasFeeToken) => singleGasFeeToken.tokenAddress.toLowerCase() ===
|
|
44
|
+
sourceTokenAddress.toLowerCase());
|
|
45
|
+
if (!gasFeeToken) {
|
|
46
|
+
log('No matching source token in gas fee token estimate', {
|
|
47
|
+
sourceTokenAddress,
|
|
48
|
+
sourceChainId,
|
|
49
|
+
});
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
const gasFeeTokenWithNormalizedAmount = {
|
|
53
|
+
...gasFeeToken,
|
|
54
|
+
amount: (0, controller_utils_1.toHex)(getNormalizedGasFeeTokenAmount({
|
|
55
|
+
gasFeeToken,
|
|
56
|
+
totalGasEstimate,
|
|
57
|
+
totalItemCount,
|
|
58
|
+
})),
|
|
59
|
+
};
|
|
60
|
+
const gasFeeTokenCost = (0, gas_1.calculateGasFeeTokenCost)({
|
|
61
|
+
chainId: sourceChainId,
|
|
62
|
+
gasFeeToken: gasFeeTokenWithNormalizedAmount,
|
|
63
|
+
messenger,
|
|
64
|
+
});
|
|
65
|
+
if (!gasFeeTokenCost) {
|
|
66
|
+
log('Unable to calculate gas fee token cost using fiat rates', {
|
|
67
|
+
sourceTokenAddress,
|
|
68
|
+
sourceChainId,
|
|
69
|
+
});
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
log('Estimated gas station cost for source token', {
|
|
73
|
+
amount: gasFeeTokenCost.raw,
|
|
74
|
+
sourceTokenAddress,
|
|
75
|
+
sourceChainId,
|
|
76
|
+
});
|
|
77
|
+
return gasFeeTokenCost;
|
|
78
|
+
}
|
|
79
|
+
exports.getGasStationCostInSourceTokenRaw = getGasStationCostInSourceTokenRaw;
|
|
80
|
+
function getNormalizedGasFeeTokenAmount({ gasFeeToken, totalGasEstimate, totalItemCount, }) {
|
|
81
|
+
let amount = new bignumber_js_1.BigNumber(gasFeeToken.amount);
|
|
82
|
+
if (totalItemCount > 1) {
|
|
83
|
+
const gas = new bignumber_js_1.BigNumber(gasFeeToken.gas);
|
|
84
|
+
const gasFeeAmount = new bignumber_js_1.BigNumber(gasFeeToken.amount);
|
|
85
|
+
if (totalGasEstimate > 0 && gas.isGreaterThan(0)) {
|
|
86
|
+
const gasRate = gasFeeAmount.dividedBy(gas);
|
|
87
|
+
amount = gasRate.multipliedBy(totalGasEstimate);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return amount.integerValue(bignumber_js_1.BigNumber.ROUND_CEIL).toFixed(0);
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=gas-station.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gas-station.cjs","sourceRoot":"","sources":["../../../src/strategy/relay/gas-station.ts"],"names":[],"mappings":"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","sourcesContent":["import { toHex } from '@metamask/controller-utils';\nimport type { GasFeeToken } from '@metamask/transaction-controller';\nimport type { Hex } from '@metamask/utils';\nimport { createModuleLogger } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport { projectLogger } from '../../logger';\nimport type {\n Amount,\n QuoteRequest,\n TransactionPayControllerMessenger,\n} from '../../types';\nimport {\n getEIP7702SupportedChains,\n getFeatureFlags,\n} from '../../utils/feature-flags';\nimport { calculateGasFeeTokenCost } from '../../utils/gas';\n\nconst log = createModuleLogger(projectLogger, 'relay-gas-station');\n\ntype GasStationCostParams = {\n firstStepData: {\n data: Hex;\n to: Hex;\n value?: string;\n };\n messenger: TransactionPayControllerMessenger;\n request: Pick<QuoteRequest, 'from' | 'sourceChainId' | 'sourceTokenAddress'>;\n totalGasEstimate: number;\n totalItemCount: number;\n};\n\nexport type GasStationEligibility = {\n chainSupportsGasStation: boolean;\n isDisabledChain: boolean;\n isEligible: boolean;\n};\n\nexport function getGasStationEligibility(\n messenger: TransactionPayControllerMessenger,\n sourceChainId: QuoteRequest['sourceChainId'],\n): GasStationEligibility {\n const { relayDisabledGasStationChains } = getFeatureFlags(messenger);\n const supportedChains = getEIP7702SupportedChains(messenger);\n const chainSupportsGasStation = supportedChains.some(\n (supportedChainId) =>\n supportedChainId.toLowerCase() === sourceChainId.toLowerCase(),\n );\n\n const isDisabledChain = relayDisabledGasStationChains.includes(sourceChainId);\n\n return {\n chainSupportsGasStation,\n isDisabledChain,\n isEligible: !isDisabledChain && chainSupportsGasStation,\n };\n}\n\nexport async function getGasStationCostInSourceTokenRaw({\n firstStepData,\n messenger,\n request,\n totalGasEstimate,\n totalItemCount,\n}: GasStationCostParams): Promise<Amount | undefined> {\n const { data, to, value } = firstStepData;\n const { from, sourceChainId, sourceTokenAddress } = request;\n\n let gasFeeTokens: GasFeeToken[];\n\n try {\n gasFeeTokens = await messenger.call(\n 'TransactionController:getGasFeeTokens',\n {\n chainId: sourceChainId,\n data,\n from,\n to,\n value: toHex(value ?? '0'),\n },\n );\n } catch (error) {\n log('Failed to estimate gas fee tokens', {\n error,\n sourceChainId,\n });\n return undefined;\n }\n\n const gasFeeToken = gasFeeTokens.find(\n (singleGasFeeToken) =>\n singleGasFeeToken.tokenAddress.toLowerCase() ===\n sourceTokenAddress.toLowerCase(),\n );\n\n if (!gasFeeToken) {\n log('No matching source token in gas fee token estimate', {\n sourceTokenAddress,\n sourceChainId,\n });\n return undefined;\n }\n\n const gasFeeTokenWithNormalizedAmount = {\n ...gasFeeToken,\n amount: toHex(\n getNormalizedGasFeeTokenAmount({\n gasFeeToken,\n totalGasEstimate,\n totalItemCount,\n }),\n ),\n };\n\n const gasFeeTokenCost = calculateGasFeeTokenCost({\n chainId: sourceChainId,\n gasFeeToken: gasFeeTokenWithNormalizedAmount,\n messenger,\n });\n\n if (!gasFeeTokenCost) {\n log('Unable to calculate gas fee token cost using fiat rates', {\n sourceTokenAddress,\n sourceChainId,\n });\n return undefined;\n }\n\n log('Estimated gas station cost for source token', {\n amount: gasFeeTokenCost.raw,\n sourceTokenAddress,\n sourceChainId,\n });\n\n return gasFeeTokenCost;\n}\n\nfunction getNormalizedGasFeeTokenAmount({\n gasFeeToken,\n totalGasEstimate,\n totalItemCount,\n}: {\n gasFeeToken: GasFeeToken;\n totalGasEstimate: number;\n totalItemCount: number;\n}): string {\n let amount = new BigNumber(gasFeeToken.amount);\n\n if (totalItemCount > 1) {\n const gas = new BigNumber(gasFeeToken.gas);\n const gasFeeAmount = new BigNumber(gasFeeToken.amount);\n\n if (totalGasEstimate > 0 && gas.isGreaterThan(0)) {\n const gasRate = gasFeeAmount.dividedBy(gas);\n amount = gasRate.multipliedBy(totalGasEstimate);\n }\n }\n\n return amount.integerValue(BigNumber.ROUND_CEIL).toFixed(0);\n}\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Hex } from "@metamask/utils";
|
|
2
|
+
import type { Amount, QuoteRequest, TransactionPayControllerMessenger } from "../../types.cjs";
|
|
3
|
+
type GasStationCostParams = {
|
|
4
|
+
firstStepData: {
|
|
5
|
+
data: Hex;
|
|
6
|
+
to: Hex;
|
|
7
|
+
value?: string;
|
|
8
|
+
};
|
|
9
|
+
messenger: TransactionPayControllerMessenger;
|
|
10
|
+
request: Pick<QuoteRequest, 'from' | 'sourceChainId' | 'sourceTokenAddress'>;
|
|
11
|
+
totalGasEstimate: number;
|
|
12
|
+
totalItemCount: number;
|
|
13
|
+
};
|
|
14
|
+
export type GasStationEligibility = {
|
|
15
|
+
chainSupportsGasStation: boolean;
|
|
16
|
+
isDisabledChain: boolean;
|
|
17
|
+
isEligible: boolean;
|
|
18
|
+
};
|
|
19
|
+
export declare function getGasStationEligibility(messenger: TransactionPayControllerMessenger, sourceChainId: QuoteRequest['sourceChainId']): GasStationEligibility;
|
|
20
|
+
export declare function getGasStationCostInSourceTokenRaw({ firstStepData, messenger, request, totalGasEstimate, totalItemCount, }: GasStationCostParams): Promise<Amount | undefined>;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=gas-station.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gas-station.d.cts","sourceRoot":"","sources":["../../../src/strategy/relay/gas-station.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAK3C,OAAO,KAAK,EACV,MAAM,EACN,YAAY,EACZ,iCAAiC,EAClC,wBAAoB;AASrB,KAAK,oBAAoB,GAAG;IAC1B,aAAa,EAAE;QACb,IAAI,EAAE,GAAG,CAAC;QACV,EAAE,EAAE,GAAG,CAAC;QACR,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,SAAS,EAAE,iCAAiC,CAAC;IAC7C,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,eAAe,GAAG,oBAAoB,CAAC,CAAC;IAC7E,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,uBAAuB,EAAE,OAAO,CAAC;IACjC,eAAe,EAAE,OAAO,CAAC;IACzB,UAAU,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,iCAAiC,EAC5C,aAAa,EAAE,YAAY,CAAC,eAAe,CAAC,GAC3C,qBAAqB,CAevB;AAED,wBAAsB,iCAAiC,CAAC,EACtD,aAAa,EACb,SAAS,EACT,OAAO,EACP,gBAAgB,EAChB,cAAc,GACf,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAuEpD"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Hex } from "@metamask/utils";
|
|
2
|
+
import type { Amount, QuoteRequest, TransactionPayControllerMessenger } from "../../types.mjs";
|
|
3
|
+
type GasStationCostParams = {
|
|
4
|
+
firstStepData: {
|
|
5
|
+
data: Hex;
|
|
6
|
+
to: Hex;
|
|
7
|
+
value?: string;
|
|
8
|
+
};
|
|
9
|
+
messenger: TransactionPayControllerMessenger;
|
|
10
|
+
request: Pick<QuoteRequest, 'from' | 'sourceChainId' | 'sourceTokenAddress'>;
|
|
11
|
+
totalGasEstimate: number;
|
|
12
|
+
totalItemCount: number;
|
|
13
|
+
};
|
|
14
|
+
export type GasStationEligibility = {
|
|
15
|
+
chainSupportsGasStation: boolean;
|
|
16
|
+
isDisabledChain: boolean;
|
|
17
|
+
isEligible: boolean;
|
|
18
|
+
};
|
|
19
|
+
export declare function getGasStationEligibility(messenger: TransactionPayControllerMessenger, sourceChainId: QuoteRequest['sourceChainId']): GasStationEligibility;
|
|
20
|
+
export declare function getGasStationCostInSourceTokenRaw({ firstStepData, messenger, request, totalGasEstimate, totalItemCount, }: GasStationCostParams): Promise<Amount | undefined>;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=gas-station.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gas-station.d.mts","sourceRoot":"","sources":["../../../src/strategy/relay/gas-station.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAK3C,OAAO,KAAK,EACV,MAAM,EACN,YAAY,EACZ,iCAAiC,EAClC,wBAAoB;AASrB,KAAK,oBAAoB,GAAG;IAC1B,aAAa,EAAE;QACb,IAAI,EAAE,GAAG,CAAC;QACV,EAAE,EAAE,GAAG,CAAC;QACR,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,SAAS,EAAE,iCAAiC,CAAC;IAC7C,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,eAAe,GAAG,oBAAoB,CAAC,CAAC;IAC7E,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,uBAAuB,EAAE,OAAO,CAAC;IACjC,eAAe,EAAE,OAAO,CAAC;IACzB,UAAU,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,iCAAiC,EAC5C,aAAa,EAAE,YAAY,CAAC,eAAe,CAAC,GAC3C,qBAAqB,CAevB;AAED,wBAAsB,iCAAiC,CAAC,EACtD,aAAa,EACb,SAAS,EACT,OAAO,EACP,gBAAgB,EAChB,cAAc,GACf,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAuEpD"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { toHex } from "@metamask/controller-utils";
|
|
2
|
+
import { createModuleLogger } from "@metamask/utils";
|
|
3
|
+
import { BigNumber } from "bignumber.js";
|
|
4
|
+
import { projectLogger } from "../../logger.mjs";
|
|
5
|
+
import { getEIP7702SupportedChains, getFeatureFlags } from "../../utils/feature-flags.mjs";
|
|
6
|
+
import { calculateGasFeeTokenCost } from "../../utils/gas.mjs";
|
|
7
|
+
const log = createModuleLogger(projectLogger, 'relay-gas-station');
|
|
8
|
+
export function getGasStationEligibility(messenger, sourceChainId) {
|
|
9
|
+
const { relayDisabledGasStationChains } = getFeatureFlags(messenger);
|
|
10
|
+
const supportedChains = getEIP7702SupportedChains(messenger);
|
|
11
|
+
const chainSupportsGasStation = supportedChains.some((supportedChainId) => supportedChainId.toLowerCase() === sourceChainId.toLowerCase());
|
|
12
|
+
const isDisabledChain = relayDisabledGasStationChains.includes(sourceChainId);
|
|
13
|
+
return {
|
|
14
|
+
chainSupportsGasStation,
|
|
15
|
+
isDisabledChain,
|
|
16
|
+
isEligible: !isDisabledChain && chainSupportsGasStation,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export async function getGasStationCostInSourceTokenRaw({ firstStepData, messenger, request, totalGasEstimate, totalItemCount, }) {
|
|
20
|
+
const { data, to, value } = firstStepData;
|
|
21
|
+
const { from, sourceChainId, sourceTokenAddress } = request;
|
|
22
|
+
let gasFeeTokens;
|
|
23
|
+
try {
|
|
24
|
+
gasFeeTokens = await messenger.call('TransactionController:getGasFeeTokens', {
|
|
25
|
+
chainId: sourceChainId,
|
|
26
|
+
data,
|
|
27
|
+
from,
|
|
28
|
+
to,
|
|
29
|
+
value: toHex(value ?? '0'),
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
log('Failed to estimate gas fee tokens', {
|
|
34
|
+
error,
|
|
35
|
+
sourceChainId,
|
|
36
|
+
});
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
const gasFeeToken = gasFeeTokens.find((singleGasFeeToken) => singleGasFeeToken.tokenAddress.toLowerCase() ===
|
|
40
|
+
sourceTokenAddress.toLowerCase());
|
|
41
|
+
if (!gasFeeToken) {
|
|
42
|
+
log('No matching source token in gas fee token estimate', {
|
|
43
|
+
sourceTokenAddress,
|
|
44
|
+
sourceChainId,
|
|
45
|
+
});
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
const gasFeeTokenWithNormalizedAmount = {
|
|
49
|
+
...gasFeeToken,
|
|
50
|
+
amount: toHex(getNormalizedGasFeeTokenAmount({
|
|
51
|
+
gasFeeToken,
|
|
52
|
+
totalGasEstimate,
|
|
53
|
+
totalItemCount,
|
|
54
|
+
})),
|
|
55
|
+
};
|
|
56
|
+
const gasFeeTokenCost = calculateGasFeeTokenCost({
|
|
57
|
+
chainId: sourceChainId,
|
|
58
|
+
gasFeeToken: gasFeeTokenWithNormalizedAmount,
|
|
59
|
+
messenger,
|
|
60
|
+
});
|
|
61
|
+
if (!gasFeeTokenCost) {
|
|
62
|
+
log('Unable to calculate gas fee token cost using fiat rates', {
|
|
63
|
+
sourceTokenAddress,
|
|
64
|
+
sourceChainId,
|
|
65
|
+
});
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
log('Estimated gas station cost for source token', {
|
|
69
|
+
amount: gasFeeTokenCost.raw,
|
|
70
|
+
sourceTokenAddress,
|
|
71
|
+
sourceChainId,
|
|
72
|
+
});
|
|
73
|
+
return gasFeeTokenCost;
|
|
74
|
+
}
|
|
75
|
+
function getNormalizedGasFeeTokenAmount({ gasFeeToken, totalGasEstimate, totalItemCount, }) {
|
|
76
|
+
let amount = new BigNumber(gasFeeToken.amount);
|
|
77
|
+
if (totalItemCount > 1) {
|
|
78
|
+
const gas = new BigNumber(gasFeeToken.gas);
|
|
79
|
+
const gasFeeAmount = new BigNumber(gasFeeToken.amount);
|
|
80
|
+
if (totalGasEstimate > 0 && gas.isGreaterThan(0)) {
|
|
81
|
+
const gasRate = gasFeeAmount.dividedBy(gas);
|
|
82
|
+
amount = gasRate.multipliedBy(totalGasEstimate);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return amount.integerValue(BigNumber.ROUND_CEIL).toFixed(0);
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=gas-station.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gas-station.mjs","sourceRoot":"","sources":["../../../src/strategy/relay/gas-station.ts"],"names":[],"mappings":"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","sourcesContent":["import { toHex } from '@metamask/controller-utils';\nimport type { GasFeeToken } from '@metamask/transaction-controller';\nimport type { Hex } from '@metamask/utils';\nimport { createModuleLogger } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport { projectLogger } from '../../logger';\nimport type {\n Amount,\n QuoteRequest,\n TransactionPayControllerMessenger,\n} from '../../types';\nimport {\n getEIP7702SupportedChains,\n getFeatureFlags,\n} from '../../utils/feature-flags';\nimport { calculateGasFeeTokenCost } from '../../utils/gas';\n\nconst log = createModuleLogger(projectLogger, 'relay-gas-station');\n\ntype GasStationCostParams = {\n firstStepData: {\n data: Hex;\n to: Hex;\n value?: string;\n };\n messenger: TransactionPayControllerMessenger;\n request: Pick<QuoteRequest, 'from' | 'sourceChainId' | 'sourceTokenAddress'>;\n totalGasEstimate: number;\n totalItemCount: number;\n};\n\nexport type GasStationEligibility = {\n chainSupportsGasStation: boolean;\n isDisabledChain: boolean;\n isEligible: boolean;\n};\n\nexport function getGasStationEligibility(\n messenger: TransactionPayControllerMessenger,\n sourceChainId: QuoteRequest['sourceChainId'],\n): GasStationEligibility {\n const { relayDisabledGasStationChains } = getFeatureFlags(messenger);\n const supportedChains = getEIP7702SupportedChains(messenger);\n const chainSupportsGasStation = supportedChains.some(\n (supportedChainId) =>\n supportedChainId.toLowerCase() === sourceChainId.toLowerCase(),\n );\n\n const isDisabledChain = relayDisabledGasStationChains.includes(sourceChainId);\n\n return {\n chainSupportsGasStation,\n isDisabledChain,\n isEligible: !isDisabledChain && chainSupportsGasStation,\n };\n}\n\nexport async function getGasStationCostInSourceTokenRaw({\n firstStepData,\n messenger,\n request,\n totalGasEstimate,\n totalItemCount,\n}: GasStationCostParams): Promise<Amount | undefined> {\n const { data, to, value } = firstStepData;\n const { from, sourceChainId, sourceTokenAddress } = request;\n\n let gasFeeTokens: GasFeeToken[];\n\n try {\n gasFeeTokens = await messenger.call(\n 'TransactionController:getGasFeeTokens',\n {\n chainId: sourceChainId,\n data,\n from,\n to,\n value: toHex(value ?? '0'),\n },\n );\n } catch (error) {\n log('Failed to estimate gas fee tokens', {\n error,\n sourceChainId,\n });\n return undefined;\n }\n\n const gasFeeToken = gasFeeTokens.find(\n (singleGasFeeToken) =>\n singleGasFeeToken.tokenAddress.toLowerCase() ===\n sourceTokenAddress.toLowerCase(),\n );\n\n if (!gasFeeToken) {\n log('No matching source token in gas fee token estimate', {\n sourceTokenAddress,\n sourceChainId,\n });\n return undefined;\n }\n\n const gasFeeTokenWithNormalizedAmount = {\n ...gasFeeToken,\n amount: toHex(\n getNormalizedGasFeeTokenAmount({\n gasFeeToken,\n totalGasEstimate,\n totalItemCount,\n }),\n ),\n };\n\n const gasFeeTokenCost = calculateGasFeeTokenCost({\n chainId: sourceChainId,\n gasFeeToken: gasFeeTokenWithNormalizedAmount,\n messenger,\n });\n\n if (!gasFeeTokenCost) {\n log('Unable to calculate gas fee token cost using fiat rates', {\n sourceTokenAddress,\n sourceChainId,\n });\n return undefined;\n }\n\n log('Estimated gas station cost for source token', {\n amount: gasFeeTokenCost.raw,\n sourceTokenAddress,\n sourceChainId,\n });\n\n return gasFeeTokenCost;\n}\n\nfunction getNormalizedGasFeeTokenAmount({\n gasFeeToken,\n totalGasEstimate,\n totalItemCount,\n}: {\n gasFeeToken: GasFeeToken;\n totalGasEstimate: number;\n totalItemCount: number;\n}): string {\n let amount = new BigNumber(gasFeeToken.amount);\n\n if (totalItemCount > 1) {\n const gas = new BigNumber(gasFeeToken.gas);\n const gasFeeAmount = new BigNumber(gasFeeToken.amount);\n\n if (totalGasEstimate > 0 && gas.isGreaterThan(0)) {\n const gasRate = gasFeeAmount.dividedBy(gas);\n amount = gasRate.multipliedBy(totalGasEstimate);\n }\n }\n\n return amount.integerValue(BigNumber.ROUND_CEIL).toFixed(0);\n}\n"]}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getRelayMaxGasStationQuote = void 0;
|
|
4
|
+
const utils_1 = require("@metamask/utils");
|
|
5
|
+
const bignumber_js_1 = require("bignumber.js");
|
|
6
|
+
const gas_station_1 = require("./gas-station.cjs");
|
|
7
|
+
const logger_1 = require("../../logger.cjs");
|
|
8
|
+
const token_1 = require("../../utils/token.cjs");
|
|
9
|
+
const log = (0, utils_1.createModuleLogger)(logger_1.projectLogger, 'relay-max-gas-station');
|
|
10
|
+
const PROBE_AMOUNT_PERCENTAGE = 0.25;
|
|
11
|
+
var GasCostEstimateSource;
|
|
12
|
+
(function (GasCostEstimateSource) {
|
|
13
|
+
GasCostEstimateSource["GasStation"] = "gas-station";
|
|
14
|
+
GasCostEstimateSource["Probe"] = "probe";
|
|
15
|
+
GasCostEstimateSource["Quote"] = "quote";
|
|
16
|
+
})(GasCostEstimateSource || (GasCostEstimateSource = {}));
|
|
17
|
+
/**
|
|
18
|
+
* Returns a Relay max-amount quote using a two-phase gas-station fallback.
|
|
19
|
+
*
|
|
20
|
+
* It first requests a standard max quote (phase 1), then when needed estimates
|
|
21
|
+
* gas in source-token units (directly or via a probe quote), requests an
|
|
22
|
+
* adjusted max quote (phase 2), and accepts phase 2 only if validation passes
|
|
23
|
+
* (source gas fee token selected, gas-limit checks, affordability).
|
|
24
|
+
*
|
|
25
|
+
* If any step fails or validation is unsafe, it safely falls back to phase 1.
|
|
26
|
+
* Successful phase-2 quotes are tagged with `metamask.isMaxGasStation = true`.
|
|
27
|
+
*
|
|
28
|
+
* @param request - Relay quote request for a max-amount flow.
|
|
29
|
+
* @param fullRequest - Full quote request context including messenger and transaction.
|
|
30
|
+
* @param getSingleQuote - Quote fetcher used for phase-1, phase-2, and probe quotes.
|
|
31
|
+
* @returns The validated adjusted phase-2 quote, or the original phase-1 quote on fallback.
|
|
32
|
+
*/
|
|
33
|
+
async function getRelayMaxGasStationQuote(request, fullRequest, getSingleQuote) {
|
|
34
|
+
const { messenger } = fullRequest;
|
|
35
|
+
const { sourceChainId, sourceTokenAmount } = request;
|
|
36
|
+
const context = {
|
|
37
|
+
fullRequest,
|
|
38
|
+
getSingleQuote,
|
|
39
|
+
messenger,
|
|
40
|
+
request,
|
|
41
|
+
};
|
|
42
|
+
const phase1Quote = await getSingleQuote(request, fullRequest);
|
|
43
|
+
const nativeBalanceCheck = checkEnoughNativeBalanceIfSourceGasFeeTokenNotUsed(phase1Quote, messenger, request);
|
|
44
|
+
if (nativeBalanceCheck.hasEnoughNativeBalance) {
|
|
45
|
+
return fallbackToPhase1(phase1Quote, 'Native balance is sufficient for gas', {
|
|
46
|
+
nativeBalance: nativeBalanceCheck.nativeBalance,
|
|
47
|
+
nativeGasCost: nativeBalanceCheck.nativeGasCostRaw,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
const gasStationEligibility = (0, gas_station_1.getGasStationEligibility)(messenger, sourceChainId);
|
|
51
|
+
if (!gasStationEligibility.isEligible) {
|
|
52
|
+
return fallbackToPhase1(phase1Quote, 'Gas station is disabled or unsupported');
|
|
53
|
+
}
|
|
54
|
+
const sourceAmountBN = new bignumber_js_1.BigNumber(sourceTokenAmount);
|
|
55
|
+
const probeCost = await getProbeGasCostInSourceTokenRaw(context);
|
|
56
|
+
if (!probeCost) {
|
|
57
|
+
return fallbackToPhase1(phase1Quote, 'Unable to estimate gas-station source token cost');
|
|
58
|
+
}
|
|
59
|
+
const initialGasEstimate = {
|
|
60
|
+
amount: probeCost,
|
|
61
|
+
source: GasCostEstimateSource.Probe,
|
|
62
|
+
};
|
|
63
|
+
const adjustedSourceAmount = getAdjustedSourceAmount(sourceAmountBN, initialGasEstimate.amount);
|
|
64
|
+
if (!adjustedSourceAmount.isGreaterThan(0)) {
|
|
65
|
+
return fallbackToPhase1(phase1Quote, 'Insufficient balance for gas station after max adjustment');
|
|
66
|
+
}
|
|
67
|
+
const phase2Quote = await getAdjustedPhase2Quote(adjustedSourceAmount, initialGasEstimate, context);
|
|
68
|
+
if (!phase2Quote) {
|
|
69
|
+
return fallbackToPhase1(phase1Quote, 'Adjusted phase-2 quote request failed');
|
|
70
|
+
}
|
|
71
|
+
if (!phase2Quote.fees.isSourceGasFeeToken) {
|
|
72
|
+
return fallbackToPhase1(phase1Quote, 'Adjusted quote did not return source gas fee token pricing');
|
|
73
|
+
}
|
|
74
|
+
const phase1GasLimits = phase1Quote.original.metamask.gasLimits ?? [];
|
|
75
|
+
const phase2GasLimits = phase2Quote.original.metamask.gasLimits ?? [];
|
|
76
|
+
if (!hasMatchingGasLimitShape(phase1GasLimits, phase2GasLimits)) {
|
|
77
|
+
return fallbackToPhase1(phase1Quote, 'Adjusted quote gas limit shape changed between phases', {
|
|
78
|
+
phase1GasLimits,
|
|
79
|
+
phase2GasLimits,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
const phase1TotalGasLimit = getTotalGasLimit(phase1GasLimits);
|
|
83
|
+
const phase2TotalGasLimit = getTotalGasLimit(phase2GasLimits);
|
|
84
|
+
if (phase2TotalGasLimit > phase1TotalGasLimit) {
|
|
85
|
+
return fallbackToPhase1(phase1Quote, 'Adjusted quote total gas limit increased between phases', {
|
|
86
|
+
phase1TotalGasLimit,
|
|
87
|
+
phase2TotalGasLimit,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
const validationGasEstimate = new bignumber_js_1.BigNumber(phase2Quote.fees.sourceNetwork.max.raw);
|
|
91
|
+
if (!isAdjustedAmountAffordable(adjustedSourceAmount, validationGasEstimate, sourceAmountBN)) {
|
|
92
|
+
return fallbackToPhase1(phase1Quote, 'Adjusted quote fails affordability validation', {
|
|
93
|
+
adjustedSourceAmount: adjustedSourceAmount.toString(10),
|
|
94
|
+
validationGasCost: validationGasEstimate.toString(10),
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
markQuoteAsMaxGasStation(phase2Quote);
|
|
98
|
+
return phase2Quote;
|
|
99
|
+
}
|
|
100
|
+
exports.getRelayMaxGasStationQuote = getRelayMaxGasStationQuote;
|
|
101
|
+
function checkEnoughNativeBalanceIfSourceGasFeeTokenNotUsed(quote, messenger, request) {
|
|
102
|
+
if (quote.fees.isSourceGasFeeToken) {
|
|
103
|
+
return { hasEnoughNativeBalance: false };
|
|
104
|
+
}
|
|
105
|
+
const nativeGasCostRaw = quote.fees.sourceNetwork.max.raw;
|
|
106
|
+
const nativeBalance = (0, token_1.getTokenBalance)(messenger, request.from, request.sourceChainId, (0, token_1.getNativeToken)(request.sourceChainId));
|
|
107
|
+
return {
|
|
108
|
+
hasEnoughNativeBalance: new bignumber_js_1.BigNumber(nativeBalance).isGreaterThanOrEqualTo(nativeGasCostRaw),
|
|
109
|
+
nativeBalance,
|
|
110
|
+
nativeGasCostRaw,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function getAdjustedSourceAmount(sourceAmount, estimatedGasCost) {
|
|
114
|
+
return sourceAmount
|
|
115
|
+
.minus(estimatedGasCost)
|
|
116
|
+
.integerValue(bignumber_js_1.BigNumber.ROUND_DOWN);
|
|
117
|
+
}
|
|
118
|
+
function isAdjustedAmountAffordable(adjustedSourceAmount, gasCost, originalSourceAmount) {
|
|
119
|
+
return adjustedSourceAmount
|
|
120
|
+
.plus(gasCost)
|
|
121
|
+
.isLessThanOrEqualTo(originalSourceAmount);
|
|
122
|
+
}
|
|
123
|
+
async function getAdjustedPhase2Quote(adjustedSourceAmount, initialGasEstimate, context) {
|
|
124
|
+
const { fullRequest, getSingleQuote, request } = context;
|
|
125
|
+
log('Requesting adjusted max quote', {
|
|
126
|
+
adjustedAmount: adjustedSourceAmount.toString(10),
|
|
127
|
+
gasCostInSourceToken: initialGasEstimate.amount.toString(10),
|
|
128
|
+
gasEstimateSource: initialGasEstimate.source,
|
|
129
|
+
originalAmount: request.sourceTokenAmount,
|
|
130
|
+
});
|
|
131
|
+
try {
|
|
132
|
+
return await getSingleQuote({
|
|
133
|
+
...request,
|
|
134
|
+
sourceTokenAmount: adjustedSourceAmount.toFixed(0, bignumber_js_1.BigNumber.ROUND_DOWN),
|
|
135
|
+
}, fullRequest);
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
log('Adjusted quote request failed, falling back to phase-1 quote', {
|
|
139
|
+
error,
|
|
140
|
+
});
|
|
141
|
+
return undefined;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async function getGasCostFromQuoteOrGasStation(quote, messenger, request) {
|
|
145
|
+
const gasCost = quote.fees.sourceNetwork.max;
|
|
146
|
+
if (quote.fees.isSourceGasFeeToken) {
|
|
147
|
+
log('Gas cost already in source token units', { raw: gasCost.raw });
|
|
148
|
+
return {
|
|
149
|
+
amount: new bignumber_js_1.BigNumber(gasCost.raw),
|
|
150
|
+
source: GasCostEstimateSource.Quote,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
const firstStepData = quote.original.steps[0]?.items[0]?.data;
|
|
154
|
+
if (!firstStepData) {
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
const totalItemCount = quote.original.steps.reduce((count, step) => count + step.items.length, 0);
|
|
158
|
+
const totalGasEstimate = (quote.original.metamask.gasLimits ?? []).reduce((acc, gasLimit) => acc + gasLimit, 0);
|
|
159
|
+
const gasStationCost = await (0, gas_station_1.getGasStationCostInSourceTokenRaw)({
|
|
160
|
+
firstStepData,
|
|
161
|
+
messenger,
|
|
162
|
+
request: {
|
|
163
|
+
from: request.from,
|
|
164
|
+
sourceChainId: request.sourceChainId,
|
|
165
|
+
sourceTokenAddress: request.sourceTokenAddress,
|
|
166
|
+
},
|
|
167
|
+
totalGasEstimate,
|
|
168
|
+
totalItemCount,
|
|
169
|
+
});
|
|
170
|
+
if (!gasStationCost) {
|
|
171
|
+
return undefined;
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
amount: new bignumber_js_1.BigNumber(gasStationCost.raw),
|
|
175
|
+
source: GasCostEstimateSource.GasStation,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
async function getProbeGasCostInSourceTokenRaw(context) {
|
|
179
|
+
const { fullRequest, getSingleQuote, messenger, request } = context;
|
|
180
|
+
const sourceTokenInfo = (0, token_1.getTokenInfo)(messenger, request.sourceTokenAddress, request.sourceChainId);
|
|
181
|
+
if (!sourceTokenInfo) {
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
const probeAmount = getProbeSourceAmountRaw(request.sourceTokenAmount, sourceTokenInfo.decimals);
|
|
185
|
+
if (!probeAmount || probeAmount === request.sourceTokenAmount) {
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
log('Requesting probe quote for gas station estimation', {
|
|
189
|
+
originalSourceAmount: request.sourceTokenAmount,
|
|
190
|
+
probeAmount,
|
|
191
|
+
});
|
|
192
|
+
let probeQuote;
|
|
193
|
+
try {
|
|
194
|
+
probeQuote = await getSingleQuote({
|
|
195
|
+
...request,
|
|
196
|
+
sourceTokenAmount: probeAmount,
|
|
197
|
+
}, fullRequest);
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
log('Probe quote request failed', { error });
|
|
201
|
+
return undefined;
|
|
202
|
+
}
|
|
203
|
+
if (!probeQuote) {
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
206
|
+
const probeEstimate = await getGasCostFromQuoteOrGasStation(probeQuote, messenger, request);
|
|
207
|
+
return probeEstimate?.amount;
|
|
208
|
+
}
|
|
209
|
+
function getProbeSourceAmountRaw(sourceAmountRaw, sourceDecimals) {
|
|
210
|
+
const sourceAmount = new bignumber_js_1.BigNumber(sourceAmountRaw);
|
|
211
|
+
if (sourceAmount.isLessThanOrEqualTo(0)) {
|
|
212
|
+
return undefined;
|
|
213
|
+
}
|
|
214
|
+
const probeRawAmount = sourceAmount
|
|
215
|
+
.multipliedBy(PROBE_AMOUNT_PERCENTAGE)
|
|
216
|
+
.integerValue(bignumber_js_1.BigNumber.ROUND_FLOOR);
|
|
217
|
+
// Minimum probe size: ~0.01 token for tokens with >=2 decimals,
|
|
218
|
+
// otherwise one raw unit for low-decimal tokens.
|
|
219
|
+
const minimumProbeRaw = new bignumber_js_1.BigNumber(1).shiftedBy(Math.max(sourceDecimals - 2, 0));
|
|
220
|
+
const probeRaw = bignumber_js_1.BigNumber.minimum(sourceAmount, bignumber_js_1.BigNumber.maximum(probeRawAmount, minimumProbeRaw)).integerValue(bignumber_js_1.BigNumber.ROUND_FLOOR);
|
|
221
|
+
if (probeRaw.isLessThanOrEqualTo(0)) {
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
return probeRaw.toFixed(0);
|
|
225
|
+
}
|
|
226
|
+
function fallbackToPhase1(phase1Quote, message, paramsToLog) {
|
|
227
|
+
log(message, paramsToLog);
|
|
228
|
+
return phase1Quote;
|
|
229
|
+
}
|
|
230
|
+
function hasMatchingGasLimitShape(phase1, phase2) {
|
|
231
|
+
return phase1.length === phase2.length;
|
|
232
|
+
}
|
|
233
|
+
function getTotalGasLimit(gasLimits) {
|
|
234
|
+
return gasLimits.reduce((total, gasLimit) => total + gasLimit, 0);
|
|
235
|
+
}
|
|
236
|
+
function markQuoteAsMaxGasStation(quote) {
|
|
237
|
+
quote.original.metamask = {
|
|
238
|
+
...quote.original.metamask,
|
|
239
|
+
isMaxGasStation: true,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
//# sourceMappingURL=relay-max-gas-station.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relay-max-gas-station.cjs","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 {\n getGasStationEligibility,\n getGasStationCostInSourceTokenRaw,\n} from './gas-station';\nimport type { RelayQuote } from './types';\nimport { projectLogger } from '../../logger';\nimport type {\n PayStrategyGetQuotesRequest,\n QuoteRequest,\n TransactionPayControllerMessenger,\n TransactionPayQuote,\n} from '../../types';\nimport {\n getNativeToken,\n getTokenBalance,\n getTokenInfo,\n} from '../../utils/token';\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 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 firstStepData = quote.original.steps[0]?.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"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { RelayQuote } from "./types.cjs";
|
|
2
|
+
import type { PayStrategyGetQuotesRequest, QuoteRequest, TransactionPayQuote } from "../../types.cjs";
|
|
3
|
+
type GetSingleQuoteFn = (request: QuoteRequest, fullRequest: PayStrategyGetQuotesRequest) => Promise<TransactionPayQuote<RelayQuote>>;
|
|
4
|
+
/**
|
|
5
|
+
* Returns a Relay max-amount quote using a two-phase gas-station fallback.
|
|
6
|
+
*
|
|
7
|
+
* It first requests a standard max quote (phase 1), then when needed estimates
|
|
8
|
+
* gas in source-token units (directly or via a probe quote), requests an
|
|
9
|
+
* adjusted max quote (phase 2), and accepts phase 2 only if validation passes
|
|
10
|
+
* (source gas fee token selected, gas-limit checks, affordability).
|
|
11
|
+
*
|
|
12
|
+
* If any step fails or validation is unsafe, it safely falls back to phase 1.
|
|
13
|
+
* Successful phase-2 quotes are tagged with `metamask.isMaxGasStation = true`.
|
|
14
|
+
*
|
|
15
|
+
* @param request - Relay quote request for a max-amount flow.
|
|
16
|
+
* @param fullRequest - Full quote request context including messenger and transaction.
|
|
17
|
+
* @param getSingleQuote - Quote fetcher used for phase-1, phase-2, and probe quotes.
|
|
18
|
+
* @returns The validated adjusted phase-2 quote, or the original phase-1 quote on fallback.
|
|
19
|
+
*/
|
|
20
|
+
export declare function getRelayMaxGasStationQuote(request: QuoteRequest, fullRequest: PayStrategyGetQuotesRequest, getSingleQuote: GetSingleQuoteFn): Promise<TransactionPayQuote<RelayQuote>>;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=relay-max-gas-station.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relay-max-gas-station.d.cts","sourceRoot":"","sources":["../../../src/strategy/relay/relay-max-gas-station.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,oBAAgB;AAE1C,OAAO,KAAK,EACV,2BAA2B,EAC3B,YAAY,EAEZ,mBAAmB,EACpB,wBAAoB;AA4BrB,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,CA4I1C"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { RelayQuote } from "./types.mjs";
|
|
2
|
+
import type { PayStrategyGetQuotesRequest, QuoteRequest, TransactionPayQuote } from "../../types.mjs";
|
|
3
|
+
type GetSingleQuoteFn = (request: QuoteRequest, fullRequest: PayStrategyGetQuotesRequest) => Promise<TransactionPayQuote<RelayQuote>>;
|
|
4
|
+
/**
|
|
5
|
+
* Returns a Relay max-amount quote using a two-phase gas-station fallback.
|
|
6
|
+
*
|
|
7
|
+
* It first requests a standard max quote (phase 1), then when needed estimates
|
|
8
|
+
* gas in source-token units (directly or via a probe quote), requests an
|
|
9
|
+
* adjusted max quote (phase 2), and accepts phase 2 only if validation passes
|
|
10
|
+
* (source gas fee token selected, gas-limit checks, affordability).
|
|
11
|
+
*
|
|
12
|
+
* If any step fails or validation is unsafe, it safely falls back to phase 1.
|
|
13
|
+
* Successful phase-2 quotes are tagged with `metamask.isMaxGasStation = true`.
|
|
14
|
+
*
|
|
15
|
+
* @param request - Relay quote request for a max-amount flow.
|
|
16
|
+
* @param fullRequest - Full quote request context including messenger and transaction.
|
|
17
|
+
* @param getSingleQuote - Quote fetcher used for phase-1, phase-2, and probe quotes.
|
|
18
|
+
* @returns The validated adjusted phase-2 quote, or the original phase-1 quote on fallback.
|
|
19
|
+
*/
|
|
20
|
+
export declare function getRelayMaxGasStationQuote(request: QuoteRequest, fullRequest: PayStrategyGetQuotesRequest, getSingleQuote: GetSingleQuoteFn): Promise<TransactionPayQuote<RelayQuote>>;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=relay-max-gas-station.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relay-max-gas-station.d.mts","sourceRoot":"","sources":["../../../src/strategy/relay/relay-max-gas-station.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,oBAAgB;AAE1C,OAAO,KAAK,EACV,2BAA2B,EAC3B,YAAY,EAEZ,mBAAmB,EACpB,wBAAoB;AA4BrB,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,CA4I1C"}
|