@metamask-previews/transaction-pay-controller 16.4.0-preview-384cfdfef → 16.4.1-preview-00245ea
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 +15 -1
- package/dist/strategy/relay/constants.cjs +3 -1
- package/dist/strategy/relay/constants.cjs.map +1 -1
- package/dist/strategy/relay/constants.d.cts +2 -0
- package/dist/strategy/relay/constants.d.cts.map +1 -1
- package/dist/strategy/relay/constants.d.mts +2 -0
- package/dist/strategy/relay/constants.d.mts.map +1 -1
- package/dist/strategy/relay/constants.mjs +2 -0
- package/dist/strategy/relay/constants.mjs.map +1 -1
- package/dist/strategy/relay/gas-station.cjs +1 -2
- package/dist/strategy/relay/gas-station.cjs.map +1 -1
- package/dist/strategy/relay/gas-station.d.cts.map +1 -1
- package/dist/strategy/relay/gas-station.d.mts.map +1 -1
- package/dist/strategy/relay/gas-station.mjs +2 -3
- package/dist/strategy/relay/gas-station.mjs.map +1 -1
- package/dist/strategy/relay/relay-api.cjs +55 -0
- package/dist/strategy/relay/relay-api.cjs.map +1 -0
- package/dist/strategy/relay/relay-api.d.cts +26 -0
- package/dist/strategy/relay/relay-api.d.cts.map +1 -0
- package/dist/strategy/relay/relay-api.d.mts +26 -0
- package/dist/strategy/relay/relay-api.d.mts.map +1 -0
- package/dist/strategy/relay/relay-api.mjs +49 -0
- package/dist/strategy/relay/relay-api.mjs.map +1 -0
- package/dist/strategy/relay/relay-quotes.cjs +8 -9
- 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 +10 -11
- package/dist/strategy/relay/relay-quotes.mjs.map +1 -1
- package/dist/strategy/relay/relay-submit.cjs +109 -7
- package/dist/strategy/relay/relay-submit.cjs.map +1 -1
- package/dist/strategy/relay/relay-submit.d.cts.map +1 -1
- package/dist/strategy/relay/relay-submit.d.mts.map +1 -1
- package/dist/strategy/relay/relay-submit.mjs +112 -10
- package/dist/strategy/relay/relay-submit.mjs.map +1 -1
- package/dist/strategy/relay/types.cjs.map +1 -1
- package/dist/strategy/relay/types.d.cts +27 -0
- package/dist/strategy/relay/types.d.cts.map +1 -1
- package/dist/strategy/relay/types.d.mts +27 -0
- package/dist/strategy/relay/types.d.mts.map +1 -1
- package/dist/strategy/relay/types.mjs.map +1 -1
- package/dist/tests/messenger-mock.d.cts +1 -2
- package/dist/tests/messenger-mock.d.cts.map +1 -1
- package/dist/tests/messenger-mock.d.mts +1 -2
- package/dist/tests/messenger-mock.d.mts.map +1 -1
- package/dist/utils/feature-flags.cjs +37 -7
- package/dist/utils/feature-flags.cjs.map +1 -1
- package/dist/utils/feature-flags.d.cts +24 -3
- package/dist/utils/feature-flags.d.cts.map +1 -1
- package/dist/utils/feature-flags.d.mts +24 -3
- package/dist/utils/feature-flags.d.mts.map +1 -1
- package/dist/utils/feature-flags.mjs +34 -6
- package/dist/utils/feature-flags.mjs.map +1 -1
- package/package.json +5 -5
|
@@ -6,6 +6,7 @@ const transaction_controller_1 = require("@metamask/transaction-controller");
|
|
|
6
6
|
const utils_1 = require("@metamask/utils");
|
|
7
7
|
const bignumber_js_1 = require("bignumber.js");
|
|
8
8
|
const constants_1 = require("./constants.cjs");
|
|
9
|
+
const relay_api_1 = require("./relay-api.cjs");
|
|
9
10
|
const logger_1 = require("../../logger.cjs");
|
|
10
11
|
const feature_flags_1 = require("../../utils/feature-flags.cjs");
|
|
11
12
|
const token_1 = require("../../utils/token.cjs");
|
|
@@ -46,7 +47,17 @@ async function executeSingleQuote(quote, messenger, transaction) {
|
|
|
46
47
|
tx.txParams.nonce = undefined;
|
|
47
48
|
});
|
|
48
49
|
await submitTransactions(quote, transaction, messenger);
|
|
49
|
-
const targetHash = await waitForRelayCompletion(quote.original)
|
|
50
|
+
const targetHash = await waitForRelayCompletion(quote.original, (sourceHash) => {
|
|
51
|
+
log('Source hash received', sourceHash);
|
|
52
|
+
(0, transaction_1.updateTransaction)({
|
|
53
|
+
transactionId: transaction.id,
|
|
54
|
+
messenger,
|
|
55
|
+
note: 'Add source hash from Relay status',
|
|
56
|
+
}, (tx) => {
|
|
57
|
+
tx.metamaskPay ?? (tx.metamaskPay = {});
|
|
58
|
+
tx.metamaskPay.sourceHash = sourceHash;
|
|
59
|
+
});
|
|
60
|
+
});
|
|
50
61
|
log('Relay request completed', targetHash);
|
|
51
62
|
(0, transaction_1.updateTransaction)({
|
|
52
63
|
transactionId: transaction.id,
|
|
@@ -61,9 +72,10 @@ async function executeSingleQuote(quote, messenger, transaction) {
|
|
|
61
72
|
* Wait for a Relay request to complete.
|
|
62
73
|
*
|
|
63
74
|
* @param quote - Relay quote associated with the request.
|
|
75
|
+
* @param onSourceHash - Called with the source tx hash as soon as it appears.
|
|
64
76
|
* @returns A promise that resolves when the Relay request is complete.
|
|
65
77
|
*/
|
|
66
|
-
async function waitForRelayCompletion(quote) {
|
|
78
|
+
async function waitForRelayCompletion(quote, onSourceHash) {
|
|
67
79
|
const isSameChain = quote.details.currencyIn.currency.chainId ===
|
|
68
80
|
quote.details.currencyOut.currency.chainId;
|
|
69
81
|
const isSingleDepositStep = quote.steps.length === 1 && quote.steps[0].id === 'deposit';
|
|
@@ -72,14 +84,17 @@ async function waitForRelayCompletion(quote) {
|
|
|
72
84
|
return FALLBACK_HASH;
|
|
73
85
|
}
|
|
74
86
|
const { requestId } = quote.steps[0];
|
|
75
|
-
|
|
87
|
+
let sourceHashEmitted = false;
|
|
76
88
|
while (true) {
|
|
77
|
-
const
|
|
78
|
-
const status = (await response.json());
|
|
89
|
+
const status = await (0, relay_api_1.getRelayStatus)(requestId);
|
|
79
90
|
log('Polled status', status.status, status);
|
|
91
|
+
if (!sourceHashEmitted && status.inTxHashes?.length) {
|
|
92
|
+
sourceHashEmitted = true;
|
|
93
|
+
onSourceHash?.(status.inTxHashes[0]);
|
|
94
|
+
}
|
|
80
95
|
if (status.status === 'success') {
|
|
81
|
-
const targetHash = status.txHashes?.slice(-1)[0];
|
|
82
|
-
return targetHash
|
|
96
|
+
const targetHash = status.txHashes?.slice(-1)[0] ?? FALLBACK_HASH;
|
|
97
|
+
return targetHash;
|
|
83
98
|
}
|
|
84
99
|
if (['failure', 'refund', 'refunded'].includes(status.status)) {
|
|
85
100
|
throw new Error(`Relay request failed with status: ${status.status}`);
|
|
@@ -144,6 +159,13 @@ async function validateSourceBalance(quote, messenger) {
|
|
|
144
159
|
/**
|
|
145
160
|
* Submit transactions for a relay quote.
|
|
146
161
|
*
|
|
162
|
+
* On EIP-7702 supported chains, combines the source calls via
|
|
163
|
+
* getDelegationTransaction and submits through Relay's /execute endpoint
|
|
164
|
+
* (gasless — Relay's relayer pays origin gas).
|
|
165
|
+
*
|
|
166
|
+
* On other chains, adds the transactions directly via the
|
|
167
|
+
* TransactionController and waits for on-chain confirmation.
|
|
168
|
+
*
|
|
147
169
|
* @param quote - Relay quote.
|
|
148
170
|
* @param transaction - Original transaction meta.
|
|
149
171
|
* @param messenger - Controller messenger.
|
|
@@ -173,8 +195,88 @@ async function submitTransactions(quote, transaction, messenger) {
|
|
|
173
195
|
...normalizedParams,
|
|
174
196
|
]
|
|
175
197
|
: normalizedParams;
|
|
198
|
+
const { sourceChainId } = quote.request;
|
|
199
|
+
if ((0, feature_flags_1.isRelayExecuteEnabled)(messenger) &&
|
|
200
|
+
(0, feature_flags_1.isEIP7702Chain)(messenger, sourceChainId)) {
|
|
201
|
+
return await submitViaRelayExecute(quote, transaction, messenger, allParams);
|
|
202
|
+
}
|
|
203
|
+
return await submitViaTransactionController(quote, transaction, messenger, normalizedParams, allParams);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Submit source transactions via Relay's /execute endpoint.
|
|
207
|
+
*
|
|
208
|
+
* Combines all source calls (approve + deposit, and optionally the
|
|
209
|
+
* original transaction for post-quote flows) into a single EIP-7702
|
|
210
|
+
* delegation transaction using getDelegationTransaction, then submits
|
|
211
|
+
* it to Relay's /execute endpoint for gasless execution.
|
|
212
|
+
*
|
|
213
|
+
* @param quote - Relay quote.
|
|
214
|
+
* @param transaction - Original transaction meta.
|
|
215
|
+
* @param messenger - Controller messenger.
|
|
216
|
+
* @param allParams - All source transaction params to combine.
|
|
217
|
+
* @returns Fallback hash (actual hash comes from relay status polling).
|
|
218
|
+
*/
|
|
219
|
+
async function submitViaRelayExecute(quote, transaction, messenger, allParams) {
|
|
220
|
+
const { from, sourceChainId } = quote.request;
|
|
221
|
+
const { requestId } = quote.original.steps[0];
|
|
222
|
+
const sourceCallTransaction = {
|
|
223
|
+
...transaction,
|
|
224
|
+
chainId: sourceChainId,
|
|
225
|
+
nestedTransactions: allParams.map((params) => ({
|
|
226
|
+
data: (params.data ?? '0x'),
|
|
227
|
+
to: params.to,
|
|
228
|
+
value: (params.value ?? '0x0'),
|
|
229
|
+
})),
|
|
230
|
+
};
|
|
231
|
+
const delegation = await messenger.call('TransactionPayController:getDelegationTransaction', { transaction: sourceCallTransaction });
|
|
232
|
+
log('Delegation result for source calls', delegation);
|
|
233
|
+
const executeBody = {
|
|
234
|
+
executionKind: 'rawCalls',
|
|
235
|
+
data: {
|
|
236
|
+
chainId: Number(sourceChainId),
|
|
237
|
+
to: delegation.to,
|
|
238
|
+
data: delegation.data,
|
|
239
|
+
value: new bignumber_js_1.BigNumber(delegation.value).toFixed(),
|
|
240
|
+
...(delegation.authorizationList?.length
|
|
241
|
+
? {
|
|
242
|
+
authorizationList: delegation.authorizationList.map((auth) => ({
|
|
243
|
+
chainId: Number(auth.chainId),
|
|
244
|
+
address: auth.address,
|
|
245
|
+
nonce: Number(auth.nonce),
|
|
246
|
+
yParity: Number(auth.yParity),
|
|
247
|
+
r: auth.r,
|
|
248
|
+
s: auth.s,
|
|
249
|
+
})),
|
|
250
|
+
}
|
|
251
|
+
: {}),
|
|
252
|
+
},
|
|
253
|
+
executionOptions: {
|
|
254
|
+
subsidizeFees: false,
|
|
255
|
+
},
|
|
256
|
+
requestId,
|
|
257
|
+
};
|
|
258
|
+
log('Submitting via Relay execute', { executeBody, from });
|
|
259
|
+
const result = await (0, relay_api_1.submitRelayExecute)(messenger, executeBody);
|
|
260
|
+
log('Relay execute response', result);
|
|
261
|
+
return FALLBACK_HASH;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Submit source transactions via the TransactionController.
|
|
265
|
+
*
|
|
266
|
+
* Uses addTransaction for single params or addTransactionBatch for
|
|
267
|
+
* multiple params. Waits for all transactions to be confirmed on-chain.
|
|
268
|
+
*
|
|
269
|
+
* @param quote - Relay quote.
|
|
270
|
+
* @param transaction - Original transaction meta.
|
|
271
|
+
* @param messenger - Controller messenger.
|
|
272
|
+
* @param normalizedParams - Normalized relay-only params (without prepended original tx).
|
|
273
|
+
* @param allParams - All params including any prepended original tx for post-quote flows.
|
|
274
|
+
* @returns Hash of the last submitted transaction.
|
|
275
|
+
*/
|
|
276
|
+
async function submitViaTransactionController(quote, transaction, messenger, normalizedParams, allParams) {
|
|
176
277
|
const transactionIds = [];
|
|
177
278
|
const { from, sourceChainId, sourceTokenAddress } = quote.request;
|
|
279
|
+
const { isPostQuote } = quote.request;
|
|
178
280
|
const networkClientId = messenger.call('NetworkController:findNetworkClientIdByChainId', sourceChainId);
|
|
179
281
|
log('Adding transactions', {
|
|
180
282
|
normalizedParams: allParams,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relay-submit.cjs","sourceRoot":"","sources":["../../../src/strategy/relay/relay-submit.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n ORIGIN_METAMASK,\n successfulFetch,\n toHex,\n} from '@metamask/controller-utils';\nimport { TransactionType } from '@metamask/transaction-controller';\nimport type { TransactionParams } from '@metamask/transaction-controller';\nimport type {\n AuthorizationList,\n TransactionMeta,\n} from '@metamask/transaction-controller';\nimport type { Hex } from '@metamask/utils';\nimport { createModuleLogger } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport {\n RELAY_DEPOSIT_TYPES,\n RELAY_POLLING_INTERVAL,\n RELAY_STATUS_URL,\n} from './constants';\nimport type { RelayQuote, RelayStatusResponse } from './types';\nimport { projectLogger } from '../../logger';\nimport type {\n PayStrategyExecuteRequest,\n TransactionPayControllerMessenger,\n TransactionPayQuote,\n} from '../../types';\nimport { getFeatureFlags } from '../../utils/feature-flags';\nimport {\n getLiveTokenBalance,\n normalizeTokenAddress,\n TokenAddressTarget,\n} from '../../utils/token';\nimport {\n collectTransactionIds,\n getTransaction,\n updateTransaction,\n waitForTransactionConfirmed,\n} from '../../utils/transaction';\n\nconst FALLBACK_HASH = '0x0' as Hex;\n\nconst log = createModuleLogger(projectLogger, 'relay-strategy');\n\n/**\n * Submits Relay quotes.\n *\n * @param request - Request object.\n * @returns An object containing the transaction hash if available.\n */\nexport async function submitRelayQuotes(\n request: PayStrategyExecuteRequest<RelayQuote>,\n): Promise<{ transactionHash?: Hex }> {\n log('Executing quotes', request);\n\n const { quotes, messenger, transaction } = request;\n\n let transactionHash: Hex | undefined;\n\n for (const quote of quotes) {\n ({ transactionHash } = await executeSingleQuote(\n quote,\n messenger,\n transaction,\n ));\n }\n\n return { transactionHash };\n}\n\n/**\n * Executes a single Relay quote.\n *\n * @param quote - Relay quote to execute.\n * @param messenger - Controller messenger.\n * @param transaction - Original transaction meta.\n * @returns An object containing the transaction hash if available.\n */\nasync function executeSingleQuote(\n quote: TransactionPayQuote<RelayQuote>,\n messenger: TransactionPayControllerMessenger,\n transaction: TransactionMeta,\n): Promise<{ transactionHash?: Hex }> {\n log('Executing single quote', quote);\n\n updateTransaction(\n {\n transactionId: transaction.id,\n messenger,\n note: 'Remove nonce from skipped transaction',\n },\n (tx) => {\n tx.txParams.nonce = undefined;\n },\n );\n\n await submitTransactions(quote, transaction, messenger);\n\n const targetHash = await waitForRelayCompletion(quote.original);\n\n log('Relay request completed', targetHash);\n\n updateTransaction(\n {\n transactionId: transaction.id,\n messenger,\n note: 'Intent complete after Relay completion',\n },\n (tx) => {\n tx.isIntentComplete = true;\n },\n );\n\n return { transactionHash: targetHash };\n}\n\n/**\n * Wait for a Relay request to complete.\n *\n * @param quote - Relay quote associated with the request.\n * @returns A promise that resolves when the Relay request is complete.\n */\nasync function waitForRelayCompletion(quote: RelayQuote): Promise<Hex> {\n const isSameChain =\n quote.details.currencyIn.currency.chainId ===\n quote.details.currencyOut.currency.chainId;\n\n const isSingleDepositStep =\n quote.steps.length === 1 && quote.steps[0].id === 'deposit';\n\n if (isSameChain && !isSingleDepositStep) {\n log('Skipping polling as same chain');\n return FALLBACK_HASH;\n }\n\n const { requestId } = quote.steps[0];\n const url = `${RELAY_STATUS_URL}?requestId=${requestId}`;\n\n while (true) {\n const response = await successfulFetch(url, { method: 'GET' });\n const status = (await response.json()) as RelayStatusResponse;\n\n log('Polled status', status.status, status);\n\n if (status.status === 'success') {\n const targetHash = status.txHashes?.slice(-1)[0] as Hex;\n return targetHash ?? FALLBACK_HASH;\n }\n\n if (['failure', 'refund', 'refunded'].includes(status.status)) {\n throw new Error(`Relay request failed with status: ${status.status}`);\n }\n\n await new Promise((resolve) => setTimeout(resolve, RELAY_POLLING_INTERVAL));\n }\n}\n\n/**\n * Normalize the parameters from a relay quote step to match TransactionParams.\n *\n * @param params - Parameters from a relay quote step.\n * @param messenger - Controller messenger.\n * @returns Normalized transaction parameters.\n */\nfunction normalizeParams(\n params: RelayQuote['steps'][0]['items'][0]['data'],\n messenger: TransactionPayControllerMessenger,\n): TransactionParams {\n const featureFlags = getFeatureFlags(messenger);\n\n return {\n data: params.data,\n from: params.from,\n gas: toHex(params.gas ?? featureFlags.relayFallbackGas.max),\n maxFeePerGas: toHex(params.maxFeePerGas),\n maxPriorityFeePerGas: toHex(params.maxPriorityFeePerGas),\n to: params.to,\n value: toHex(params.value ?? '0'),\n };\n}\n\n/**\n * Validate the source token balance is sufficient for the relay deposit.\n *\n * Reads the live balance from TokenBalancesController and compares it against\n * the quote's required source amount to prevent submitting transactions that\n * will revert on-chain due to insufficient balance.\n *\n * @param quote - Relay quote containing the required source amount.\n * @param messenger - Controller messenger.\n */\nasync function validateSourceBalance(\n quote: TransactionPayQuote<RelayQuote>,\n messenger: TransactionPayControllerMessenger,\n): Promise<void> {\n const { from, sourceChainId, sourceTokenAddress } = quote.request;\n\n const normalizedSourceTokenAddress = normalizeTokenAddress(\n sourceTokenAddress,\n sourceChainId,\n TokenAddressTarget.MetaMask,\n );\n\n let currentBalance: string;\n\n try {\n currentBalance = await getLiveTokenBalance(\n messenger,\n from,\n sourceChainId,\n normalizedSourceTokenAddress,\n );\n } catch (error) {\n throw new Error(\n `Cannot validate payment token balance - ${(error as Error).message}`,\n );\n }\n\n const requiredAmount = new BigNumber(quote.sourceAmount.raw);\n const balance = new BigNumber(currentBalance);\n\n log('Validating source balance', {\n from,\n sourceChainId,\n sourceTokenAddress,\n currentBalance,\n requiredAmount: requiredAmount.toString(10),\n });\n\n if (balance.isLessThan(requiredAmount)) {\n throw new Error(\n `Insufficient source token balance for relay deposit. ` +\n `Required: ${requiredAmount.toString(10)}, ` +\n `Available: ${balance.toString(10)}`,\n );\n }\n}\n\n/**\n * Submit transactions for a relay quote.\n *\n * @param quote - Relay quote.\n * @param transaction - Original transaction meta.\n * @param messenger - Controller messenger.\n * @returns Hash of the last submitted transaction.\n */\nasync function submitTransactions(\n quote: TransactionPayQuote<RelayQuote>,\n transaction: TransactionMeta,\n messenger: TransactionPayControllerMessenger,\n): Promise<Hex> {\n const { steps } = quote.original;\n const params = steps.flatMap((step) => step.items).map((item) => item.data);\n const invalidKind = steps.find((step) => step.kind !== 'transaction')?.kind;\n\n if (invalidKind) {\n throw new Error(`Unsupported step kind: ${invalidKind}`);\n }\n\n await validateSourceBalance(quote, messenger);\n\n const normalizedParams = params.map((singleParams) =>\n normalizeParams(singleParams, messenger),\n );\n\n // For post-quote flows, prepend the original transaction so it gets\n // included in the batch alongside the relay deposit(s).\n // This always results in multiple params, so it takes the batch path.\n const { isPostQuote } = quote.request;\n\n const allParams =\n isPostQuote && transaction.txParams.to\n ? [\n {\n data: transaction.txParams.data as Hex | undefined,\n from: transaction.txParams.from,\n to: transaction.txParams.to,\n value: transaction.txParams.value as Hex | undefined,\n } as TransactionParams,\n ...normalizedParams,\n ]\n : normalizedParams;\n\n const transactionIds: string[] = [];\n const { from, sourceChainId, sourceTokenAddress } = quote.request;\n\n const networkClientId = messenger.call(\n 'NetworkController:findNetworkClientIdByChainId',\n sourceChainId,\n );\n\n log('Adding transactions', {\n normalizedParams: allParams,\n sourceChainId,\n from,\n networkClientId,\n });\n\n const { end } = collectTransactionIds(\n sourceChainId,\n from,\n messenger,\n (transactionId) => {\n transactionIds.push(transactionId);\n\n updateTransaction(\n {\n transactionId: transaction.id,\n messenger,\n note: 'Add required transaction ID from Relay submission',\n },\n (tx) => {\n tx.requiredTransactionIds ??= [];\n tx.requiredTransactionIds.push(transactionId);\n },\n );\n },\n );\n\n let result: { result: Promise<string> } | undefined;\n\n const gasFeeToken = quote.fees.isSourceGasFeeToken\n ? sourceTokenAddress\n : undefined;\n\n const isSameChain =\n quote.original.details.currencyIn.currency.chainId ===\n quote.original.details.currencyOut.currency.chainId;\n\n const authorizationList: AuthorizationList | undefined =\n isSameChain && quote.original.request.authorizationList?.length\n ? quote.original.request.authorizationList.map((a) => ({\n address: a.address,\n chainId: toHex(a.chainId),\n }))\n : undefined;\n\n const { gasLimits } = quote.original.metamask;\n\n if (allParams.length === 1) {\n const transactionParams = {\n ...allParams[0],\n authorizationList,\n gas: toHex(gasLimits[0]),\n };\n\n result = await messenger.call(\n 'TransactionController:addTransaction',\n transactionParams,\n {\n gasFeeToken,\n networkClientId,\n origin: ORIGIN_METAMASK,\n requireApproval: false,\n type: getRelayDepositType(transaction.type),\n },\n );\n } else {\n const gasLimit7702 =\n gasLimits.length === 1 && allParams.length > 1\n ? toHex(gasLimits[0])\n : undefined;\n\n const transactions = allParams.map((singleParams, index) => {\n const gasLimit = gasLimits[index];\n const gas =\n gasLimit === undefined || gasLimit7702 ? undefined : toHex(gasLimit);\n\n return {\n params: {\n data: singleParams.data as Hex,\n gas,\n maxFeePerGas: singleParams.maxFeePerGas as Hex,\n maxPriorityFeePerGas: singleParams.maxPriorityFeePerGas as Hex,\n to: singleParams.to as Hex,\n value: singleParams.value as Hex,\n },\n type: getTransactionType(\n isPostQuote,\n index,\n transaction.type,\n normalizedParams.length,\n ),\n };\n });\n\n await messenger.call('TransactionController:addTransactionBatch', {\n from,\n disable7702: !gasLimit7702,\n disableHook: Boolean(gasLimit7702),\n disableSequential: Boolean(gasLimit7702),\n gasFeeToken,\n gasLimit7702,\n networkClientId,\n origin: ORIGIN_METAMASK,\n overwriteUpgrade: true,\n requireApproval: false,\n transactions,\n });\n }\n\n end();\n\n log('Added transactions', transactionIds);\n\n if (result) {\n const txHash = await result.result;\n log('Submitted transaction', txHash);\n }\n\n await Promise.all(\n transactionIds.map((txId) => waitForTransactionConfirmed(txId, messenger)),\n );\n\n log('All transactions confirmed', transactionIds);\n\n const hash = getTransaction(transactionIds.slice(-1)[0], messenger)?.hash;\n\n return hash as Hex;\n}\n\n/**\n * Determine the transaction type for a given index in the batch.\n *\n * @param isPostQuote - Whether this is a post-quote flow.\n * @param index - Index of the transaction in the batch.\n * @param originalType - Type of the original transaction (used for post-quote index 0).\n * @param relayParamCount - Number of relay-only params (excludes prepended original tx).\n * @returns The transaction type.\n */\nfunction getTransactionType(\n isPostQuote: boolean | undefined,\n index: number,\n originalType: TransactionMeta['type'],\n relayParamCount: number,\n): TransactionMeta['type'] {\n // Post-quote index 0 is the original transaction\n if (isPostQuote && index === 0) {\n return originalType;\n }\n\n // Adjust index for post-quote flows where original tx is prepended\n const relayIndex = isPostQuote ? index - 1 : index;\n\n const depositType = getRelayDepositType(originalType);\n\n // Single relay step is always a deposit (no approval needed)\n if (relayParamCount === 1) {\n return depositType;\n }\n\n return relayIndex === 0 ? TransactionType.tokenMethodApprove : depositType;\n}\n\n/**\n * Get the relay deposit transaction type based on the parent transaction type.\n *\n * @param originalType - Type of the parent transaction.\n * @returns The mapped relay deposit type, or `relayDeposit` as a fallback.\n */\nfunction getRelayDepositType(\n originalType: TransactionMeta['type'],\n): TransactionType {\n return (\n (originalType && RELAY_DEPOSIT_TYPES[originalType]) ??\n TransactionType.relayDeposit\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"relay-submit.cjs","sourceRoot":"","sources":["../../../src/strategy/relay/relay-submit.ts"],"names":[],"mappings":"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","sourcesContent":["import { ORIGIN_METAMASK, toHex } from '@metamask/controller-utils';\nimport { TransactionType } from '@metamask/transaction-controller';\nimport type { TransactionParams } from '@metamask/transaction-controller';\nimport type {\n AuthorizationList,\n TransactionMeta,\n} from '@metamask/transaction-controller';\nimport type { Hex } from '@metamask/utils';\nimport { createModuleLogger } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport { RELAY_DEPOSIT_TYPES, RELAY_POLLING_INTERVAL } from './constants';\nimport { getRelayStatus, submitRelayExecute } from './relay-api';\nimport type {\n RelayExecuteRequest,\n RelayQuote,\n RelayStatusResponse,\n} from './types';\nimport { projectLogger } from '../../logger';\nimport type {\n PayStrategyExecuteRequest,\n TransactionPayControllerMessenger,\n TransactionPayQuote,\n} from '../../types';\nimport {\n getFeatureFlags,\n isEIP7702Chain,\n isRelayExecuteEnabled,\n} from '../../utils/feature-flags';\nimport {\n getLiveTokenBalance,\n normalizeTokenAddress,\n TokenAddressTarget,\n} from '../../utils/token';\nimport {\n collectTransactionIds,\n getTransaction,\n updateTransaction,\n waitForTransactionConfirmed,\n} from '../../utils/transaction';\n\nconst FALLBACK_HASH = '0x0' as Hex;\n\nconst log = createModuleLogger(projectLogger, 'relay-strategy');\n\n/**\n * Submits Relay quotes.\n *\n * @param request - Request object.\n * @returns An object containing the transaction hash if available.\n */\nexport async function submitRelayQuotes(\n request: PayStrategyExecuteRequest<RelayQuote>,\n): Promise<{ transactionHash?: Hex }> {\n log('Executing quotes', request);\n\n const { quotes, messenger, transaction } = request;\n\n let transactionHash: Hex | undefined;\n\n for (const quote of quotes) {\n ({ transactionHash } = await executeSingleQuote(\n quote,\n messenger,\n transaction,\n ));\n }\n\n return { transactionHash };\n}\n\n/**\n * Executes a single Relay quote.\n *\n * @param quote - Relay quote to execute.\n * @param messenger - Controller messenger.\n * @param transaction - Original transaction meta.\n * @returns An object containing the transaction hash if available.\n */\nasync function executeSingleQuote(\n quote: TransactionPayQuote<RelayQuote>,\n messenger: TransactionPayControllerMessenger,\n transaction: TransactionMeta,\n): Promise<{ transactionHash?: Hex }> {\n log('Executing single quote', quote);\n\n updateTransaction(\n {\n transactionId: transaction.id,\n messenger,\n note: 'Remove nonce from skipped transaction',\n },\n (tx) => {\n tx.txParams.nonce = undefined;\n },\n );\n\n await submitTransactions(quote, transaction, messenger);\n\n const targetHash = await waitForRelayCompletion(\n quote.original,\n (sourceHash) => {\n log('Source hash received', sourceHash);\n\n updateTransaction(\n {\n transactionId: transaction.id,\n messenger,\n note: 'Add source hash from Relay status',\n },\n (tx) => {\n tx.metamaskPay ??= {};\n tx.metamaskPay.sourceHash = sourceHash;\n },\n );\n },\n );\n\n log('Relay request completed', targetHash);\n\n updateTransaction(\n {\n transactionId: transaction.id,\n messenger,\n note: 'Intent complete after Relay completion',\n },\n (tx) => {\n tx.isIntentComplete = true;\n },\n );\n\n return { transactionHash: targetHash };\n}\n\n/**\n * Wait for a Relay request to complete.\n *\n * @param quote - Relay quote associated with the request.\n * @param onSourceHash - Called with the source tx hash as soon as it appears.\n * @returns A promise that resolves when the Relay request is complete.\n */\nasync function waitForRelayCompletion(\n quote: RelayQuote,\n onSourceHash?: (hash: Hex) => void,\n): Promise<Hex> {\n const isSameChain =\n quote.details.currencyIn.currency.chainId ===\n quote.details.currencyOut.currency.chainId;\n\n const isSingleDepositStep =\n quote.steps.length === 1 && quote.steps[0].id === 'deposit';\n\n if (isSameChain && !isSingleDepositStep) {\n log('Skipping polling as same chain');\n return FALLBACK_HASH;\n }\n\n const { requestId } = quote.steps[0];\n let sourceHashEmitted = false;\n\n while (true) {\n const status: RelayStatusResponse = await getRelayStatus(requestId);\n\n log('Polled status', status.status, status);\n\n if (!sourceHashEmitted && status.inTxHashes?.length) {\n sourceHashEmitted = true;\n onSourceHash?.(status.inTxHashes[0] as Hex);\n }\n\n if (status.status === 'success') {\n const targetHash =\n (status.txHashes?.slice(-1)[0] as Hex) ?? FALLBACK_HASH;\n return targetHash;\n }\n\n if (['failure', 'refund', 'refunded'].includes(status.status)) {\n throw new Error(`Relay request failed with status: ${status.status}`);\n }\n\n await new Promise((resolve) => setTimeout(resolve, RELAY_POLLING_INTERVAL));\n }\n}\n\n/**\n * Normalize the parameters from a relay quote step to match TransactionParams.\n *\n * @param params - Parameters from a relay quote step.\n * @param messenger - Controller messenger.\n * @returns Normalized transaction parameters.\n */\nfunction normalizeParams(\n params: RelayQuote['steps'][0]['items'][0]['data'],\n messenger: TransactionPayControllerMessenger,\n): TransactionParams {\n const featureFlags = getFeatureFlags(messenger);\n\n return {\n data: params.data,\n from: params.from,\n gas: toHex(params.gas ?? featureFlags.relayFallbackGas.max),\n maxFeePerGas: toHex(params.maxFeePerGas),\n maxPriorityFeePerGas: toHex(params.maxPriorityFeePerGas),\n to: params.to,\n value: toHex(params.value ?? '0'),\n };\n}\n\n/**\n * Validate the source token balance is sufficient for the relay deposit.\n *\n * Reads the live balance from TokenBalancesController and compares it against\n * the quote's required source amount to prevent submitting transactions that\n * will revert on-chain due to insufficient balance.\n *\n * @param quote - Relay quote containing the required source amount.\n * @param messenger - Controller messenger.\n */\nasync function validateSourceBalance(\n quote: TransactionPayQuote<RelayQuote>,\n messenger: TransactionPayControllerMessenger,\n): Promise<void> {\n const { from, sourceChainId, sourceTokenAddress } = quote.request;\n\n const normalizedSourceTokenAddress = normalizeTokenAddress(\n sourceTokenAddress,\n sourceChainId,\n TokenAddressTarget.MetaMask,\n );\n\n let currentBalance: string;\n\n try {\n currentBalance = await getLiveTokenBalance(\n messenger,\n from,\n sourceChainId,\n normalizedSourceTokenAddress,\n );\n } catch (error) {\n throw new Error(\n `Cannot validate payment token balance - ${(error as Error).message}`,\n );\n }\n\n const requiredAmount = new BigNumber(quote.sourceAmount.raw);\n const balance = new BigNumber(currentBalance);\n\n log('Validating source balance', {\n from,\n sourceChainId,\n sourceTokenAddress,\n currentBalance,\n requiredAmount: requiredAmount.toString(10),\n });\n\n if (balance.isLessThan(requiredAmount)) {\n throw new Error(\n `Insufficient source token balance for relay deposit. ` +\n `Required: ${requiredAmount.toString(10)}, ` +\n `Available: ${balance.toString(10)}`,\n );\n }\n}\n\n/**\n * Submit transactions for a relay quote.\n *\n * On EIP-7702 supported chains, combines the source calls via\n * getDelegationTransaction and submits through Relay's /execute endpoint\n * (gasless — Relay's relayer pays origin gas).\n *\n * On other chains, adds the transactions directly via the\n * TransactionController and waits for on-chain confirmation.\n *\n * @param quote - Relay quote.\n * @param transaction - Original transaction meta.\n * @param messenger - Controller messenger.\n * @returns Hash of the last submitted transaction.\n */\nasync function submitTransactions(\n quote: TransactionPayQuote<RelayQuote>,\n transaction: TransactionMeta,\n messenger: TransactionPayControllerMessenger,\n): Promise<Hex> {\n const { steps } = quote.original;\n const params = steps.flatMap((step) => step.items).map((item) => item.data);\n const invalidKind = steps.find((step) => step.kind !== 'transaction')?.kind;\n\n if (invalidKind) {\n throw new Error(`Unsupported step kind: ${invalidKind}`);\n }\n\n await validateSourceBalance(quote, messenger);\n\n const normalizedParams = params.map((singleParams) =>\n normalizeParams(singleParams, messenger),\n );\n\n // For post-quote flows, prepend the original transaction so it gets\n // included in the batch alongside the relay deposit(s).\n // This always results in multiple params, so it takes the batch path.\n const { isPostQuote } = quote.request;\n\n const allParams =\n isPostQuote && transaction.txParams.to\n ? [\n {\n data: transaction.txParams.data as Hex | undefined,\n from: transaction.txParams.from,\n to: transaction.txParams.to,\n value: transaction.txParams.value as Hex | undefined,\n } as TransactionParams,\n ...normalizedParams,\n ]\n : normalizedParams;\n\n const { sourceChainId } = quote.request;\n\n if (\n isRelayExecuteEnabled(messenger) &&\n isEIP7702Chain(messenger, sourceChainId)\n ) {\n return await submitViaRelayExecute(\n quote,\n transaction,\n messenger,\n allParams,\n );\n }\n\n return await submitViaTransactionController(\n quote,\n transaction,\n messenger,\n normalizedParams,\n allParams,\n );\n}\n\n/**\n * Submit source transactions via Relay's /execute endpoint.\n *\n * Combines all source calls (approve + deposit, and optionally the\n * original transaction for post-quote flows) into a single EIP-7702\n * delegation transaction using getDelegationTransaction, then submits\n * it to Relay's /execute endpoint for gasless execution.\n *\n * @param quote - Relay quote.\n * @param transaction - Original transaction meta.\n * @param messenger - Controller messenger.\n * @param allParams - All source transaction params to combine.\n * @returns Fallback hash (actual hash comes from relay status polling).\n */\nasync function submitViaRelayExecute(\n quote: TransactionPayQuote<RelayQuote>,\n transaction: TransactionMeta,\n messenger: TransactionPayControllerMessenger,\n allParams: TransactionParams[],\n): Promise<Hex> {\n const { from, sourceChainId } = quote.request;\n const { requestId } = quote.original.steps[0];\n\n const sourceCallTransaction = {\n ...transaction,\n chainId: sourceChainId,\n nestedTransactions: allParams.map((params) => ({\n data: (params.data ?? '0x') as Hex,\n to: params.to as Hex,\n value: (params.value ?? '0x0') as Hex,\n })),\n } as TransactionMeta;\n\n const delegation = await messenger.call(\n 'TransactionPayController:getDelegationTransaction',\n { transaction: sourceCallTransaction },\n );\n\n log('Delegation result for source calls', delegation);\n\n const executeBody: RelayExecuteRequest = {\n executionKind: 'rawCalls',\n data: {\n chainId: Number(sourceChainId),\n to: delegation.to,\n data: delegation.data,\n value: new BigNumber(delegation.value).toFixed(),\n ...(delegation.authorizationList?.length\n ? {\n authorizationList: delegation.authorizationList.map((auth) => ({\n chainId: Number(auth.chainId),\n address: auth.address,\n nonce: Number(auth.nonce),\n yParity: Number(auth.yParity),\n r: auth.r as Hex,\n s: auth.s as Hex,\n })),\n }\n : {}),\n },\n executionOptions: {\n subsidizeFees: false,\n },\n requestId,\n };\n\n log('Submitting via Relay execute', { executeBody, from });\n\n const result = await submitRelayExecute(messenger, executeBody);\n\n log('Relay execute response', result);\n\n return FALLBACK_HASH;\n}\n\n/**\n * Submit source transactions via the TransactionController.\n *\n * Uses addTransaction for single params or addTransactionBatch for\n * multiple params. Waits for all transactions to be confirmed on-chain.\n *\n * @param quote - Relay quote.\n * @param transaction - Original transaction meta.\n * @param messenger - Controller messenger.\n * @param normalizedParams - Normalized relay-only params (without prepended original tx).\n * @param allParams - All params including any prepended original tx for post-quote flows.\n * @returns Hash of the last submitted transaction.\n */\nasync function submitViaTransactionController(\n quote: TransactionPayQuote<RelayQuote>,\n transaction: TransactionMeta,\n messenger: TransactionPayControllerMessenger,\n normalizedParams: TransactionParams[],\n allParams: TransactionParams[],\n): Promise<Hex> {\n const transactionIds: string[] = [];\n const { from, sourceChainId, sourceTokenAddress } = quote.request;\n const { isPostQuote } = quote.request;\n\n const networkClientId = messenger.call(\n 'NetworkController:findNetworkClientIdByChainId',\n sourceChainId,\n );\n\n log('Adding transactions', {\n normalizedParams: allParams,\n sourceChainId,\n from,\n networkClientId,\n });\n\n const { end } = collectTransactionIds(\n sourceChainId,\n from,\n messenger,\n (transactionId) => {\n transactionIds.push(transactionId);\n\n updateTransaction(\n {\n transactionId: transaction.id,\n messenger,\n note: 'Add required transaction ID from Relay submission',\n },\n (tx) => {\n tx.requiredTransactionIds ??= [];\n tx.requiredTransactionIds.push(transactionId);\n },\n );\n },\n );\n\n let result: { result: Promise<string> } | undefined;\n\n const gasFeeToken = quote.fees.isSourceGasFeeToken\n ? sourceTokenAddress\n : undefined;\n\n const isSameChain =\n quote.original.details.currencyIn.currency.chainId ===\n quote.original.details.currencyOut.currency.chainId;\n\n const authorizationList: AuthorizationList | undefined =\n isSameChain && quote.original.request.authorizationList?.length\n ? quote.original.request.authorizationList.map((a) => ({\n address: a.address,\n chainId: toHex(a.chainId),\n }))\n : undefined;\n\n const { gasLimits } = quote.original.metamask;\n\n if (allParams.length === 1) {\n const transactionParams = {\n ...allParams[0],\n authorizationList,\n gas: toHex(gasLimits[0]),\n };\n\n result = await messenger.call(\n 'TransactionController:addTransaction',\n transactionParams,\n {\n gasFeeToken,\n networkClientId,\n origin: ORIGIN_METAMASK,\n requireApproval: false,\n type: getRelayDepositType(transaction.type),\n },\n );\n } else {\n const gasLimit7702 =\n gasLimits.length === 1 && allParams.length > 1\n ? toHex(gasLimits[0])\n : undefined;\n\n const transactions = allParams.map((singleParams, index) => {\n const gasLimit = gasLimits[index];\n const gas =\n gasLimit === undefined || gasLimit7702 ? undefined : toHex(gasLimit);\n\n return {\n params: {\n data: singleParams.data as Hex,\n gas,\n maxFeePerGas: singleParams.maxFeePerGas as Hex,\n maxPriorityFeePerGas: singleParams.maxPriorityFeePerGas as Hex,\n to: singleParams.to as Hex,\n value: singleParams.value as Hex,\n },\n type: getTransactionType(\n isPostQuote,\n index,\n transaction.type,\n normalizedParams.length,\n ),\n };\n });\n\n await messenger.call('TransactionController:addTransactionBatch', {\n from,\n disable7702: !gasLimit7702,\n disableHook: Boolean(gasLimit7702),\n disableSequential: Boolean(gasLimit7702),\n gasFeeToken,\n gasLimit7702,\n networkClientId,\n origin: ORIGIN_METAMASK,\n overwriteUpgrade: true,\n requireApproval: false,\n transactions,\n });\n }\n\n end();\n\n log('Added transactions', transactionIds);\n\n if (result) {\n const txHash = await result.result;\n log('Submitted transaction', txHash);\n }\n\n await Promise.all(\n transactionIds.map((txId) => waitForTransactionConfirmed(txId, messenger)),\n );\n\n log('All transactions confirmed', transactionIds);\n\n const hash = getTransaction(transactionIds.slice(-1)[0], messenger)?.hash;\n\n return hash as Hex;\n}\n\n/**\n * Determine the transaction type for a given index in the batch.\n *\n * @param isPostQuote - Whether this is a post-quote flow.\n * @param index - Index of the transaction in the batch.\n * @param originalType - Type of the original transaction (used for post-quote index 0).\n * @param relayParamCount - Number of relay-only params (excludes prepended original tx).\n * @returns The transaction type.\n */\nfunction getTransactionType(\n isPostQuote: boolean | undefined,\n index: number,\n originalType: TransactionMeta['type'],\n relayParamCount: number,\n): TransactionMeta['type'] {\n // Post-quote index 0 is the original transaction\n if (isPostQuote && index === 0) {\n return originalType;\n }\n\n // Adjust index for post-quote flows where original tx is prepended\n const relayIndex = isPostQuote ? index - 1 : index;\n\n const depositType = getRelayDepositType(originalType);\n\n // Single relay step is always a deposit (no approval needed)\n if (relayParamCount === 1) {\n return depositType;\n }\n\n return relayIndex === 0 ? TransactionType.tokenMethodApprove : depositType;\n}\n\n/**\n * Get the relay deposit transaction type based on the parent transaction type.\n *\n * @param originalType - Type of the parent transaction.\n * @returns The mapped relay deposit type, or `relayDeposit` as a fallback.\n */\nfunction getRelayDepositType(\n originalType: TransactionMeta['type'],\n): TransactionType {\n return (\n (originalType && RELAY_DEPOSIT_TYPES[originalType]) ??\n TransactionType.relayDeposit\n );\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relay-submit.d.cts","sourceRoot":"","sources":["../../../src/strategy/relay/relay-submit.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"relay-submit.d.cts","sourceRoot":"","sources":["../../../src/strategy/relay/relay-submit.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAM3C,OAAO,KAAK,EAEV,UAAU,EAEX,oBAAgB;AAEjB,OAAO,KAAK,EACV,yBAAyB,EAG1B,wBAAoB;AAsBrB;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,yBAAyB,CAAC,UAAU,CAAC,GAC7C,OAAO,CAAC;IAAE,eAAe,CAAC,EAAE,GAAG,CAAA;CAAE,CAAC,CAgBpC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relay-submit.d.mts","sourceRoot":"","sources":["../../../src/strategy/relay/relay-submit.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"relay-submit.d.mts","sourceRoot":"","sources":["../../../src/strategy/relay/relay-submit.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAM3C,OAAO,KAAK,EAEV,UAAU,EAEX,oBAAgB;AAEjB,OAAO,KAAK,EACV,yBAAyB,EAG1B,wBAAoB;AAsBrB;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,yBAAyB,CAAC,UAAU,CAAC,GAC7C,OAAO,CAAC;IAAE,eAAe,CAAC,EAAE,GAAG,CAAA;CAAE,CAAC,CAgBpC"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { ORIGIN_METAMASK,
|
|
1
|
+
import { ORIGIN_METAMASK, toHex } from "@metamask/controller-utils";
|
|
2
2
|
import { TransactionType } from "@metamask/transaction-controller";
|
|
3
3
|
import { createModuleLogger } from "@metamask/utils";
|
|
4
4
|
import { BigNumber } from "bignumber.js";
|
|
5
|
-
import { RELAY_DEPOSIT_TYPES, RELAY_POLLING_INTERVAL
|
|
5
|
+
import { RELAY_DEPOSIT_TYPES, RELAY_POLLING_INTERVAL } from "./constants.mjs";
|
|
6
|
+
import { getRelayStatus, submitRelayExecute } from "./relay-api.mjs";
|
|
6
7
|
import { projectLogger } from "../../logger.mjs";
|
|
7
|
-
import { getFeatureFlags } from "../../utils/feature-flags.mjs";
|
|
8
|
+
import { getFeatureFlags, isEIP7702Chain, isRelayExecuteEnabled } from "../../utils/feature-flags.mjs";
|
|
8
9
|
import { getLiveTokenBalance, normalizeTokenAddress, TokenAddressTarget } from "../../utils/token.mjs";
|
|
9
10
|
import { collectTransactionIds, getTransaction, updateTransaction, waitForTransactionConfirmed } from "../../utils/transaction.mjs";
|
|
10
11
|
const FALLBACK_HASH = '0x0';
|
|
@@ -42,7 +43,17 @@ async function executeSingleQuote(quote, messenger, transaction) {
|
|
|
42
43
|
tx.txParams.nonce = undefined;
|
|
43
44
|
});
|
|
44
45
|
await submitTransactions(quote, transaction, messenger);
|
|
45
|
-
const targetHash = await waitForRelayCompletion(quote.original)
|
|
46
|
+
const targetHash = await waitForRelayCompletion(quote.original, (sourceHash) => {
|
|
47
|
+
log('Source hash received', sourceHash);
|
|
48
|
+
updateTransaction({
|
|
49
|
+
transactionId: transaction.id,
|
|
50
|
+
messenger,
|
|
51
|
+
note: 'Add source hash from Relay status',
|
|
52
|
+
}, (tx) => {
|
|
53
|
+
tx.metamaskPay ?? (tx.metamaskPay = {});
|
|
54
|
+
tx.metamaskPay.sourceHash = sourceHash;
|
|
55
|
+
});
|
|
56
|
+
});
|
|
46
57
|
log('Relay request completed', targetHash);
|
|
47
58
|
updateTransaction({
|
|
48
59
|
transactionId: transaction.id,
|
|
@@ -57,9 +68,10 @@ async function executeSingleQuote(quote, messenger, transaction) {
|
|
|
57
68
|
* Wait for a Relay request to complete.
|
|
58
69
|
*
|
|
59
70
|
* @param quote - Relay quote associated with the request.
|
|
71
|
+
* @param onSourceHash - Called with the source tx hash as soon as it appears.
|
|
60
72
|
* @returns A promise that resolves when the Relay request is complete.
|
|
61
73
|
*/
|
|
62
|
-
async function waitForRelayCompletion(quote) {
|
|
74
|
+
async function waitForRelayCompletion(quote, onSourceHash) {
|
|
63
75
|
const isSameChain = quote.details.currencyIn.currency.chainId ===
|
|
64
76
|
quote.details.currencyOut.currency.chainId;
|
|
65
77
|
const isSingleDepositStep = quote.steps.length === 1 && quote.steps[0].id === 'deposit';
|
|
@@ -68,14 +80,17 @@ async function waitForRelayCompletion(quote) {
|
|
|
68
80
|
return FALLBACK_HASH;
|
|
69
81
|
}
|
|
70
82
|
const { requestId } = quote.steps[0];
|
|
71
|
-
|
|
83
|
+
let sourceHashEmitted = false;
|
|
72
84
|
while (true) {
|
|
73
|
-
const
|
|
74
|
-
const status = (await response.json());
|
|
85
|
+
const status = await getRelayStatus(requestId);
|
|
75
86
|
log('Polled status', status.status, status);
|
|
87
|
+
if (!sourceHashEmitted && status.inTxHashes?.length) {
|
|
88
|
+
sourceHashEmitted = true;
|
|
89
|
+
onSourceHash?.(status.inTxHashes[0]);
|
|
90
|
+
}
|
|
76
91
|
if (status.status === 'success') {
|
|
77
|
-
const targetHash = status.txHashes?.slice(-1)[0];
|
|
78
|
-
return targetHash
|
|
92
|
+
const targetHash = status.txHashes?.slice(-1)[0] ?? FALLBACK_HASH;
|
|
93
|
+
return targetHash;
|
|
79
94
|
}
|
|
80
95
|
if (['failure', 'refund', 'refunded'].includes(status.status)) {
|
|
81
96
|
throw new Error(`Relay request failed with status: ${status.status}`);
|
|
@@ -140,6 +155,13 @@ async function validateSourceBalance(quote, messenger) {
|
|
|
140
155
|
/**
|
|
141
156
|
* Submit transactions for a relay quote.
|
|
142
157
|
*
|
|
158
|
+
* On EIP-7702 supported chains, combines the source calls via
|
|
159
|
+
* getDelegationTransaction and submits through Relay's /execute endpoint
|
|
160
|
+
* (gasless — Relay's relayer pays origin gas).
|
|
161
|
+
*
|
|
162
|
+
* On other chains, adds the transactions directly via the
|
|
163
|
+
* TransactionController and waits for on-chain confirmation.
|
|
164
|
+
*
|
|
143
165
|
* @param quote - Relay quote.
|
|
144
166
|
* @param transaction - Original transaction meta.
|
|
145
167
|
* @param messenger - Controller messenger.
|
|
@@ -169,8 +191,88 @@ async function submitTransactions(quote, transaction, messenger) {
|
|
|
169
191
|
...normalizedParams,
|
|
170
192
|
]
|
|
171
193
|
: normalizedParams;
|
|
194
|
+
const { sourceChainId } = quote.request;
|
|
195
|
+
if (isRelayExecuteEnabled(messenger) &&
|
|
196
|
+
isEIP7702Chain(messenger, sourceChainId)) {
|
|
197
|
+
return await submitViaRelayExecute(quote, transaction, messenger, allParams);
|
|
198
|
+
}
|
|
199
|
+
return await submitViaTransactionController(quote, transaction, messenger, normalizedParams, allParams);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Submit source transactions via Relay's /execute endpoint.
|
|
203
|
+
*
|
|
204
|
+
* Combines all source calls (approve + deposit, and optionally the
|
|
205
|
+
* original transaction for post-quote flows) into a single EIP-7702
|
|
206
|
+
* delegation transaction using getDelegationTransaction, then submits
|
|
207
|
+
* it to Relay's /execute endpoint for gasless execution.
|
|
208
|
+
*
|
|
209
|
+
* @param quote - Relay quote.
|
|
210
|
+
* @param transaction - Original transaction meta.
|
|
211
|
+
* @param messenger - Controller messenger.
|
|
212
|
+
* @param allParams - All source transaction params to combine.
|
|
213
|
+
* @returns Fallback hash (actual hash comes from relay status polling).
|
|
214
|
+
*/
|
|
215
|
+
async function submitViaRelayExecute(quote, transaction, messenger, allParams) {
|
|
216
|
+
const { from, sourceChainId } = quote.request;
|
|
217
|
+
const { requestId } = quote.original.steps[0];
|
|
218
|
+
const sourceCallTransaction = {
|
|
219
|
+
...transaction,
|
|
220
|
+
chainId: sourceChainId,
|
|
221
|
+
nestedTransactions: allParams.map((params) => ({
|
|
222
|
+
data: (params.data ?? '0x'),
|
|
223
|
+
to: params.to,
|
|
224
|
+
value: (params.value ?? '0x0'),
|
|
225
|
+
})),
|
|
226
|
+
};
|
|
227
|
+
const delegation = await messenger.call('TransactionPayController:getDelegationTransaction', { transaction: sourceCallTransaction });
|
|
228
|
+
log('Delegation result for source calls', delegation);
|
|
229
|
+
const executeBody = {
|
|
230
|
+
executionKind: 'rawCalls',
|
|
231
|
+
data: {
|
|
232
|
+
chainId: Number(sourceChainId),
|
|
233
|
+
to: delegation.to,
|
|
234
|
+
data: delegation.data,
|
|
235
|
+
value: new BigNumber(delegation.value).toFixed(),
|
|
236
|
+
...(delegation.authorizationList?.length
|
|
237
|
+
? {
|
|
238
|
+
authorizationList: delegation.authorizationList.map((auth) => ({
|
|
239
|
+
chainId: Number(auth.chainId),
|
|
240
|
+
address: auth.address,
|
|
241
|
+
nonce: Number(auth.nonce),
|
|
242
|
+
yParity: Number(auth.yParity),
|
|
243
|
+
r: auth.r,
|
|
244
|
+
s: auth.s,
|
|
245
|
+
})),
|
|
246
|
+
}
|
|
247
|
+
: {}),
|
|
248
|
+
},
|
|
249
|
+
executionOptions: {
|
|
250
|
+
subsidizeFees: false,
|
|
251
|
+
},
|
|
252
|
+
requestId,
|
|
253
|
+
};
|
|
254
|
+
log('Submitting via Relay execute', { executeBody, from });
|
|
255
|
+
const result = await submitRelayExecute(messenger, executeBody);
|
|
256
|
+
log('Relay execute response', result);
|
|
257
|
+
return FALLBACK_HASH;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Submit source transactions via the TransactionController.
|
|
261
|
+
*
|
|
262
|
+
* Uses addTransaction for single params or addTransactionBatch for
|
|
263
|
+
* multiple params. Waits for all transactions to be confirmed on-chain.
|
|
264
|
+
*
|
|
265
|
+
* @param quote - Relay quote.
|
|
266
|
+
* @param transaction - Original transaction meta.
|
|
267
|
+
* @param messenger - Controller messenger.
|
|
268
|
+
* @param normalizedParams - Normalized relay-only params (without prepended original tx).
|
|
269
|
+
* @param allParams - All params including any prepended original tx for post-quote flows.
|
|
270
|
+
* @returns Hash of the last submitted transaction.
|
|
271
|
+
*/
|
|
272
|
+
async function submitViaTransactionController(quote, transaction, messenger, normalizedParams, allParams) {
|
|
172
273
|
const transactionIds = [];
|
|
173
274
|
const { from, sourceChainId, sourceTokenAddress } = quote.request;
|
|
275
|
+
const { isPostQuote } = quote.request;
|
|
174
276
|
const networkClientId = messenger.call('NetworkController:findNetworkClientIdByChainId', sourceChainId);
|
|
175
277
|
log('Adding transactions', {
|
|
176
278
|
normalizedParams: allParams,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relay-submit.mjs","sourceRoot":"","sources":["../../../src/strategy/relay/relay-submit.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n ORIGIN_METAMASK,\n successfulFetch,\n toHex,\n} from '@metamask/controller-utils';\nimport { TransactionType } from '@metamask/transaction-controller';\nimport type { TransactionParams } from '@metamask/transaction-controller';\nimport type {\n AuthorizationList,\n TransactionMeta,\n} from '@metamask/transaction-controller';\nimport type { Hex } from '@metamask/utils';\nimport { createModuleLogger } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport {\n RELAY_DEPOSIT_TYPES,\n RELAY_POLLING_INTERVAL,\n RELAY_STATUS_URL,\n} from './constants';\nimport type { RelayQuote, RelayStatusResponse } from './types';\nimport { projectLogger } from '../../logger';\nimport type {\n PayStrategyExecuteRequest,\n TransactionPayControllerMessenger,\n TransactionPayQuote,\n} from '../../types';\nimport { getFeatureFlags } from '../../utils/feature-flags';\nimport {\n getLiveTokenBalance,\n normalizeTokenAddress,\n TokenAddressTarget,\n} from '../../utils/token';\nimport {\n collectTransactionIds,\n getTransaction,\n updateTransaction,\n waitForTransactionConfirmed,\n} from '../../utils/transaction';\n\nconst FALLBACK_HASH = '0x0' as Hex;\n\nconst log = createModuleLogger(projectLogger, 'relay-strategy');\n\n/**\n * Submits Relay quotes.\n *\n * @param request - Request object.\n * @returns An object containing the transaction hash if available.\n */\nexport async function submitRelayQuotes(\n request: PayStrategyExecuteRequest<RelayQuote>,\n): Promise<{ transactionHash?: Hex }> {\n log('Executing quotes', request);\n\n const { quotes, messenger, transaction } = request;\n\n let transactionHash: Hex | undefined;\n\n for (const quote of quotes) {\n ({ transactionHash } = await executeSingleQuote(\n quote,\n messenger,\n transaction,\n ));\n }\n\n return { transactionHash };\n}\n\n/**\n * Executes a single Relay quote.\n *\n * @param quote - Relay quote to execute.\n * @param messenger - Controller messenger.\n * @param transaction - Original transaction meta.\n * @returns An object containing the transaction hash if available.\n */\nasync function executeSingleQuote(\n quote: TransactionPayQuote<RelayQuote>,\n messenger: TransactionPayControllerMessenger,\n transaction: TransactionMeta,\n): Promise<{ transactionHash?: Hex }> {\n log('Executing single quote', quote);\n\n updateTransaction(\n {\n transactionId: transaction.id,\n messenger,\n note: 'Remove nonce from skipped transaction',\n },\n (tx) => {\n tx.txParams.nonce = undefined;\n },\n );\n\n await submitTransactions(quote, transaction, messenger);\n\n const targetHash = await waitForRelayCompletion(quote.original);\n\n log('Relay request completed', targetHash);\n\n updateTransaction(\n {\n transactionId: transaction.id,\n messenger,\n note: 'Intent complete after Relay completion',\n },\n (tx) => {\n tx.isIntentComplete = true;\n },\n );\n\n return { transactionHash: targetHash };\n}\n\n/**\n * Wait for a Relay request to complete.\n *\n * @param quote - Relay quote associated with the request.\n * @returns A promise that resolves when the Relay request is complete.\n */\nasync function waitForRelayCompletion(quote: RelayQuote): Promise<Hex> {\n const isSameChain =\n quote.details.currencyIn.currency.chainId ===\n quote.details.currencyOut.currency.chainId;\n\n const isSingleDepositStep =\n quote.steps.length === 1 && quote.steps[0].id === 'deposit';\n\n if (isSameChain && !isSingleDepositStep) {\n log('Skipping polling as same chain');\n return FALLBACK_HASH;\n }\n\n const { requestId } = quote.steps[0];\n const url = `${RELAY_STATUS_URL}?requestId=${requestId}`;\n\n while (true) {\n const response = await successfulFetch(url, { method: 'GET' });\n const status = (await response.json()) as RelayStatusResponse;\n\n log('Polled status', status.status, status);\n\n if (status.status === 'success') {\n const targetHash = status.txHashes?.slice(-1)[0] as Hex;\n return targetHash ?? FALLBACK_HASH;\n }\n\n if (['failure', 'refund', 'refunded'].includes(status.status)) {\n throw new Error(`Relay request failed with status: ${status.status}`);\n }\n\n await new Promise((resolve) => setTimeout(resolve, RELAY_POLLING_INTERVAL));\n }\n}\n\n/**\n * Normalize the parameters from a relay quote step to match TransactionParams.\n *\n * @param params - Parameters from a relay quote step.\n * @param messenger - Controller messenger.\n * @returns Normalized transaction parameters.\n */\nfunction normalizeParams(\n params: RelayQuote['steps'][0]['items'][0]['data'],\n messenger: TransactionPayControllerMessenger,\n): TransactionParams {\n const featureFlags = getFeatureFlags(messenger);\n\n return {\n data: params.data,\n from: params.from,\n gas: toHex(params.gas ?? featureFlags.relayFallbackGas.max),\n maxFeePerGas: toHex(params.maxFeePerGas),\n maxPriorityFeePerGas: toHex(params.maxPriorityFeePerGas),\n to: params.to,\n value: toHex(params.value ?? '0'),\n };\n}\n\n/**\n * Validate the source token balance is sufficient for the relay deposit.\n *\n * Reads the live balance from TokenBalancesController and compares it against\n * the quote's required source amount to prevent submitting transactions that\n * will revert on-chain due to insufficient balance.\n *\n * @param quote - Relay quote containing the required source amount.\n * @param messenger - Controller messenger.\n */\nasync function validateSourceBalance(\n quote: TransactionPayQuote<RelayQuote>,\n messenger: TransactionPayControllerMessenger,\n): Promise<void> {\n const { from, sourceChainId, sourceTokenAddress } = quote.request;\n\n const normalizedSourceTokenAddress = normalizeTokenAddress(\n sourceTokenAddress,\n sourceChainId,\n TokenAddressTarget.MetaMask,\n );\n\n let currentBalance: string;\n\n try {\n currentBalance = await getLiveTokenBalance(\n messenger,\n from,\n sourceChainId,\n normalizedSourceTokenAddress,\n );\n } catch (error) {\n throw new Error(\n `Cannot validate payment token balance - ${(error as Error).message}`,\n );\n }\n\n const requiredAmount = new BigNumber(quote.sourceAmount.raw);\n const balance = new BigNumber(currentBalance);\n\n log('Validating source balance', {\n from,\n sourceChainId,\n sourceTokenAddress,\n currentBalance,\n requiredAmount: requiredAmount.toString(10),\n });\n\n if (balance.isLessThan(requiredAmount)) {\n throw new Error(\n `Insufficient source token balance for relay deposit. ` +\n `Required: ${requiredAmount.toString(10)}, ` +\n `Available: ${balance.toString(10)}`,\n );\n }\n}\n\n/**\n * Submit transactions for a relay quote.\n *\n * @param quote - Relay quote.\n * @param transaction - Original transaction meta.\n * @param messenger - Controller messenger.\n * @returns Hash of the last submitted transaction.\n */\nasync function submitTransactions(\n quote: TransactionPayQuote<RelayQuote>,\n transaction: TransactionMeta,\n messenger: TransactionPayControllerMessenger,\n): Promise<Hex> {\n const { steps } = quote.original;\n const params = steps.flatMap((step) => step.items).map((item) => item.data);\n const invalidKind = steps.find((step) => step.kind !== 'transaction')?.kind;\n\n if (invalidKind) {\n throw new Error(`Unsupported step kind: ${invalidKind}`);\n }\n\n await validateSourceBalance(quote, messenger);\n\n const normalizedParams = params.map((singleParams) =>\n normalizeParams(singleParams, messenger),\n );\n\n // For post-quote flows, prepend the original transaction so it gets\n // included in the batch alongside the relay deposit(s).\n // This always results in multiple params, so it takes the batch path.\n const { isPostQuote } = quote.request;\n\n const allParams =\n isPostQuote && transaction.txParams.to\n ? [\n {\n data: transaction.txParams.data as Hex | undefined,\n from: transaction.txParams.from,\n to: transaction.txParams.to,\n value: transaction.txParams.value as Hex | undefined,\n } as TransactionParams,\n ...normalizedParams,\n ]\n : normalizedParams;\n\n const transactionIds: string[] = [];\n const { from, sourceChainId, sourceTokenAddress } = quote.request;\n\n const networkClientId = messenger.call(\n 'NetworkController:findNetworkClientIdByChainId',\n sourceChainId,\n );\n\n log('Adding transactions', {\n normalizedParams: allParams,\n sourceChainId,\n from,\n networkClientId,\n });\n\n const { end } = collectTransactionIds(\n sourceChainId,\n from,\n messenger,\n (transactionId) => {\n transactionIds.push(transactionId);\n\n updateTransaction(\n {\n transactionId: transaction.id,\n messenger,\n note: 'Add required transaction ID from Relay submission',\n },\n (tx) => {\n tx.requiredTransactionIds ??= [];\n tx.requiredTransactionIds.push(transactionId);\n },\n );\n },\n );\n\n let result: { result: Promise<string> } | undefined;\n\n const gasFeeToken = quote.fees.isSourceGasFeeToken\n ? sourceTokenAddress\n : undefined;\n\n const isSameChain =\n quote.original.details.currencyIn.currency.chainId ===\n quote.original.details.currencyOut.currency.chainId;\n\n const authorizationList: AuthorizationList | undefined =\n isSameChain && quote.original.request.authorizationList?.length\n ? quote.original.request.authorizationList.map((a) => ({\n address: a.address,\n chainId: toHex(a.chainId),\n }))\n : undefined;\n\n const { gasLimits } = quote.original.metamask;\n\n if (allParams.length === 1) {\n const transactionParams = {\n ...allParams[0],\n authorizationList,\n gas: toHex(gasLimits[0]),\n };\n\n result = await messenger.call(\n 'TransactionController:addTransaction',\n transactionParams,\n {\n gasFeeToken,\n networkClientId,\n origin: ORIGIN_METAMASK,\n requireApproval: false,\n type: getRelayDepositType(transaction.type),\n },\n );\n } else {\n const gasLimit7702 =\n gasLimits.length === 1 && allParams.length > 1\n ? toHex(gasLimits[0])\n : undefined;\n\n const transactions = allParams.map((singleParams, index) => {\n const gasLimit = gasLimits[index];\n const gas =\n gasLimit === undefined || gasLimit7702 ? undefined : toHex(gasLimit);\n\n return {\n params: {\n data: singleParams.data as Hex,\n gas,\n maxFeePerGas: singleParams.maxFeePerGas as Hex,\n maxPriorityFeePerGas: singleParams.maxPriorityFeePerGas as Hex,\n to: singleParams.to as Hex,\n value: singleParams.value as Hex,\n },\n type: getTransactionType(\n isPostQuote,\n index,\n transaction.type,\n normalizedParams.length,\n ),\n };\n });\n\n await messenger.call('TransactionController:addTransactionBatch', {\n from,\n disable7702: !gasLimit7702,\n disableHook: Boolean(gasLimit7702),\n disableSequential: Boolean(gasLimit7702),\n gasFeeToken,\n gasLimit7702,\n networkClientId,\n origin: ORIGIN_METAMASK,\n overwriteUpgrade: true,\n requireApproval: false,\n transactions,\n });\n }\n\n end();\n\n log('Added transactions', transactionIds);\n\n if (result) {\n const txHash = await result.result;\n log('Submitted transaction', txHash);\n }\n\n await Promise.all(\n transactionIds.map((txId) => waitForTransactionConfirmed(txId, messenger)),\n );\n\n log('All transactions confirmed', transactionIds);\n\n const hash = getTransaction(transactionIds.slice(-1)[0], messenger)?.hash;\n\n return hash as Hex;\n}\n\n/**\n * Determine the transaction type for a given index in the batch.\n *\n * @param isPostQuote - Whether this is a post-quote flow.\n * @param index - Index of the transaction in the batch.\n * @param originalType - Type of the original transaction (used for post-quote index 0).\n * @param relayParamCount - Number of relay-only params (excludes prepended original tx).\n * @returns The transaction type.\n */\nfunction getTransactionType(\n isPostQuote: boolean | undefined,\n index: number,\n originalType: TransactionMeta['type'],\n relayParamCount: number,\n): TransactionMeta['type'] {\n // Post-quote index 0 is the original transaction\n if (isPostQuote && index === 0) {\n return originalType;\n }\n\n // Adjust index for post-quote flows where original tx is prepended\n const relayIndex = isPostQuote ? index - 1 : index;\n\n const depositType = getRelayDepositType(originalType);\n\n // Single relay step is always a deposit (no approval needed)\n if (relayParamCount === 1) {\n return depositType;\n }\n\n return relayIndex === 0 ? TransactionType.tokenMethodApprove : depositType;\n}\n\n/**\n * Get the relay deposit transaction type based on the parent transaction type.\n *\n * @param originalType - Type of the parent transaction.\n * @returns The mapped relay deposit type, or `relayDeposit` as a fallback.\n */\nfunction getRelayDepositType(\n originalType: TransactionMeta['type'],\n): TransactionType {\n return (\n (originalType && RELAY_DEPOSIT_TYPES[originalType]) ??\n TransactionType.relayDeposit\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"relay-submit.mjs","sourceRoot":"","sources":["../../../src/strategy/relay/relay-submit.ts"],"names":[],"mappings":"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","sourcesContent":["import { ORIGIN_METAMASK, toHex } from '@metamask/controller-utils';\nimport { TransactionType } from '@metamask/transaction-controller';\nimport type { TransactionParams } from '@metamask/transaction-controller';\nimport type {\n AuthorizationList,\n TransactionMeta,\n} from '@metamask/transaction-controller';\nimport type { Hex } from '@metamask/utils';\nimport { createModuleLogger } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport { RELAY_DEPOSIT_TYPES, RELAY_POLLING_INTERVAL } from './constants';\nimport { getRelayStatus, submitRelayExecute } from './relay-api';\nimport type {\n RelayExecuteRequest,\n RelayQuote,\n RelayStatusResponse,\n} from './types';\nimport { projectLogger } from '../../logger';\nimport type {\n PayStrategyExecuteRequest,\n TransactionPayControllerMessenger,\n TransactionPayQuote,\n} from '../../types';\nimport {\n getFeatureFlags,\n isEIP7702Chain,\n isRelayExecuteEnabled,\n} from '../../utils/feature-flags';\nimport {\n getLiveTokenBalance,\n normalizeTokenAddress,\n TokenAddressTarget,\n} from '../../utils/token';\nimport {\n collectTransactionIds,\n getTransaction,\n updateTransaction,\n waitForTransactionConfirmed,\n} from '../../utils/transaction';\n\nconst FALLBACK_HASH = '0x0' as Hex;\n\nconst log = createModuleLogger(projectLogger, 'relay-strategy');\n\n/**\n * Submits Relay quotes.\n *\n * @param request - Request object.\n * @returns An object containing the transaction hash if available.\n */\nexport async function submitRelayQuotes(\n request: PayStrategyExecuteRequest<RelayQuote>,\n): Promise<{ transactionHash?: Hex }> {\n log('Executing quotes', request);\n\n const { quotes, messenger, transaction } = request;\n\n let transactionHash: Hex | undefined;\n\n for (const quote of quotes) {\n ({ transactionHash } = await executeSingleQuote(\n quote,\n messenger,\n transaction,\n ));\n }\n\n return { transactionHash };\n}\n\n/**\n * Executes a single Relay quote.\n *\n * @param quote - Relay quote to execute.\n * @param messenger - Controller messenger.\n * @param transaction - Original transaction meta.\n * @returns An object containing the transaction hash if available.\n */\nasync function executeSingleQuote(\n quote: TransactionPayQuote<RelayQuote>,\n messenger: TransactionPayControllerMessenger,\n transaction: TransactionMeta,\n): Promise<{ transactionHash?: Hex }> {\n log('Executing single quote', quote);\n\n updateTransaction(\n {\n transactionId: transaction.id,\n messenger,\n note: 'Remove nonce from skipped transaction',\n },\n (tx) => {\n tx.txParams.nonce = undefined;\n },\n );\n\n await submitTransactions(quote, transaction, messenger);\n\n const targetHash = await waitForRelayCompletion(\n quote.original,\n (sourceHash) => {\n log('Source hash received', sourceHash);\n\n updateTransaction(\n {\n transactionId: transaction.id,\n messenger,\n note: 'Add source hash from Relay status',\n },\n (tx) => {\n tx.metamaskPay ??= {};\n tx.metamaskPay.sourceHash = sourceHash;\n },\n );\n },\n );\n\n log('Relay request completed', targetHash);\n\n updateTransaction(\n {\n transactionId: transaction.id,\n messenger,\n note: 'Intent complete after Relay completion',\n },\n (tx) => {\n tx.isIntentComplete = true;\n },\n );\n\n return { transactionHash: targetHash };\n}\n\n/**\n * Wait for a Relay request to complete.\n *\n * @param quote - Relay quote associated with the request.\n * @param onSourceHash - Called with the source tx hash as soon as it appears.\n * @returns A promise that resolves when the Relay request is complete.\n */\nasync function waitForRelayCompletion(\n quote: RelayQuote,\n onSourceHash?: (hash: Hex) => void,\n): Promise<Hex> {\n const isSameChain =\n quote.details.currencyIn.currency.chainId ===\n quote.details.currencyOut.currency.chainId;\n\n const isSingleDepositStep =\n quote.steps.length === 1 && quote.steps[0].id === 'deposit';\n\n if (isSameChain && !isSingleDepositStep) {\n log('Skipping polling as same chain');\n return FALLBACK_HASH;\n }\n\n const { requestId } = quote.steps[0];\n let sourceHashEmitted = false;\n\n while (true) {\n const status: RelayStatusResponse = await getRelayStatus(requestId);\n\n log('Polled status', status.status, status);\n\n if (!sourceHashEmitted && status.inTxHashes?.length) {\n sourceHashEmitted = true;\n onSourceHash?.(status.inTxHashes[0] as Hex);\n }\n\n if (status.status === 'success') {\n const targetHash =\n (status.txHashes?.slice(-1)[0] as Hex) ?? FALLBACK_HASH;\n return targetHash;\n }\n\n if (['failure', 'refund', 'refunded'].includes(status.status)) {\n throw new Error(`Relay request failed with status: ${status.status}`);\n }\n\n await new Promise((resolve) => setTimeout(resolve, RELAY_POLLING_INTERVAL));\n }\n}\n\n/**\n * Normalize the parameters from a relay quote step to match TransactionParams.\n *\n * @param params - Parameters from a relay quote step.\n * @param messenger - Controller messenger.\n * @returns Normalized transaction parameters.\n */\nfunction normalizeParams(\n params: RelayQuote['steps'][0]['items'][0]['data'],\n messenger: TransactionPayControllerMessenger,\n): TransactionParams {\n const featureFlags = getFeatureFlags(messenger);\n\n return {\n data: params.data,\n from: params.from,\n gas: toHex(params.gas ?? featureFlags.relayFallbackGas.max),\n maxFeePerGas: toHex(params.maxFeePerGas),\n maxPriorityFeePerGas: toHex(params.maxPriorityFeePerGas),\n to: params.to,\n value: toHex(params.value ?? '0'),\n };\n}\n\n/**\n * Validate the source token balance is sufficient for the relay deposit.\n *\n * Reads the live balance from TokenBalancesController and compares it against\n * the quote's required source amount to prevent submitting transactions that\n * will revert on-chain due to insufficient balance.\n *\n * @param quote - Relay quote containing the required source amount.\n * @param messenger - Controller messenger.\n */\nasync function validateSourceBalance(\n quote: TransactionPayQuote<RelayQuote>,\n messenger: TransactionPayControllerMessenger,\n): Promise<void> {\n const { from, sourceChainId, sourceTokenAddress } = quote.request;\n\n const normalizedSourceTokenAddress = normalizeTokenAddress(\n sourceTokenAddress,\n sourceChainId,\n TokenAddressTarget.MetaMask,\n );\n\n let currentBalance: string;\n\n try {\n currentBalance = await getLiveTokenBalance(\n messenger,\n from,\n sourceChainId,\n normalizedSourceTokenAddress,\n );\n } catch (error) {\n throw new Error(\n `Cannot validate payment token balance - ${(error as Error).message}`,\n );\n }\n\n const requiredAmount = new BigNumber(quote.sourceAmount.raw);\n const balance = new BigNumber(currentBalance);\n\n log('Validating source balance', {\n from,\n sourceChainId,\n sourceTokenAddress,\n currentBalance,\n requiredAmount: requiredAmount.toString(10),\n });\n\n if (balance.isLessThan(requiredAmount)) {\n throw new Error(\n `Insufficient source token balance for relay deposit. ` +\n `Required: ${requiredAmount.toString(10)}, ` +\n `Available: ${balance.toString(10)}`,\n );\n }\n}\n\n/**\n * Submit transactions for a relay quote.\n *\n * On EIP-7702 supported chains, combines the source calls via\n * getDelegationTransaction and submits through Relay's /execute endpoint\n * (gasless — Relay's relayer pays origin gas).\n *\n * On other chains, adds the transactions directly via the\n * TransactionController and waits for on-chain confirmation.\n *\n * @param quote - Relay quote.\n * @param transaction - Original transaction meta.\n * @param messenger - Controller messenger.\n * @returns Hash of the last submitted transaction.\n */\nasync function submitTransactions(\n quote: TransactionPayQuote<RelayQuote>,\n transaction: TransactionMeta,\n messenger: TransactionPayControllerMessenger,\n): Promise<Hex> {\n const { steps } = quote.original;\n const params = steps.flatMap((step) => step.items).map((item) => item.data);\n const invalidKind = steps.find((step) => step.kind !== 'transaction')?.kind;\n\n if (invalidKind) {\n throw new Error(`Unsupported step kind: ${invalidKind}`);\n }\n\n await validateSourceBalance(quote, messenger);\n\n const normalizedParams = params.map((singleParams) =>\n normalizeParams(singleParams, messenger),\n );\n\n // For post-quote flows, prepend the original transaction so it gets\n // included in the batch alongside the relay deposit(s).\n // This always results in multiple params, so it takes the batch path.\n const { isPostQuote } = quote.request;\n\n const allParams =\n isPostQuote && transaction.txParams.to\n ? [\n {\n data: transaction.txParams.data as Hex | undefined,\n from: transaction.txParams.from,\n to: transaction.txParams.to,\n value: transaction.txParams.value as Hex | undefined,\n } as TransactionParams,\n ...normalizedParams,\n ]\n : normalizedParams;\n\n const { sourceChainId } = quote.request;\n\n if (\n isRelayExecuteEnabled(messenger) &&\n isEIP7702Chain(messenger, sourceChainId)\n ) {\n return await submitViaRelayExecute(\n quote,\n transaction,\n messenger,\n allParams,\n );\n }\n\n return await submitViaTransactionController(\n quote,\n transaction,\n messenger,\n normalizedParams,\n allParams,\n );\n}\n\n/**\n * Submit source transactions via Relay's /execute endpoint.\n *\n * Combines all source calls (approve + deposit, and optionally the\n * original transaction for post-quote flows) into a single EIP-7702\n * delegation transaction using getDelegationTransaction, then submits\n * it to Relay's /execute endpoint for gasless execution.\n *\n * @param quote - Relay quote.\n * @param transaction - Original transaction meta.\n * @param messenger - Controller messenger.\n * @param allParams - All source transaction params to combine.\n * @returns Fallback hash (actual hash comes from relay status polling).\n */\nasync function submitViaRelayExecute(\n quote: TransactionPayQuote<RelayQuote>,\n transaction: TransactionMeta,\n messenger: TransactionPayControllerMessenger,\n allParams: TransactionParams[],\n): Promise<Hex> {\n const { from, sourceChainId } = quote.request;\n const { requestId } = quote.original.steps[0];\n\n const sourceCallTransaction = {\n ...transaction,\n chainId: sourceChainId,\n nestedTransactions: allParams.map((params) => ({\n data: (params.data ?? '0x') as Hex,\n to: params.to as Hex,\n value: (params.value ?? '0x0') as Hex,\n })),\n } as TransactionMeta;\n\n const delegation = await messenger.call(\n 'TransactionPayController:getDelegationTransaction',\n { transaction: sourceCallTransaction },\n );\n\n log('Delegation result for source calls', delegation);\n\n const executeBody: RelayExecuteRequest = {\n executionKind: 'rawCalls',\n data: {\n chainId: Number(sourceChainId),\n to: delegation.to,\n data: delegation.data,\n value: new BigNumber(delegation.value).toFixed(),\n ...(delegation.authorizationList?.length\n ? {\n authorizationList: delegation.authorizationList.map((auth) => ({\n chainId: Number(auth.chainId),\n address: auth.address,\n nonce: Number(auth.nonce),\n yParity: Number(auth.yParity),\n r: auth.r as Hex,\n s: auth.s as Hex,\n })),\n }\n : {}),\n },\n executionOptions: {\n subsidizeFees: false,\n },\n requestId,\n };\n\n log('Submitting via Relay execute', { executeBody, from });\n\n const result = await submitRelayExecute(messenger, executeBody);\n\n log('Relay execute response', result);\n\n return FALLBACK_HASH;\n}\n\n/**\n * Submit source transactions via the TransactionController.\n *\n * Uses addTransaction for single params or addTransactionBatch for\n * multiple params. Waits for all transactions to be confirmed on-chain.\n *\n * @param quote - Relay quote.\n * @param transaction - Original transaction meta.\n * @param messenger - Controller messenger.\n * @param normalizedParams - Normalized relay-only params (without prepended original tx).\n * @param allParams - All params including any prepended original tx for post-quote flows.\n * @returns Hash of the last submitted transaction.\n */\nasync function submitViaTransactionController(\n quote: TransactionPayQuote<RelayQuote>,\n transaction: TransactionMeta,\n messenger: TransactionPayControllerMessenger,\n normalizedParams: TransactionParams[],\n allParams: TransactionParams[],\n): Promise<Hex> {\n const transactionIds: string[] = [];\n const { from, sourceChainId, sourceTokenAddress } = quote.request;\n const { isPostQuote } = quote.request;\n\n const networkClientId = messenger.call(\n 'NetworkController:findNetworkClientIdByChainId',\n sourceChainId,\n );\n\n log('Adding transactions', {\n normalizedParams: allParams,\n sourceChainId,\n from,\n networkClientId,\n });\n\n const { end } = collectTransactionIds(\n sourceChainId,\n from,\n messenger,\n (transactionId) => {\n transactionIds.push(transactionId);\n\n updateTransaction(\n {\n transactionId: transaction.id,\n messenger,\n note: 'Add required transaction ID from Relay submission',\n },\n (tx) => {\n tx.requiredTransactionIds ??= [];\n tx.requiredTransactionIds.push(transactionId);\n },\n );\n },\n );\n\n let result: { result: Promise<string> } | undefined;\n\n const gasFeeToken = quote.fees.isSourceGasFeeToken\n ? sourceTokenAddress\n : undefined;\n\n const isSameChain =\n quote.original.details.currencyIn.currency.chainId ===\n quote.original.details.currencyOut.currency.chainId;\n\n const authorizationList: AuthorizationList | undefined =\n isSameChain && quote.original.request.authorizationList?.length\n ? quote.original.request.authorizationList.map((a) => ({\n address: a.address,\n chainId: toHex(a.chainId),\n }))\n : undefined;\n\n const { gasLimits } = quote.original.metamask;\n\n if (allParams.length === 1) {\n const transactionParams = {\n ...allParams[0],\n authorizationList,\n gas: toHex(gasLimits[0]),\n };\n\n result = await messenger.call(\n 'TransactionController:addTransaction',\n transactionParams,\n {\n gasFeeToken,\n networkClientId,\n origin: ORIGIN_METAMASK,\n requireApproval: false,\n type: getRelayDepositType(transaction.type),\n },\n );\n } else {\n const gasLimit7702 =\n gasLimits.length === 1 && allParams.length > 1\n ? toHex(gasLimits[0])\n : undefined;\n\n const transactions = allParams.map((singleParams, index) => {\n const gasLimit = gasLimits[index];\n const gas =\n gasLimit === undefined || gasLimit7702 ? undefined : toHex(gasLimit);\n\n return {\n params: {\n data: singleParams.data as Hex,\n gas,\n maxFeePerGas: singleParams.maxFeePerGas as Hex,\n maxPriorityFeePerGas: singleParams.maxPriorityFeePerGas as Hex,\n to: singleParams.to as Hex,\n value: singleParams.value as Hex,\n },\n type: getTransactionType(\n isPostQuote,\n index,\n transaction.type,\n normalizedParams.length,\n ),\n };\n });\n\n await messenger.call('TransactionController:addTransactionBatch', {\n from,\n disable7702: !gasLimit7702,\n disableHook: Boolean(gasLimit7702),\n disableSequential: Boolean(gasLimit7702),\n gasFeeToken,\n gasLimit7702,\n networkClientId,\n origin: ORIGIN_METAMASK,\n overwriteUpgrade: true,\n requireApproval: false,\n transactions,\n });\n }\n\n end();\n\n log('Added transactions', transactionIds);\n\n if (result) {\n const txHash = await result.result;\n log('Submitted transaction', txHash);\n }\n\n await Promise.all(\n transactionIds.map((txId) => waitForTransactionConfirmed(txId, messenger)),\n );\n\n log('All transactions confirmed', transactionIds);\n\n const hash = getTransaction(transactionIds.slice(-1)[0], messenger)?.hash;\n\n return hash as Hex;\n}\n\n/**\n * Determine the transaction type for a given index in the batch.\n *\n * @param isPostQuote - Whether this is a post-quote flow.\n * @param index - Index of the transaction in the batch.\n * @param originalType - Type of the original transaction (used for post-quote index 0).\n * @param relayParamCount - Number of relay-only params (excludes prepended original tx).\n * @returns The transaction type.\n */\nfunction getTransactionType(\n isPostQuote: boolean | undefined,\n index: number,\n originalType: TransactionMeta['type'],\n relayParamCount: number,\n): TransactionMeta['type'] {\n // Post-quote index 0 is the original transaction\n if (isPostQuote && index === 0) {\n return originalType;\n }\n\n // Adjust index for post-quote flows where original tx is prepended\n const relayIndex = isPostQuote ? index - 1 : index;\n\n const depositType = getRelayDepositType(originalType);\n\n // Single relay step is always a deposit (no approval needed)\n if (relayParamCount === 1) {\n return depositType;\n }\n\n return relayIndex === 0 ? TransactionType.tokenMethodApprove : depositType;\n}\n\n/**\n * Get the relay deposit transaction type based on the parent transaction type.\n *\n * @param originalType - Type of the parent transaction.\n * @returns The mapped relay deposit type, or `relayDeposit` as a fallback.\n */\nfunction getRelayDepositType(\n originalType: TransactionMeta['type'],\n): TransactionType {\n return (\n (originalType && RELAY_DEPOSIT_TYPES[originalType]) ??\n TransactionType.relayDeposit\n );\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.cjs","sourceRoot":"","sources":["../../../src/strategy/relay/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Hex } from '@metamask/utils';\n\nexport type RelayQuoteRequest = {\n amount: string;\n authorizationList?: {\n address: Hex;\n chainId: number;\n nonce: number;\n r: Hex;\n s: Hex;\n yParity: number;\n }[];\n destinationChainId: number;\n destinationCurrency: Hex;\n originChainId: number;\n originCurrency: Hex;\n recipient: Hex;\n refundTo?: Hex;\n slippageTolerance?: string;\n tradeType: 'EXACT_INPUT' | 'EXACT_OUTPUT' | 'EXPECTED_OUTPUT';\n txs?: {\n to: Hex;\n data: Hex;\n value: Hex;\n }[];\n user: Hex;\n};\n\nexport type RelayQuote = {\n details: {\n currencyIn: {\n amount: string;\n amountFormatted: string;\n amountUsd: string;\n currency: {\n chainId: number;\n decimals: number;\n };\n };\n currencyOut: {\n amount: string;\n amountFormatted: string;\n amountUsd: string;\n currency: {\n chainId: number;\n decimals: number;\n };\n minimumAmount: string;\n };\n timeEstimate: number;\n totalImpact: {\n usd: string;\n };\n };\n fees: {\n app?: {\n amountUsd: string;\n };\n relayer: {\n amountUsd: string;\n };\n subsidized?: {\n amount: string;\n amountFormatted: string;\n amountUsd: string;\n currency: {\n address: Hex;\n chainId: number;\n decimals: number;\n };\n minimumAmount: string;\n };\n };\n metamask: {\n gasLimits: number[];\n isMaxGasStation?: boolean;\n };\n request: RelayQuoteRequest;\n steps: {\n id: string;\n items: {\n check: {\n endpoint: string;\n method: 'GET' | 'POST';\n };\n data: {\n chainId: number;\n data: Hex;\n from: Hex;\n gas?: string;\n maxFeePerGas: string;\n maxPriorityFeePerGas: string;\n to: Hex;\n value?: string;\n };\n status: 'complete' | 'incomplete';\n }[];\n kind: 'transaction';\n requestId: string;\n }[];\n};\n\nexport type RelayStatus =\n | 'waiting'\n | 'pending'\n | 'submitted'\n | 'success'\n | 'delayed'\n | 'refunded'\n | 'refund'\n | 'failure';\n\nexport type RelayStatusResponse = {\n status: RelayStatus;\n inTxHashes: string[];\n txHashes: string[];\n updatedAt: number;\n originChainId: number;\n destinationChainId: number;\n};\n"]}
|
|
1
|
+
{"version":3,"file":"types.cjs","sourceRoot":"","sources":["../../../src/strategy/relay/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { Hex } from '@metamask/utils';\n\nexport type RelayQuoteRequest = {\n amount: string;\n authorizationList?: {\n address: Hex;\n chainId: number;\n nonce: number;\n r: Hex;\n s: Hex;\n yParity: number;\n }[];\n destinationChainId: number;\n destinationCurrency: Hex;\n originChainId: number;\n originCurrency: Hex;\n originGasOverhead?: string;\n recipient: Hex;\n refundTo?: Hex;\n slippageTolerance?: string;\n tradeType: 'EXACT_INPUT' | 'EXACT_OUTPUT' | 'EXPECTED_OUTPUT';\n txs?: {\n to: Hex;\n data: Hex;\n value: Hex;\n }[];\n user: Hex;\n};\n\nexport type RelayQuote = {\n details: {\n currencyIn: {\n amount: string;\n amountFormatted: string;\n amountUsd: string;\n currency: {\n chainId: number;\n decimals: number;\n };\n };\n currencyOut: {\n amount: string;\n amountFormatted: string;\n amountUsd: string;\n currency: {\n chainId: number;\n decimals: number;\n };\n minimumAmount: string;\n };\n timeEstimate: number;\n totalImpact: {\n usd: string;\n };\n };\n fees: {\n app?: {\n amountUsd: string;\n };\n relayer: {\n amountUsd: string;\n };\n subsidized?: {\n amount: string;\n amountFormatted: string;\n amountUsd: string;\n currency: {\n address: Hex;\n chainId: number;\n decimals: number;\n };\n minimumAmount: string;\n };\n };\n metamask: {\n gasLimits: number[];\n isMaxGasStation?: boolean;\n };\n request: RelayQuoteRequest;\n steps: {\n id: string;\n items: {\n check: {\n endpoint: string;\n method: 'GET' | 'POST';\n };\n data: {\n chainId: number;\n data: Hex;\n from: Hex;\n gas?: string;\n maxFeePerGas: string;\n maxPriorityFeePerGas: string;\n to: Hex;\n value?: string;\n };\n status: 'complete' | 'incomplete';\n }[];\n kind: 'transaction';\n requestId: string;\n }[];\n};\n\nexport type RelayExecuteRequest = {\n executionKind: 'rawCalls';\n data: {\n chainId: number;\n to: Hex;\n data: Hex;\n value: string;\n authorizationList?: {\n chainId: number;\n address: Hex;\n nonce: number;\n yParity: number;\n r: Hex;\n s: Hex;\n }[];\n };\n executionOptions: {\n referrer?: string;\n subsidizeFees: boolean;\n };\n requestId?: string;\n};\n\nexport type RelayExecuteResponse = {\n message: string;\n requestId: string;\n};\n\nexport type RelayStatus =\n | 'waiting'\n | 'pending'\n | 'submitted'\n | 'success'\n | 'delayed'\n | 'refunded'\n | 'refund'\n | 'failure';\n\nexport type RelayStatusResponse = {\n status: RelayStatus;\n inTxHashes: string[];\n txHashes: string[];\n updatedAt: number;\n originChainId: number;\n destinationChainId: number;\n};\n"]}
|