@metamask-previews/transaction-pay-controller 30.3.1-preview-35f016caa → 30.3.2-preview-01e391e
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 +16 -1
- package/dist/strategy/across/AcrossStrategy.d.ts.map +1 -1
- package/dist/strategy/across/AcrossStrategy.js +3 -3
- package/dist/strategy/across/AcrossStrategy.js.map +1 -1
- package/dist/strategy/across/across-actions.js +2 -2
- package/dist/strategy/across/across-actions.js.map +1 -1
- package/dist/strategy/across/across-quotes.js +1 -1
- package/dist/strategy/across/across-quotes.js.map +1 -1
- package/dist/strategy/across/across-submit.d.ts.map +1 -1
- package/dist/strategy/across/across-submit.js +4 -2
- package/dist/strategy/across/across-submit.js.map +1 -1
- package/dist/strategy/across/perps.d.ts +4 -23
- package/dist/strategy/across/perps.d.ts.map +1 -1
- package/dist/strategy/across/perps.js +5 -42
- package/dist/strategy/across/perps.js.map +1 -1
- package/dist/strategy/relay/relay-quotes.d.ts.map +1 -1
- package/dist/strategy/relay/relay-quotes.js +42 -84
- package/dist/strategy/relay/relay-quotes.js.map +1 -1
- package/dist/strategy/server/perps.d.ts +4 -24
- package/dist/strategy/server/perps.d.ts.map +1 -1
- package/dist/strategy/server/perps.js +7 -66
- package/dist/strategy/server/perps.js.map +1 -1
- package/dist/strategy/server/server-quotes.d.ts.map +1 -1
- package/dist/strategy/server/server-quotes.js +39 -70
- package/dist/strategy/server/server-quotes.js.map +1 -1
- package/dist/utils/perps.d.ts +56 -0
- package/dist/utils/perps.d.ts.map +1 -0
- package/dist/utils/perps.js +112 -0
- package/dist/utils/perps.js.map +1 -0
- package/dist/utils/post-quote-payment-override.d.ts +42 -0
- package/dist/utils/post-quote-payment-override.d.ts.map +1 -0
- package/dist/utils/post-quote-payment-override.js +55 -0
- package/dist/utils/post-quote-payment-override.js.map +1 -0
- package/dist/utils/quote-recipient.d.ts +22 -0
- package/dist/utils/quote-recipient.d.ts.map +1 -0
- package/dist/utils/quote-recipient.js +41 -0
- package/dist/utils/quote-recipient.js.map +1 -0
- package/dist/utils/second-leg.d.ts +1 -21
- package/dist/utils/second-leg.d.ts.map +1 -1
- package/dist/utils/second-leg.js +0 -36
- package/dist/utils/second-leg.js.map +1 -1
- package/dist/utils/source-amounts.d.ts.map +1 -1
- package/dist/utils/source-amounts.js +8 -8
- package/dist/utils/source-amounts.js.map +1 -1
- package/package.json +4 -4
|
@@ -1,28 +1,8 @@
|
|
|
1
1
|
import type { BatchTransactionParams, TransactionMeta } from '@metamask/transaction-controller';
|
|
2
2
|
import type { Hex } from '@metamask/utils';
|
|
3
|
-
import type {
|
|
3
|
+
import type { TransactionPayControllerMessenger } from '../types.js';
|
|
4
4
|
export declare const SECOND_LEG_ERROR_PREFIX = "Second leg: ";
|
|
5
5
|
export type SecondLegCallsBuilder = (sourceAmountRaw: string) => Promise<BatchTransactionParams[]>;
|
|
6
|
-
/**
|
|
7
|
-
* Derives the quote recipient for non-atomic flows, where the second leg runs
|
|
8
|
-
* after settlement so funds must land directly on the account submitting that
|
|
9
|
-
* leg.
|
|
10
|
-
*
|
|
11
|
-
* Post-quote flows (e.g. Perps/Predict withdraw to Money Account) ask the
|
|
12
|
-
* client `getPaymentOverrideData` callback, which knows the Money Account
|
|
13
|
-
* address that cannot be derived from the request. Non-post-quote flows (e.g.
|
|
14
|
-
* max-amount Money Account deposit) use the parent transaction's own `from`,
|
|
15
|
-
* which is the Money Account rather than the funding EOA in `request.from`.
|
|
16
|
-
*
|
|
17
|
-
* Atomic flows return `undefined`: the provider executes the calls as part of
|
|
18
|
-
* the quote, so the funds must land on the account running them.
|
|
19
|
-
*
|
|
20
|
-
* @param transaction - Transaction metadata.
|
|
21
|
-
* @param request - Quote request.
|
|
22
|
-
* @param messenger - Controller messenger.
|
|
23
|
-
* @returns The recipient address, or `undefined` for atomic flows.
|
|
24
|
-
*/
|
|
25
|
-
export declare function resolveNonAtomicRecipient(transaction: TransactionMeta, request: QuoteRequest, messenger: TransactionPayControllerMessenger): Promise<Hex | undefined>;
|
|
26
6
|
/**
|
|
27
7
|
* Submits the second leg of a non-atomic flow: the calls that the quote could
|
|
28
8
|
* not execute itself, run on the target chain once the first leg has settled.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"second-leg.d.ts","sourceRoot":"","sources":["../../src/utils/second-leg.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,sBAAsB,EAEtB,eAAe,EAChB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAI3C,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"second-leg.d.ts","sourceRoot":"","sources":["../../src/utils/second-leg.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,sBAAsB,EAEtB,eAAe,EAChB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAI3C,OAAO,KAAK,EAAE,iCAAiC,EAAE,MAAM,aAAa,CAAC;AAYrE,eAAO,MAAM,uBAAuB,iBAAiB,CAAC;AAItD,MAAM,MAAM,qBAAqB,GAAG,CAClC,eAAe,EAAE,MAAM,KACpB,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAAC;AAEvC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAsB,eAAe,CAAC,EACpC,OAAO,EACP,IAAI,EACJ,QAAQ,EACR,SAAS,EACT,IAAI,EACJ,cAAc,EACd,SAAgB,EAChB,YAAY,EACZ,WAAW,GACZ,EAAE;IACD,OAAO,EAAE,GAAG,CAAC;IACb,IAAI,EAAE,GAAG,CAAC;IACV,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IACjC,SAAS,EAAE,iCAAiC,CAAC;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,GAAG,GAAG,SAAS,CAAC;IAChC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,GAAG,CAAC;IAClB,WAAW,EAAE,eAAe,CAAC;CAC9B,GAAG,OAAO,CAAC;IAAE,eAAe,CAAC,EAAE,GAAG,CAAA;CAAE,CAAC,CAoCrC"}
|
package/dist/utils/second-leg.js
CHANGED
|
@@ -8,42 +8,6 @@ import { getNetworkClientId } from './provider.js';
|
|
|
8
8
|
import { collectTransactionIds, getTransaction, getTransferredAmountFromTxHash, updateTransaction, waitForTransactionConfirmed, } from './transaction.js';
|
|
9
9
|
export const SECOND_LEG_ERROR_PREFIX = 'Second leg: ';
|
|
10
10
|
const log = createModuleLogger(projectLogger, 'second-leg');
|
|
11
|
-
/**
|
|
12
|
-
* Derives the quote recipient for non-atomic flows, where the second leg runs
|
|
13
|
-
* after settlement so funds must land directly on the account submitting that
|
|
14
|
-
* leg.
|
|
15
|
-
*
|
|
16
|
-
* Post-quote flows (e.g. Perps/Predict withdraw to Money Account) ask the
|
|
17
|
-
* client `getPaymentOverrideData` callback, which knows the Money Account
|
|
18
|
-
* address that cannot be derived from the request. Non-post-quote flows (e.g.
|
|
19
|
-
* max-amount Money Account deposit) use the parent transaction's own `from`,
|
|
20
|
-
* which is the Money Account rather than the funding EOA in `request.from`.
|
|
21
|
-
*
|
|
22
|
-
* Atomic flows return `undefined`: the provider executes the calls as part of
|
|
23
|
-
* the quote, so the funds must land on the account running them.
|
|
24
|
-
*
|
|
25
|
-
* @param transaction - Transaction metadata.
|
|
26
|
-
* @param request - Quote request.
|
|
27
|
-
* @param messenger - Controller messenger.
|
|
28
|
-
* @returns The recipient address, or `undefined` for atomic flows.
|
|
29
|
-
*/
|
|
30
|
-
export async function resolveNonAtomicRecipient(transaction, request, messenger) {
|
|
31
|
-
if (request.atomic !== false) {
|
|
32
|
-
return undefined;
|
|
33
|
-
}
|
|
34
|
-
if (!request.isPostQuote) {
|
|
35
|
-
return transaction.txParams.from;
|
|
36
|
-
}
|
|
37
|
-
const { transactionData: transactionDataList } = messenger.call('TransactionPayController:getState');
|
|
38
|
-
const transactionData = transactionDataList[transaction.id];
|
|
39
|
-
const amountHuman = transactionData?.tokens?.[0]?.amountHuman ?? '0';
|
|
40
|
-
const { recipient } = await messenger.call('TransactionPayController:getPaymentOverrideData', {
|
|
41
|
-
amount: amountHuman,
|
|
42
|
-
transaction,
|
|
43
|
-
transactionData,
|
|
44
|
-
});
|
|
45
|
-
return recipient;
|
|
46
|
-
}
|
|
47
11
|
/**
|
|
48
12
|
* Submits the second leg of a non-atomic flow: the calls that the quote could
|
|
49
13
|
* not execute itself, run on the target chain once the first leg has settled.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"second-leg.js","sourceRoot":"","sources":["../../src/utils/second-leg.ts"],"names":[],"mappings":"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","sourcesContent":["import { ORIGIN_METAMASK } from '@metamask/controller-utils';\nimport type {\n BatchTransactionParams,\n NestedTransactionMetadata,\n TransactionMeta,\n} from '@metamask/transaction-controller';\nimport { TransactionType } from '@metamask/transaction-controller';\nimport type { Hex } from '@metamask/utils';\nimport { createModuleLogger } from '@metamask/utils';\n\nimport { projectLogger } from '../logger.js';\nimport type {\n QuoteRequest,\n TransactionPayControllerMessenger,\n} from '../types.js';\nimport { withChompRecovery } from './chomp.js';\nimport { prefixError } from './error-prefix.js';\nimport { getNetworkClientId } from './provider.js';\nimport {\n collectTransactionIds,\n getTransaction,\n getTransferredAmountFromTxHash,\n updateTransaction,\n waitForTransactionConfirmed,\n} from './transaction.js';\n\nexport const SECOND_LEG_ERROR_PREFIX = 'Second leg: ';\n\nconst log = createModuleLogger(projectLogger, 'second-leg');\n\nexport type SecondLegCallsBuilder = (\n sourceAmountRaw: string,\n) => Promise<BatchTransactionParams[]>;\n\n/**\n * Derives the quote recipient for non-atomic flows, where the second leg runs\n * after settlement so funds must land directly on the account submitting that\n * leg.\n *\n * Post-quote flows (e.g. Perps/Predict withdraw to Money Account) ask the\n * client `getPaymentOverrideData` callback, which knows the Money Account\n * address that cannot be derived from the request. Non-post-quote flows (e.g.\n * max-amount Money Account deposit) use the parent transaction's own `from`,\n * which is the Money Account rather than the funding EOA in `request.from`.\n *\n * Atomic flows return `undefined`: the provider executes the calls as part of\n * the quote, so the funds must land on the account running them.\n *\n * @param transaction - Transaction metadata.\n * @param request - Quote request.\n * @param messenger - Controller messenger.\n * @returns The recipient address, or `undefined` for atomic flows.\n */\nexport async function resolveNonAtomicRecipient(\n transaction: TransactionMeta,\n request: QuoteRequest,\n messenger: TransactionPayControllerMessenger,\n): Promise<Hex | undefined> {\n if (request.atomic !== false) {\n return undefined;\n }\n\n if (!request.isPostQuote) {\n return transaction.txParams.from as Hex;\n }\n\n const { transactionData: transactionDataList } = messenger.call(\n 'TransactionPayController:getState',\n );\n\n const transactionData = transactionDataList[transaction.id];\n const amountHuman = transactionData?.tokens?.[0]?.amountHuman ?? '0';\n\n const { recipient } = await messenger.call(\n 'TransactionPayController:getPaymentOverrideData',\n {\n amount: amountHuman,\n transaction,\n transactionData,\n },\n );\n\n return recipient;\n}\n\n/**\n * Submits the second leg of a non-atomic flow: the calls that the quote could\n * not execute itself, run on the target chain once the first leg has settled.\n *\n * The single entrypoint for every strategy. The caller decides the chain, the\n * submitting account, the calls, and whether gas is sponsored, while\n * transaction-type-specific handling (e.g. {@link withChompRecovery}) is\n * applied here so it holds whichever strategy settled the funds.\n *\n * The amount spent is read from the settlement transaction's transfer logs\n * rather than trusted from a quote or provider, since slippage and fees mean\n * the landed amount is only known after settlement. Throws when there is no\n * settlement hash or no matching transfer, rather than guessing.\n *\n * @param options - Submit options.\n * @param options.chainId - Chain the funds settled on and the batch is\n * submitted on.\n * @param options.from - Account that received the settled funds and submits\n * the batch.\n * @param options.getCalls - Builds the batch for the settled amount. Derived\n * from the parent transaction's nested calls when omitted.\n * @param options.messenger - Controller messenger.\n * @param options.note - Note recorded against the parent transaction update.\n * @param options.settlementHash - Hash of the transaction that delivered the\n * funds to `from`.\n * @param options.sponsored - Whether gas is sponsored. Defaults to `true`,\n * since second legs run on chains where MetaMask sponsors gas; submission\n * fails rather than silently charging the user when sponsorship is refused.\n * @param options.tokenAddress - Token that settled.\n * @param options.transaction - Parent transaction meta.\n * @returns Hash of the final submitted child transaction, if available.\n */\nexport async function submitSecondLeg({\n chainId,\n from,\n getCalls,\n messenger,\n note,\n settlementHash,\n sponsored = true,\n tokenAddress,\n transaction,\n}: {\n chainId: Hex;\n from: Hex;\n getCalls?: SecondLegCallsBuilder;\n messenger: TransactionPayControllerMessenger;\n note?: string;\n settlementHash: Hex | undefined;\n sponsored?: boolean;\n tokenAddress: Hex;\n transaction: TransactionMeta;\n}): Promise<{ transactionHash?: Hex }> {\n const { amountRaw: sourceAmountRaw, fromBlock } = await resolveSettledAmount({\n chainId,\n messenger,\n recipient: from,\n settlementHash,\n tokenAddress,\n });\n\n // Resolved up front so the parent transaction reflects the settled amount\n // even when type-specific handling short-circuits the submission.\n const nestedTransactions = await resolveSecondLegCalls({\n getCalls,\n messenger,\n note,\n sourceAmountRaw,\n transaction,\n });\n\n try {\n return await withChompRecovery(\n { from, fromBlock, messenger, sourceAmountRaw, transaction },\n async () =>\n await submitBatch({\n chainId,\n from,\n messenger,\n nestedTransactions,\n sourceAmountRaw,\n sponsored,\n transaction,\n }),\n );\n } catch (error) {\n throw prefixError(error, SECOND_LEG_ERROR_PREFIX);\n }\n}\n\n/**\n * Resolves the calls to submit as the second leg.\n *\n * Callers with no calls on the parent transaction (e.g. withdraw flows) supply\n * `getCalls` to build the batch for the settled amount. Otherwise the parent\n * transaction's own nested calls are re-encoded for the settled amount via\n * `getAmountData`, and the parent transaction is mutated so its stored calls\n * and `requiredAssets` reflect that amount.\n *\n * @param options - Resolution options.\n * @param options.getCalls - Builds the batch for the settled amount, used\n * as-is when provided.\n * @param options.messenger - Controller messenger.\n * @param options.note - Note recorded against the parent transaction update.\n * @param options.sourceAmountRaw - Settled amount in raw units.\n * @param options.transaction - Parent transaction meta.\n * @returns Nested transactions to submit as the second leg.\n */\nasync function resolveSecondLegCalls({\n getCalls,\n messenger,\n note = 'Second leg: update amount',\n sourceAmountRaw,\n transaction,\n}: {\n getCalls?: SecondLegCallsBuilder;\n messenger: TransactionPayControllerMessenger;\n note?: string;\n sourceAmountRaw: string;\n transaction: TransactionMeta;\n}): Promise<NestedTransactionMetadata[]> {\n if (getCalls) {\n const calls = await getCalls(sourceAmountRaw);\n\n if (!calls.length) {\n throw new Error('Missing second leg calls');\n }\n\n return calls;\n }\n\n const transactionId = transaction.id;\n\n const updatedTransaction =\n getTransaction(transactionId, messenger) ?? transaction;\n\n const { updates } = await messenger.call(\n 'TransactionPayController:getAmountData',\n {\n amount: sourceAmountRaw,\n transaction: updatedTransaction,\n },\n );\n\n if (!updates.length) {\n throw new Error('No amount updates');\n }\n\n const nestedTransactions = updatedTransaction.nestedTransactions?.map(\n (nestedTransaction) => ({ ...nestedTransaction }),\n );\n\n if (!nestedTransactions?.length) {\n throw new Error('Missing nested transactions');\n }\n\n for (const { nestedTransactionIndex, data } of updates) {\n if (nestedTransactions[nestedTransactionIndex]) {\n nestedTransactions[nestedTransactionIndex].data = data;\n }\n }\n\n updateTransaction({ transactionId, messenger, note }, (tx) => {\n for (const { nestedTransactionIndex, data } of updates) {\n if (tx.nestedTransactions?.[nestedTransactionIndex]) {\n tx.nestedTransactions[nestedTransactionIndex].data = data;\n }\n }\n\n if (tx.requiredAssets?.[0]) {\n tx.requiredAssets[0].amount = `0x${BigInt(sourceAmountRaw).toString(16)}`;\n }\n });\n\n return nestedTransactions;\n}\n\n/**\n * Resolves the amount that settled on `recipient`, read from the settlement\n * transaction's transfer logs, along with the block it settled in.\n *\n * @param options - Resolution options.\n * @param options.chainId - Chain the funds settled on.\n * @param options.messenger - Controller messenger.\n * @param options.recipient - Account that received the settled funds.\n * @param options.settlementHash - Hash of the transaction that delivered the\n * funds.\n * @param options.tokenAddress - Token that settled.\n * @returns The settled amount in raw units, and the settlement block when\n * available.\n */\nasync function resolveSettledAmount({\n chainId,\n messenger,\n recipient,\n settlementHash,\n tokenAddress,\n}: {\n chainId: Hex;\n messenger: TransactionPayControllerMessenger;\n recipient: Hex;\n settlementHash: Hex | undefined;\n tokenAddress: Hex;\n}): Promise<{ amountRaw: string; fromBlock: Hex | undefined }> {\n if (!settlementHash) {\n throw new Error('Missing settlement hash');\n }\n\n const { amountRaw, blockNumber } = await getTransferredAmountFromTxHash({\n chainId,\n messenger,\n tokenAddress,\n txHash: settlementHash,\n walletAddress: recipient,\n });\n\n if (!amountRaw) {\n throw new Error(\n `Could not determine settled amount from transaction ${settlementHash}`,\n );\n }\n\n log('Resolved settled amount', {\n amountRaw,\n blockNumber,\n chainId,\n recipient,\n settlementHash,\n });\n\n return { amountRaw, fromBlock: blockNumber };\n}\n\n/**\n * Submits the second-leg batch and waits for every child transaction to\n * confirm.\n *\n * @param options - Submit options.\n * @param options.chainId - Chain to submit the batch on.\n * @param options.from - Account submitting the batch.\n * @param options.messenger - Controller messenger.\n * @param options.nestedTransactions - Calls to submit.\n * @param options.sourceAmountRaw - Settled amount in raw units, for logging.\n * @param options.sponsored - Whether gas is sponsored.\n * @param options.transaction - Parent transaction meta.\n * @returns Hash of the final submitted child transaction.\n */\nasync function submitBatch({\n chainId,\n from,\n messenger,\n nestedTransactions,\n sourceAmountRaw,\n sponsored,\n transaction,\n}: {\n chainId: Hex;\n from: Hex;\n messenger: TransactionPayControllerMessenger;\n nestedTransactions: NestedTransactionMetadata[];\n sourceAmountRaw: string;\n sponsored: boolean;\n transaction: TransactionMeta;\n}): Promise<{ transactionHash: Hex }> {\n const transactionId = transaction.id;\n const networkClientId = getNetworkClientId(messenger, chainId);\n const transactionIds: string[] = [];\n\n const { end } = collectTransactionIds(chainId, from, messenger, (id) => {\n transactionIds.push(id);\n\n updateTransaction(\n {\n transactionId,\n messenger,\n note: 'Add required transaction ID from second leg submission',\n },\n (tx) => {\n tx.requiredTransactionIds ??= [];\n tx.requiredTransactionIds.push(id);\n },\n );\n });\n\n const logContext = {\n chainId,\n from,\n nestedTransactionCount: nestedTransactions.length,\n networkClientId,\n sourceAmountRaw,\n transactionId,\n };\n\n log('Submitting second leg', logContext);\n\n try {\n await messenger.call('TransactionController:addTransactionBatch', {\n disableHook: true,\n disableSequential: true,\n disableUpgrade: true,\n from,\n isGasFeeSponsored: sponsored,\n isInternal: true,\n networkClientId,\n origin: ORIGIN_METAMASK,\n requireApproval: false,\n skipInitialGasEstimate: true,\n transactions: nestedTransactions.map((nestedTransaction, index) => ({\n params: {\n data: nestedTransaction.data,\n to: nestedTransaction.to,\n value: nestedTransaction.value ?? '0x0',\n },\n type:\n index === 0\n ? (nestedTransaction.type ?? TransactionType.tokenMethodApprove)\n : TransactionType.contractInteraction,\n })),\n });\n } finally {\n end();\n }\n\n log('Submitted second leg', { ...logContext, transactionIds });\n\n if (!transactionIds.length) {\n throw new Error('No transactions submitted');\n }\n\n await Promise.all(\n transactionIds.map((id) => waitForTransactionConfirmed(id, messenger)),\n );\n\n const hash = getTransaction(transactionIds.slice(-1)[0], messenger)?.hash;\n\n if (!hash) {\n throw new Error('Missing transaction hash');\n }\n\n log('Confirmed second leg', { ...logContext, hash, transactionIds });\n\n return { transactionHash: hash as Hex };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"second-leg.js","sourceRoot":"","sources":["../../src/utils/second-leg.ts"],"names":[],"mappings":"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","sourcesContent":["import { ORIGIN_METAMASK } from '@metamask/controller-utils';\nimport type {\n BatchTransactionParams,\n NestedTransactionMetadata,\n TransactionMeta,\n} from '@metamask/transaction-controller';\nimport { TransactionType } from '@metamask/transaction-controller';\nimport type { Hex } from '@metamask/utils';\nimport { createModuleLogger } from '@metamask/utils';\n\nimport { projectLogger } from '../logger.js';\nimport type { TransactionPayControllerMessenger } from '../types.js';\nimport { withChompRecovery } from './chomp.js';\nimport { prefixError } from './error-prefix.js';\nimport { getNetworkClientId } from './provider.js';\nimport {\n collectTransactionIds,\n getTransaction,\n getTransferredAmountFromTxHash,\n updateTransaction,\n waitForTransactionConfirmed,\n} from './transaction.js';\n\nexport const SECOND_LEG_ERROR_PREFIX = 'Second leg: ';\n\nconst log = createModuleLogger(projectLogger, 'second-leg');\n\nexport type SecondLegCallsBuilder = (\n sourceAmountRaw: string,\n) => Promise<BatchTransactionParams[]>;\n\n/**\n * Submits the second leg of a non-atomic flow: the calls that the quote could\n * not execute itself, run on the target chain once the first leg has settled.\n *\n * The single entrypoint for every strategy. The caller decides the chain, the\n * submitting account, the calls, and whether gas is sponsored, while\n * transaction-type-specific handling (e.g. {@link withChompRecovery}) is\n * applied here so it holds whichever strategy settled the funds.\n *\n * The amount spent is read from the settlement transaction's transfer logs\n * rather than trusted from a quote or provider, since slippage and fees mean\n * the landed amount is only known after settlement. Throws when there is no\n * settlement hash or no matching transfer, rather than guessing.\n *\n * @param options - Submit options.\n * @param options.chainId - Chain the funds settled on and the batch is\n * submitted on.\n * @param options.from - Account that received the settled funds and submits\n * the batch.\n * @param options.getCalls - Builds the batch for the settled amount. Derived\n * from the parent transaction's nested calls when omitted.\n * @param options.messenger - Controller messenger.\n * @param options.note - Note recorded against the parent transaction update.\n * @param options.settlementHash - Hash of the transaction that delivered the\n * funds to `from`.\n * @param options.sponsored - Whether gas is sponsored. Defaults to `true`,\n * since second legs run on chains where MetaMask sponsors gas; submission\n * fails rather than silently charging the user when sponsorship is refused.\n * @param options.tokenAddress - Token that settled.\n * @param options.transaction - Parent transaction meta.\n * @returns Hash of the final submitted child transaction, if available.\n */\nexport async function submitSecondLeg({\n chainId,\n from,\n getCalls,\n messenger,\n note,\n settlementHash,\n sponsored = true,\n tokenAddress,\n transaction,\n}: {\n chainId: Hex;\n from: Hex;\n getCalls?: SecondLegCallsBuilder;\n messenger: TransactionPayControllerMessenger;\n note?: string;\n settlementHash: Hex | undefined;\n sponsored?: boolean;\n tokenAddress: Hex;\n transaction: TransactionMeta;\n}): Promise<{ transactionHash?: Hex }> {\n const { amountRaw: sourceAmountRaw, fromBlock } = await resolveSettledAmount({\n chainId,\n messenger,\n recipient: from,\n settlementHash,\n tokenAddress,\n });\n\n // Resolved up front so the parent transaction reflects the settled amount\n // even when type-specific handling short-circuits the submission.\n const nestedTransactions = await resolveSecondLegCalls({\n getCalls,\n messenger,\n note,\n sourceAmountRaw,\n transaction,\n });\n\n try {\n return await withChompRecovery(\n { from, fromBlock, messenger, sourceAmountRaw, transaction },\n async () =>\n await submitBatch({\n chainId,\n from,\n messenger,\n nestedTransactions,\n sourceAmountRaw,\n sponsored,\n transaction,\n }),\n );\n } catch (error) {\n throw prefixError(error, SECOND_LEG_ERROR_PREFIX);\n }\n}\n\n/**\n * Resolves the calls to submit as the second leg.\n *\n * Callers with no calls on the parent transaction (e.g. withdraw flows) supply\n * `getCalls` to build the batch for the settled amount. Otherwise the parent\n * transaction's own nested calls are re-encoded for the settled amount via\n * `getAmountData`, and the parent transaction is mutated so its stored calls\n * and `requiredAssets` reflect that amount.\n *\n * @param options - Resolution options.\n * @param options.getCalls - Builds the batch for the settled amount, used\n * as-is when provided.\n * @param options.messenger - Controller messenger.\n * @param options.note - Note recorded against the parent transaction update.\n * @param options.sourceAmountRaw - Settled amount in raw units.\n * @param options.transaction - Parent transaction meta.\n * @returns Nested transactions to submit as the second leg.\n */\nasync function resolveSecondLegCalls({\n getCalls,\n messenger,\n note = 'Second leg: update amount',\n sourceAmountRaw,\n transaction,\n}: {\n getCalls?: SecondLegCallsBuilder;\n messenger: TransactionPayControllerMessenger;\n note?: string;\n sourceAmountRaw: string;\n transaction: TransactionMeta;\n}): Promise<NestedTransactionMetadata[]> {\n if (getCalls) {\n const calls = await getCalls(sourceAmountRaw);\n\n if (!calls.length) {\n throw new Error('Missing second leg calls');\n }\n\n return calls;\n }\n\n const transactionId = transaction.id;\n\n const updatedTransaction =\n getTransaction(transactionId, messenger) ?? transaction;\n\n const { updates } = await messenger.call(\n 'TransactionPayController:getAmountData',\n {\n amount: sourceAmountRaw,\n transaction: updatedTransaction,\n },\n );\n\n if (!updates.length) {\n throw new Error('No amount updates');\n }\n\n const nestedTransactions = updatedTransaction.nestedTransactions?.map(\n (nestedTransaction) => ({ ...nestedTransaction }),\n );\n\n if (!nestedTransactions?.length) {\n throw new Error('Missing nested transactions');\n }\n\n for (const { nestedTransactionIndex, data } of updates) {\n if (nestedTransactions[nestedTransactionIndex]) {\n nestedTransactions[nestedTransactionIndex].data = data;\n }\n }\n\n updateTransaction({ transactionId, messenger, note }, (tx) => {\n for (const { nestedTransactionIndex, data } of updates) {\n if (tx.nestedTransactions?.[nestedTransactionIndex]) {\n tx.nestedTransactions[nestedTransactionIndex].data = data;\n }\n }\n\n if (tx.requiredAssets?.[0]) {\n tx.requiredAssets[0].amount = `0x${BigInt(sourceAmountRaw).toString(16)}`;\n }\n });\n\n return nestedTransactions;\n}\n\n/**\n * Resolves the amount that settled on `recipient`, read from the settlement\n * transaction's transfer logs, along with the block it settled in.\n *\n * @param options - Resolution options.\n * @param options.chainId - Chain the funds settled on.\n * @param options.messenger - Controller messenger.\n * @param options.recipient - Account that received the settled funds.\n * @param options.settlementHash - Hash of the transaction that delivered the\n * funds.\n * @param options.tokenAddress - Token that settled.\n * @returns The settled amount in raw units, and the settlement block when\n * available.\n */\nasync function resolveSettledAmount({\n chainId,\n messenger,\n recipient,\n settlementHash,\n tokenAddress,\n}: {\n chainId: Hex;\n messenger: TransactionPayControllerMessenger;\n recipient: Hex;\n settlementHash: Hex | undefined;\n tokenAddress: Hex;\n}): Promise<{ amountRaw: string; fromBlock: Hex | undefined }> {\n if (!settlementHash) {\n throw new Error('Missing settlement hash');\n }\n\n const { amountRaw, blockNumber } = await getTransferredAmountFromTxHash({\n chainId,\n messenger,\n tokenAddress,\n txHash: settlementHash,\n walletAddress: recipient,\n });\n\n if (!amountRaw) {\n throw new Error(\n `Could not determine settled amount from transaction ${settlementHash}`,\n );\n }\n\n log('Resolved settled amount', {\n amountRaw,\n blockNumber,\n chainId,\n recipient,\n settlementHash,\n });\n\n return { amountRaw, fromBlock: blockNumber };\n}\n\n/**\n * Submits the second-leg batch and waits for every child transaction to\n * confirm.\n *\n * @param options - Submit options.\n * @param options.chainId - Chain to submit the batch on.\n * @param options.from - Account submitting the batch.\n * @param options.messenger - Controller messenger.\n * @param options.nestedTransactions - Calls to submit.\n * @param options.sourceAmountRaw - Settled amount in raw units, for logging.\n * @param options.sponsored - Whether gas is sponsored.\n * @param options.transaction - Parent transaction meta.\n * @returns Hash of the final submitted child transaction.\n */\nasync function submitBatch({\n chainId,\n from,\n messenger,\n nestedTransactions,\n sourceAmountRaw,\n sponsored,\n transaction,\n}: {\n chainId: Hex;\n from: Hex;\n messenger: TransactionPayControllerMessenger;\n nestedTransactions: NestedTransactionMetadata[];\n sourceAmountRaw: string;\n sponsored: boolean;\n transaction: TransactionMeta;\n}): Promise<{ transactionHash: Hex }> {\n const transactionId = transaction.id;\n const networkClientId = getNetworkClientId(messenger, chainId);\n const transactionIds: string[] = [];\n\n const { end } = collectTransactionIds(chainId, from, messenger, (id) => {\n transactionIds.push(id);\n\n updateTransaction(\n {\n transactionId,\n messenger,\n note: 'Add required transaction ID from second leg submission',\n },\n (tx) => {\n tx.requiredTransactionIds ??= [];\n tx.requiredTransactionIds.push(id);\n },\n );\n });\n\n const logContext = {\n chainId,\n from,\n nestedTransactionCount: nestedTransactions.length,\n networkClientId,\n sourceAmountRaw,\n transactionId,\n };\n\n log('Submitting second leg', logContext);\n\n try {\n await messenger.call('TransactionController:addTransactionBatch', {\n disableHook: true,\n disableSequential: true,\n disableUpgrade: true,\n from,\n isGasFeeSponsored: sponsored,\n isInternal: true,\n networkClientId,\n origin: ORIGIN_METAMASK,\n requireApproval: false,\n skipInitialGasEstimate: true,\n transactions: nestedTransactions.map((nestedTransaction, index) => ({\n params: {\n data: nestedTransaction.data,\n to: nestedTransaction.to,\n value: nestedTransaction.value ?? '0x0',\n },\n type:\n index === 0\n ? (nestedTransaction.type ?? TransactionType.tokenMethodApprove)\n : TransactionType.contractInteraction,\n })),\n });\n } finally {\n end();\n }\n\n log('Submitted second leg', { ...logContext, transactionIds });\n\n if (!transactionIds.length) {\n throw new Error('No transactions submitted');\n }\n\n await Promise.all(\n transactionIds.map((id) => waitForTransactionConfirmed(id, messenger)),\n );\n\n const hash = getTransaction(transactionIds.slice(-1)[0], messenger)?.hash;\n\n if (!hash) {\n throw new Error('Missing transaction hash');\n }\n\n log('Confirmed second leg', { ...logContext, hash, transactionIds });\n\n return { transactionHash: hash as Hex };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source-amounts.d.ts","sourceRoot":"","sources":["../../src/utils/source-amounts.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"source-amounts.d.ts","sourceRoot":"","sources":["../../src/utils/source-amounts.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EACV,iCAAiC,EAElC,MAAM,aAAa,CAAC;AAGrB,OAAO,KAAK,EACV,kBAAkB,EAGlB,eAAe,EAEhB,MAAM,aAAa,CAAC;AAOrB;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,aAAa,EAAE,MAAM,EACrB,eAAe,EAAE,eAAe,GAAG,SAAS,EAC5C,SAAS,EAAE,iCAAiC,EAC5C,UAAU,CAAC,EAAE,kBAAkB,GAC9B,IAAI,CA0DN"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { createModuleLogger } from '@metamask/utils';
|
|
2
2
|
import { BigNumber } from 'bignumber.js';
|
|
3
|
-
import { ARBITRUM_USDC_ADDRESS, CHAIN_ID_ARBITRUM
|
|
3
|
+
import { ARBITRUM_USDC_ADDRESS, CHAIN_ID_ARBITRUM } from '../constants.js';
|
|
4
4
|
import { TransactionPayStrategy } from '../index.js';
|
|
5
5
|
import { projectLogger } from '../logger.js';
|
|
6
|
+
import { isPerpsDepositTransaction } from './perps.js';
|
|
6
7
|
import { getTokenFiatRate, isSameToken } from './token.js';
|
|
7
8
|
import { getTransaction } from './transaction.js';
|
|
8
9
|
const log = createModuleLogger(projectLogger, 'source-amounts');
|
|
@@ -137,8 +138,8 @@ function calculateSourceAmount(paymentToken, token, messenger, transactionId, is
|
|
|
137
138
|
});
|
|
138
139
|
return undefined;
|
|
139
140
|
}
|
|
140
|
-
const {
|
|
141
|
-
const isAlwaysRequired = isQuoteAlwaysRequired(token, strategy,
|
|
141
|
+
const { strategy, transaction } = getStrategyContext(transactionId, messenger);
|
|
142
|
+
const isAlwaysRequired = isQuoteAlwaysRequired(token, strategy, transaction, isQuoteRequired);
|
|
142
143
|
if (isSameToken(token, paymentToken) && !isAlwaysRequired) {
|
|
143
144
|
log('Skipping token as same as payment token');
|
|
144
145
|
return undefined;
|
|
@@ -175,11 +176,11 @@ function calculateSourceAmount(paymentToken, token, messenger, transactionId, is
|
|
|
175
176
|
*
|
|
176
177
|
* @param token - Target token.
|
|
177
178
|
* @param strategy - Payment strategy.
|
|
178
|
-
* @param
|
|
179
|
+
* @param transaction - Parent transaction.
|
|
179
180
|
* @param isQuoteRequired - When true, a quote is always fetched even when source and target tokens are identical.
|
|
180
181
|
* @returns True if a quote is always required, false otherwise.
|
|
181
182
|
*/
|
|
182
|
-
function isQuoteAlwaysRequired(token, strategy,
|
|
183
|
+
function isQuoteAlwaysRequired(token, strategy, transaction, isQuoteRequired) {
|
|
183
184
|
if (isQuoteRequired) {
|
|
184
185
|
return true;
|
|
185
186
|
}
|
|
@@ -188,14 +189,13 @@ function isQuoteAlwaysRequired(token, strategy, parentTransactionType, isQuoteRe
|
|
|
188
189
|
return (isHyperliquidDeposit &&
|
|
189
190
|
(strategy === TransactionPayStrategy.Relay ||
|
|
190
191
|
(strategy === TransactionPayStrategy.Across &&
|
|
191
|
-
|
|
192
|
-
PERPS_DEPOSIT_TYPES.includes(parentTransactionType))));
|
|
192
|
+
isPerpsDepositTransaction(transaction))));
|
|
193
193
|
}
|
|
194
194
|
function getStrategyContext(transactionId, messenger) {
|
|
195
195
|
const transaction = getTransaction(transactionId, messenger);
|
|
196
196
|
return {
|
|
197
|
-
parentTransactionType: transaction.type,
|
|
198
197
|
strategy: messenger.call('TransactionPayController:getStrategy', transaction),
|
|
198
|
+
transaction,
|
|
199
199
|
};
|
|
200
200
|
}
|
|
201
201
|
//# sourceMappingURL=source-amounts.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source-amounts.js","sourceRoot":"","sources":["../../src/utils/source-amounts.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n TransactionMeta,\n TransactionType,\n} from '@metamask/transaction-controller';\nimport { createModuleLogger } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport {\n ARBITRUM_USDC_ADDRESS,\n CHAIN_ID_ARBITRUM,\n PERPS_DEPOSIT_TYPES,\n} from '../constants.js';\nimport type {\n TransactionPayControllerMessenger,\n TransactionPaymentToken,\n} from '../index.js';\nimport { TransactionPayStrategy } from '../index.js';\nimport { projectLogger } from '../logger.js';\nimport type {\n GetBalanceCallback,\n GetBalanceResponse,\n TransactionPaySourceAmount,\n TransactionData,\n TransactionPayRequiredToken,\n} from '../types.js';\nimport { getTokenFiatRate, isSameToken } from './token.js';\nimport { getTransaction } from './transaction.js';\n\nconst log = createModuleLogger(projectLogger, 'source-amounts');\n\n/**\n * Update the source amounts for a transaction.\n *\n * @param transactionId - ID of the transaction to update.\n * @param transactionData - Existing transaction data.\n * @param messenger - Controller messenger.\n * @param getBalance - Optional callback to override the source balance used for max-amount\n * calculation. Called only when `isMaxAmount` is true. Return `undefined` to fall back to\n * the built-in token balance.\n */\nexport function updateSourceAmounts(\n transactionId: string,\n transactionData: TransactionData | undefined,\n messenger: TransactionPayControllerMessenger,\n getBalance?: GetBalanceCallback,\n): void {\n if (!transactionData) {\n return;\n }\n\n const { isMaxAmount, isPostQuote, paymentToken, tokens } = transactionData;\n\n if (!tokens.length || !paymentToken) {\n return;\n }\n\n const transaction =\n getBalance && isMaxAmount\n ? getTransaction(transactionId, messenger)\n : undefined;\n const balanceOverride =\n getBalance && transaction\n ? getBalance({ transaction, transactionData })\n : undefined;\n\n // For post-quote flows, source amounts are calculated differently\n // The source is the transaction's required token, not the selected token\n if (isPostQuote) {\n const { isHyperliquidSource, isPolymarketDepositWallet, paymentOverride } =\n transactionData;\n const sourceAmounts = calculatePostQuoteSourceAmounts(\n tokens,\n paymentToken,\n isMaxAmount ?? false,\n isHyperliquidSource,\n isPolymarketDepositWallet,\n balanceOverride,\n Boolean(paymentOverride),\n );\n log('Updated post-quote source amounts', { transactionId, sourceAmounts });\n transactionData.sourceAmounts = sourceAmounts;\n return;\n }\n\n const { isQuoteRequired } = transactionData;\n\n const sourceAmounts = tokens\n .map((singleToken) =>\n calculateSourceAmount(\n paymentToken,\n singleToken,\n messenger,\n transactionId,\n isMaxAmount ?? false,\n isQuoteRequired,\n balanceOverride,\n ),\n )\n .filter(Boolean) as TransactionPaySourceAmount[];\n\n log('Updated source amounts', { transactionId, sourceAmounts });\n\n transactionData.sourceAmounts = sourceAmounts;\n}\n\n/**\n * Calculate source amounts for post-quote flows.\n * In this flow, the required tokens ARE the source tokens,\n * and the payment token is the target (destination).\n *\n * @param tokens - Required tokens from the transaction.\n * @param paymentToken - Selected payment/destination token.\n * @param isMaxAmount - Whether the transaction is a maximum amount transaction.\n * @param isHyperliquidSource - Whether the source is HyperLiquid (perps withdrawal).\n * @param isPolymarketDepositWallet - Whether the source is a Polymarket deposit wallet.\n * @param balanceOverride - Optional balance override from the `getBalance` callback.\n * @param hasPaymentOverride - Whether the source calls are rebuilt from the quote amount.\n * @returns Array of source amounts.\n */\nfunction calculatePostQuoteSourceAmounts(\n tokens: TransactionPayRequiredToken[],\n paymentToken: TransactionPaymentToken,\n isMaxAmount: boolean,\n isHyperliquidSource?: boolean,\n isPolymarketDepositWallet?: boolean,\n balanceOverride?: GetBalanceResponse,\n hasPaymentOverride?: boolean,\n): TransactionPaySourceAmount[] {\n return tokens\n .filter((token) => {\n if (token.skipIfBalance) {\n return false;\n }\n\n // Skip zero amounts (unless max amount, where we use balance)\n if (token.amountRaw === '0' && !isMaxAmount) {\n log('Skipping token as zero amount', { tokenAddress: token.address });\n return false;\n }\n\n // Skip same token on same chain, unless the source is a synthetic\n // upstream (HyperLiquid HyperCore or Polymarket deposit wallet) that\n // the strategy renormalizes to a different effective source.\n if (\n isSameToken(token, paymentToken) &&\n !isHyperliquidSource &&\n !isPolymarketDepositWallet\n ) {\n log('Skipping token as same as destination token');\n return false;\n }\n\n return true;\n })\n .map((token) => {\n const sourceBalanceRaw = balanceOverride?.balanceRaw ?? token.balanceRaw;\n\n // On max, the batch funds the source account with the amount encoded in\n // the original transaction, which is prepended ahead of the Relay\n // deposit. A balance refreshed after that amount was encoded can exceed\n // it, and the deposit then reverts for the difference. Synthetic sources\n // (HyperLiquid, Polymarket deposit wallet) and payment overrides build\n // their funding from the quote amount itself, so they are not bounded by\n // the original transaction.\n const isCapped =\n isMaxAmount &&\n !isHyperliquidSource &&\n !isPolymarketDepositWallet &&\n !hasPaymentOverride &&\n token.amountRaw !== '0' &&\n new BigNumber(sourceBalanceRaw).gt(token.amountRaw);\n\n let sourceAmountHuman = token.amountHuman;\n let sourceAmountRaw = token.amountRaw;\n\n if (isMaxAmount && !isCapped) {\n sourceAmountHuman = token.balanceHuman;\n sourceAmountRaw = sourceBalanceRaw;\n }\n\n return {\n sourceAmountHuman,\n sourceAmountRaw,\n sourceBalanceRaw,\n sourceChainId: token.chainId,\n sourceTokenAddress: token.address,\n targetTokenAddress: paymentToken.address,\n };\n });\n}\n\n/**\n * Calculate the required source amount for a payment token to cover a target token.\n *\n * @param paymentToken - Selected payment token.\n * @param token - Target token to cover.\n * @param messenger - Controller messenger.\n * @param transactionId - ID of the transaction.\n * @param isMaxAmount - Whether the transaction is a maximum amount transaction.\n * @param isQuoteRequired - When true, a quote is always fetched even when source and target tokens are identical.\n * @param balanceOverride - Optional balance override from the `getBalance` callback.\n * @returns The source amount or undefined if calculation failed.\n */\nfunction calculateSourceAmount(\n paymentToken: TransactionPaymentToken,\n token: TransactionPayRequiredToken,\n messenger: TransactionPayControllerMessenger,\n transactionId: string,\n isMaxAmount: boolean,\n isQuoteRequired?: boolean,\n balanceOverride?: GetBalanceResponse,\n): TransactionPaySourceAmount | undefined {\n const paymentTokenFiatRate = getTokenFiatRate(\n messenger,\n paymentToken.address,\n paymentToken.chainId,\n );\n\n if (!paymentTokenFiatRate) {\n return undefined;\n }\n\n const hasBalance = new BigNumber(token.balanceRaw).gte(token.amountRaw);\n\n if (token.skipIfBalance && hasBalance) {\n log('Skipping token as sufficient balance', {\n tokenAddress: token.address,\n });\n return undefined;\n }\n\n const { parentTransactionType, strategy } = getStrategyContext(\n transactionId,\n messenger,\n );\n const isAlwaysRequired = isQuoteAlwaysRequired(\n token,\n strategy,\n parentTransactionType,\n isQuoteRequired,\n );\n\n if (isSameToken(token, paymentToken) && !isAlwaysRequired) {\n log('Skipping token as same as payment token');\n return undefined;\n }\n\n if (token.amountRaw === '0') {\n log('Skipping token as zero amount', { tokenAddress: token.address });\n return undefined;\n }\n\n const sourceAmountHumanValue = new BigNumber(token.amountUsd).div(\n paymentTokenFiatRate.usdRate,\n );\n\n const sourceAmountHuman = sourceAmountHumanValue.toString(10);\n\n const sourceAmountRaw = sourceAmountHumanValue\n .shiftedBy(paymentToken.decimals)\n .toFixed(0);\n\n // On Max, use the exact source balance. The client `getBalance` callback is\n // authoritative and owns all balance complexity (perps, predict, money\n // account, payment overrides): when it returns a `balanceOverride`, use it;\n // when it returns `undefined`, that is a deliberate signal to use the pay\n // token's on-chain balance. This path is payment-override agnostic.\n if (isMaxAmount) {\n return {\n sourceAmountHuman: paymentToken.balanceHuman,\n sourceAmountRaw: balanceOverride?.balanceRaw ?? paymentToken.balanceRaw,\n targetTokenAddress: token.address,\n };\n }\n\n return {\n sourceAmountHuman,\n sourceAmountRaw,\n targetTokenAddress: token.address,\n };\n}\n\n/**\n * Determine if a quote is always required for a token and strategy.\n *\n * @param token - Target token.\n * @param strategy - Payment strategy.\n * @param parentTransactionType - Parent transaction type, if available.\n * @param isQuoteRequired - When true, a quote is always fetched even when source and target tokens are identical.\n * @returns True if a quote is always required, false otherwise.\n */\nfunction isQuoteAlwaysRequired(\n token: TransactionPayRequiredToken,\n strategy: TransactionPayStrategy,\n parentTransactionType?: TransactionType,\n isQuoteRequired?: boolean,\n): boolean {\n if (isQuoteRequired) {\n return true;\n }\n\n const isHyperliquidDeposit =\n token.chainId === CHAIN_ID_ARBITRUM &&\n token.address.toLowerCase() === ARBITRUM_USDC_ADDRESS.toLowerCase();\n\n return (\n isHyperliquidDeposit &&\n (strategy === TransactionPayStrategy.Relay ||\n (strategy === TransactionPayStrategy.Across &&\n parentTransactionType !== undefined &&\n PERPS_DEPOSIT_TYPES.includes(parentTransactionType)))\n );\n}\n\nfunction getStrategyContext(\n transactionId: string,\n messenger: TransactionPayControllerMessenger,\n): {\n parentTransactionType?: TransactionType;\n strategy: TransactionPayStrategy;\n} {\n const transaction = getTransaction(\n transactionId,\n messenger,\n ) as TransactionMeta;\n\n return {\n parentTransactionType: transaction.type,\n strategy: messenger.call(\n 'TransactionPayController:getStrategy',\n transaction,\n ),\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"source-amounts.js","sourceRoot":"","sources":["../../src/utils/source-amounts.ts"],"names":[],"mappings":"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","sourcesContent":["import type { TransactionMeta } from '@metamask/transaction-controller';\nimport { createModuleLogger } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport { ARBITRUM_USDC_ADDRESS, CHAIN_ID_ARBITRUM } from '../constants.js';\nimport type {\n TransactionPayControllerMessenger,\n TransactionPaymentToken,\n} from '../index.js';\nimport { TransactionPayStrategy } from '../index.js';\nimport { projectLogger } from '../logger.js';\nimport type {\n GetBalanceCallback,\n GetBalanceResponse,\n TransactionPaySourceAmount,\n TransactionData,\n TransactionPayRequiredToken,\n} from '../types.js';\nimport { isPerpsDepositTransaction } from './perps.js';\nimport { getTokenFiatRate, isSameToken } from './token.js';\nimport { getTransaction } from './transaction.js';\n\nconst log = createModuleLogger(projectLogger, 'source-amounts');\n\n/**\n * Update the source amounts for a transaction.\n *\n * @param transactionId - ID of the transaction to update.\n * @param transactionData - Existing transaction data.\n * @param messenger - Controller messenger.\n * @param getBalance - Optional callback to override the source balance used for max-amount\n * calculation. Called only when `isMaxAmount` is true. Return `undefined` to fall back to\n * the built-in token balance.\n */\nexport function updateSourceAmounts(\n transactionId: string,\n transactionData: TransactionData | undefined,\n messenger: TransactionPayControllerMessenger,\n getBalance?: GetBalanceCallback,\n): void {\n if (!transactionData) {\n return;\n }\n\n const { isMaxAmount, isPostQuote, paymentToken, tokens } = transactionData;\n\n if (!tokens.length || !paymentToken) {\n return;\n }\n\n const transaction =\n getBalance && isMaxAmount\n ? getTransaction(transactionId, messenger)\n : undefined;\n const balanceOverride =\n getBalance && transaction\n ? getBalance({ transaction, transactionData })\n : undefined;\n\n // For post-quote flows, source amounts are calculated differently\n // The source is the transaction's required token, not the selected token\n if (isPostQuote) {\n const { isHyperliquidSource, isPolymarketDepositWallet, paymentOverride } =\n transactionData;\n const sourceAmounts = calculatePostQuoteSourceAmounts(\n tokens,\n paymentToken,\n isMaxAmount ?? false,\n isHyperliquidSource,\n isPolymarketDepositWallet,\n balanceOverride,\n Boolean(paymentOverride),\n );\n log('Updated post-quote source amounts', { transactionId, sourceAmounts });\n transactionData.sourceAmounts = sourceAmounts;\n return;\n }\n\n const { isQuoteRequired } = transactionData;\n\n const sourceAmounts = tokens\n .map((singleToken) =>\n calculateSourceAmount(\n paymentToken,\n singleToken,\n messenger,\n transactionId,\n isMaxAmount ?? false,\n isQuoteRequired,\n balanceOverride,\n ),\n )\n .filter(Boolean) as TransactionPaySourceAmount[];\n\n log('Updated source amounts', { transactionId, sourceAmounts });\n\n transactionData.sourceAmounts = sourceAmounts;\n}\n\n/**\n * Calculate source amounts for post-quote flows.\n * In this flow, the required tokens ARE the source tokens,\n * and the payment token is the target (destination).\n *\n * @param tokens - Required tokens from the transaction.\n * @param paymentToken - Selected payment/destination token.\n * @param isMaxAmount - Whether the transaction is a maximum amount transaction.\n * @param isHyperliquidSource - Whether the source is HyperLiquid (perps withdrawal).\n * @param isPolymarketDepositWallet - Whether the source is a Polymarket deposit wallet.\n * @param balanceOverride - Optional balance override from the `getBalance` callback.\n * @param hasPaymentOverride - Whether the source calls are rebuilt from the quote amount.\n * @returns Array of source amounts.\n */\nfunction calculatePostQuoteSourceAmounts(\n tokens: TransactionPayRequiredToken[],\n paymentToken: TransactionPaymentToken,\n isMaxAmount: boolean,\n isHyperliquidSource?: boolean,\n isPolymarketDepositWallet?: boolean,\n balanceOverride?: GetBalanceResponse,\n hasPaymentOverride?: boolean,\n): TransactionPaySourceAmount[] {\n return tokens\n .filter((token) => {\n if (token.skipIfBalance) {\n return false;\n }\n\n // Skip zero amounts (unless max amount, where we use balance)\n if (token.amountRaw === '0' && !isMaxAmount) {\n log('Skipping token as zero amount', { tokenAddress: token.address });\n return false;\n }\n\n // Skip same token on same chain, unless the source is a synthetic\n // upstream (HyperLiquid HyperCore or Polymarket deposit wallet) that\n // the strategy renormalizes to a different effective source.\n if (\n isSameToken(token, paymentToken) &&\n !isHyperliquidSource &&\n !isPolymarketDepositWallet\n ) {\n log('Skipping token as same as destination token');\n return false;\n }\n\n return true;\n })\n .map((token) => {\n const sourceBalanceRaw = balanceOverride?.balanceRaw ?? token.balanceRaw;\n\n // On max, the batch funds the source account with the amount encoded in\n // the original transaction, which is prepended ahead of the Relay\n // deposit. A balance refreshed after that amount was encoded can exceed\n // it, and the deposit then reverts for the difference. Synthetic sources\n // (HyperLiquid, Polymarket deposit wallet) and payment overrides build\n // their funding from the quote amount itself, so they are not bounded by\n // the original transaction.\n const isCapped =\n isMaxAmount &&\n !isHyperliquidSource &&\n !isPolymarketDepositWallet &&\n !hasPaymentOverride &&\n token.amountRaw !== '0' &&\n new BigNumber(sourceBalanceRaw).gt(token.amountRaw);\n\n let sourceAmountHuman = token.amountHuman;\n let sourceAmountRaw = token.amountRaw;\n\n if (isMaxAmount && !isCapped) {\n sourceAmountHuman = token.balanceHuman;\n sourceAmountRaw = sourceBalanceRaw;\n }\n\n return {\n sourceAmountHuman,\n sourceAmountRaw,\n sourceBalanceRaw,\n sourceChainId: token.chainId,\n sourceTokenAddress: token.address,\n targetTokenAddress: paymentToken.address,\n };\n });\n}\n\n/**\n * Calculate the required source amount for a payment token to cover a target token.\n *\n * @param paymentToken - Selected payment token.\n * @param token - Target token to cover.\n * @param messenger - Controller messenger.\n * @param transactionId - ID of the transaction.\n * @param isMaxAmount - Whether the transaction is a maximum amount transaction.\n * @param isQuoteRequired - When true, a quote is always fetched even when source and target tokens are identical.\n * @param balanceOverride - Optional balance override from the `getBalance` callback.\n * @returns The source amount or undefined if calculation failed.\n */\nfunction calculateSourceAmount(\n paymentToken: TransactionPaymentToken,\n token: TransactionPayRequiredToken,\n messenger: TransactionPayControllerMessenger,\n transactionId: string,\n isMaxAmount: boolean,\n isQuoteRequired?: boolean,\n balanceOverride?: GetBalanceResponse,\n): TransactionPaySourceAmount | undefined {\n const paymentTokenFiatRate = getTokenFiatRate(\n messenger,\n paymentToken.address,\n paymentToken.chainId,\n );\n\n if (!paymentTokenFiatRate) {\n return undefined;\n }\n\n const hasBalance = new BigNumber(token.balanceRaw).gte(token.amountRaw);\n\n if (token.skipIfBalance && hasBalance) {\n log('Skipping token as sufficient balance', {\n tokenAddress: token.address,\n });\n return undefined;\n }\n\n const { strategy, transaction } = getStrategyContext(\n transactionId,\n messenger,\n );\n const isAlwaysRequired = isQuoteAlwaysRequired(\n token,\n strategy,\n transaction,\n isQuoteRequired,\n );\n\n if (isSameToken(token, paymentToken) && !isAlwaysRequired) {\n log('Skipping token as same as payment token');\n return undefined;\n }\n\n if (token.amountRaw === '0') {\n log('Skipping token as zero amount', { tokenAddress: token.address });\n return undefined;\n }\n\n const sourceAmountHumanValue = new BigNumber(token.amountUsd).div(\n paymentTokenFiatRate.usdRate,\n );\n\n const sourceAmountHuman = sourceAmountHumanValue.toString(10);\n\n const sourceAmountRaw = sourceAmountHumanValue\n .shiftedBy(paymentToken.decimals)\n .toFixed(0);\n\n // On Max, use the exact source balance. The client `getBalance` callback is\n // authoritative and owns all balance complexity (perps, predict, money\n // account, payment overrides): when it returns a `balanceOverride`, use it;\n // when it returns `undefined`, that is a deliberate signal to use the pay\n // token's on-chain balance. This path is payment-override agnostic.\n if (isMaxAmount) {\n return {\n sourceAmountHuman: paymentToken.balanceHuman,\n sourceAmountRaw: balanceOverride?.balanceRaw ?? paymentToken.balanceRaw,\n targetTokenAddress: token.address,\n };\n }\n\n return {\n sourceAmountHuman,\n sourceAmountRaw,\n targetTokenAddress: token.address,\n };\n}\n\n/**\n * Determine if a quote is always required for a token and strategy.\n *\n * @param token - Target token.\n * @param strategy - Payment strategy.\n * @param transaction - Parent transaction.\n * @param isQuoteRequired - When true, a quote is always fetched even when source and target tokens are identical.\n * @returns True if a quote is always required, false otherwise.\n */\nfunction isQuoteAlwaysRequired(\n token: TransactionPayRequiredToken,\n strategy: TransactionPayStrategy,\n transaction: TransactionMeta,\n isQuoteRequired?: boolean,\n): boolean {\n if (isQuoteRequired) {\n return true;\n }\n\n const isHyperliquidDeposit =\n token.chainId === CHAIN_ID_ARBITRUM &&\n token.address.toLowerCase() === ARBITRUM_USDC_ADDRESS.toLowerCase();\n\n return (\n isHyperliquidDeposit &&\n (strategy === TransactionPayStrategy.Relay ||\n (strategy === TransactionPayStrategy.Across &&\n isPerpsDepositTransaction(transaction)))\n );\n}\n\nfunction getStrategyContext(\n transactionId: string,\n messenger: TransactionPayControllerMessenger,\n): {\n strategy: TransactionPayStrategy;\n transaction: TransactionMeta;\n} {\n const transaction = getTransaction(\n transactionId,\n messenger,\n ) as TransactionMeta;\n\n return {\n strategy: messenger.call(\n 'TransactionPayController:getStrategy',\n transaction,\n ),\n transaction,\n };\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metamask-previews/transaction-pay-controller",
|
|
3
|
-
"version": "30.3.
|
|
3
|
+
"version": "30.3.2-preview-01e391e",
|
|
4
4
|
"description": "Manages alternate payment strategies to provide required funds for transactions in MetaMask",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Ethereum",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"@ethersproject/contracts": "^5.7.0",
|
|
56
56
|
"@ethersproject/providers": "^5.8.0",
|
|
57
57
|
"@metamask/accounts-controller": "^40.1.0",
|
|
58
|
-
"@metamask/assets-controller": "^20.
|
|
58
|
+
"@metamask/assets-controller": "^20.1.0",
|
|
59
59
|
"@metamask/base-controller": "^10.1.0",
|
|
60
60
|
"@metamask/controller-utils": "^13.0.0",
|
|
61
61
|
"@metamask/gas-fee-controller": "^27.0.0",
|
|
@@ -63,10 +63,10 @@
|
|
|
63
63
|
"@metamask/messenger": "^3.0.0",
|
|
64
64
|
"@metamask/metamask-eth-abis": "^3.1.1",
|
|
65
65
|
"@metamask/network-controller": "^37.1.1",
|
|
66
|
-
"@metamask/ramps-controller": "^28.
|
|
66
|
+
"@metamask/ramps-controller": "^28.1.0",
|
|
67
67
|
"@metamask/remote-feature-flag-controller": "^7.0.0",
|
|
68
68
|
"@metamask/sentinel-api-service": "^2.1.0",
|
|
69
|
-
"@metamask/transaction-controller": "^
|
|
69
|
+
"@metamask/transaction-controller": "^73.0.0",
|
|
70
70
|
"@metamask/utils": "^12.0.1",
|
|
71
71
|
"bignumber.js": "^9.1.2",
|
|
72
72
|
"bn.js": "^5.2.5",
|