@metamask-previews/transaction-pay-controller 29.2.0-preview-3c2e73813 → 29.2.1-preview-6acac7d
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [29.2.1]
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Cap max-amount post-quote source amounts to the amount encoded in the original transaction, so Relay deposits are no longer quoted for more than the batch funds ([#10392](https://github.com/MetaMask/core/pull/10392))
|
|
15
|
+
- Post-quote batches prepend the original transaction to fund the source account, so a balance refreshed after that amount was encoded could produce a deposit that reverted with `Token balance is too low`.
|
|
16
|
+
- Synthetic sources (HyperLiquid, Polymarket deposit wallet) and payment overrides build their funding from the quote amount itself and are not capped.
|
|
17
|
+
|
|
10
18
|
## [29.2.0]
|
|
11
19
|
|
|
12
20
|
### Changed
|
|
@@ -1618,7 +1626,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
1618
1626
|
|
|
1619
1627
|
- Initial release ([#6820](https://github.com/MetaMask/core/pull/6820))
|
|
1620
1628
|
|
|
1621
|
-
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/transaction-pay-controller@29.2.
|
|
1629
|
+
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/transaction-pay-controller@29.2.1...HEAD
|
|
1630
|
+
[29.2.1]: https://github.com/MetaMask/core/compare/@metamask/transaction-pay-controller@29.2.0...@metamask/transaction-pay-controller@29.2.1
|
|
1622
1631
|
[29.2.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-pay-controller@29.1.1...@metamask/transaction-pay-controller@29.2.0
|
|
1623
1632
|
[29.1.1]: https://github.com/MetaMask/core/compare/@metamask/transaction-pay-controller@29.1.0...@metamask/transaction-pay-controller@29.1.1
|
|
1624
1633
|
[29.1.0]: https://github.com/MetaMask/core/compare/@metamask/transaction-pay-controller@29.0.2...@metamask/transaction-pay-controller@29.1.0
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source-amounts.d.ts","sourceRoot":"","sources":["../../src/utils/source-amounts.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,iCAAiC,EAElC,MAAM,aAAa,CAAC;AAGrB,OAAO,KAAK,EACV,kBAAkB,EAGlB,eAAe,EAEhB,MAAM,aAAa,CAAC;AAMrB;;;;;;;;;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,
|
|
1
|
+
{"version":3,"file":"source-amounts.d.ts","sourceRoot":"","sources":["../../src/utils/source-amounts.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,iCAAiC,EAElC,MAAM,aAAa,CAAC;AAGrB,OAAO,KAAK,EACV,kBAAkB,EAGlB,eAAe,EAEhB,MAAM,aAAa,CAAC;AAMrB;;;;;;;;;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"}
|
|
@@ -33,8 +33,8 @@ export function updateSourceAmounts(transactionId, transactionData, messenger, g
|
|
|
33
33
|
// For post-quote flows, source amounts are calculated differently
|
|
34
34
|
// The source is the transaction's required token, not the selected token
|
|
35
35
|
if (isPostQuote) {
|
|
36
|
-
const { isHyperliquidSource, isPolymarketDepositWallet } = transactionData;
|
|
37
|
-
const sourceAmounts = calculatePostQuoteSourceAmounts(tokens, paymentToken, isMaxAmount ?? false, isHyperliquidSource, isPolymarketDepositWallet, balanceOverride);
|
|
36
|
+
const { isHyperliquidSource, isPolymarketDepositWallet, paymentOverride } = transactionData;
|
|
37
|
+
const sourceAmounts = calculatePostQuoteSourceAmounts(tokens, paymentToken, isMaxAmount ?? false, isHyperliquidSource, isPolymarketDepositWallet, balanceOverride, Boolean(paymentOverride));
|
|
38
38
|
log('Updated post-quote source amounts', { transactionId, sourceAmounts });
|
|
39
39
|
transactionData.sourceAmounts = sourceAmounts;
|
|
40
40
|
return;
|
|
@@ -57,9 +57,10 @@ export function updateSourceAmounts(transactionId, transactionData, messenger, g
|
|
|
57
57
|
* @param isHyperliquidSource - Whether the source is HyperLiquid (perps withdrawal).
|
|
58
58
|
* @param isPolymarketDepositWallet - Whether the source is a Polymarket deposit wallet.
|
|
59
59
|
* @param balanceOverride - Optional balance override from the `getBalance` callback.
|
|
60
|
+
* @param hasPaymentOverride - Whether the source calls are rebuilt from the quote amount.
|
|
60
61
|
* @returns Array of source amounts.
|
|
61
62
|
*/
|
|
62
|
-
function calculatePostQuoteSourceAmounts(tokens, paymentToken, isMaxAmount, isHyperliquidSource, isPolymarketDepositWallet, balanceOverride) {
|
|
63
|
+
function calculatePostQuoteSourceAmounts(tokens, paymentToken, isMaxAmount, isHyperliquidSource, isPolymarketDepositWallet, balanceOverride, hasPaymentOverride) {
|
|
63
64
|
return tokens
|
|
64
65
|
.filter((token) => {
|
|
65
66
|
if (token.skipIfBalance) {
|
|
@@ -81,16 +82,36 @@ function calculatePostQuoteSourceAmounts(tokens, paymentToken, isMaxAmount, isHy
|
|
|
81
82
|
}
|
|
82
83
|
return true;
|
|
83
84
|
})
|
|
84
|
-
.map((token) =>
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
85
|
+
.map((token) => {
|
|
86
|
+
const sourceBalanceRaw = balanceOverride?.balanceRaw ?? token.balanceRaw;
|
|
87
|
+
// On max, the batch funds the source account with the amount encoded in
|
|
88
|
+
// the original transaction, which is prepended ahead of the Relay
|
|
89
|
+
// deposit. A balance refreshed after that amount was encoded can exceed
|
|
90
|
+
// it, and the deposit then reverts for the difference. Synthetic sources
|
|
91
|
+
// (HyperLiquid, Polymarket deposit wallet) and payment overrides build
|
|
92
|
+
// their funding from the quote amount itself, so they are not bounded by
|
|
93
|
+
// the original transaction.
|
|
94
|
+
const isCapped = isMaxAmount &&
|
|
95
|
+
!isHyperliquidSource &&
|
|
96
|
+
!isPolymarketDepositWallet &&
|
|
97
|
+
!hasPaymentOverride &&
|
|
98
|
+
token.amountRaw !== '0' &&
|
|
99
|
+
new BigNumber(sourceBalanceRaw).gt(token.amountRaw);
|
|
100
|
+
let sourceAmountHuman = token.amountHuman;
|
|
101
|
+
let sourceAmountRaw = token.amountRaw;
|
|
102
|
+
if (isMaxAmount && !isCapped) {
|
|
103
|
+
sourceAmountHuman = token.balanceHuman;
|
|
104
|
+
sourceAmountRaw = sourceBalanceRaw;
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
sourceAmountHuman,
|
|
108
|
+
sourceAmountRaw,
|
|
109
|
+
sourceBalanceRaw,
|
|
110
|
+
sourceChainId: token.chainId,
|
|
111
|
+
sourceTokenAddress: token.address,
|
|
112
|
+
targetTokenAddress: paymentToken.address,
|
|
113
|
+
};
|
|
114
|
+
});
|
|
94
115
|
}
|
|
95
116
|
/**
|
|
96
117
|
* Calculate the required source amount for a payment token to cover a target token.
|
|
@@ -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 } = transactionData;\n const sourceAmounts = calculatePostQuoteSourceAmounts(\n tokens,\n paymentToken,\n isMaxAmount ?? false,\n isHyperliquidSource,\n isPolymarketDepositWallet,\n balanceOverride,\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 * @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): 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 sourceAmountHuman: isMaxAmount ? token.balanceHuman : token.amountHuman,\n sourceAmountRaw: isMaxAmount\n ? (balanceOverride?.balanceRaw ?? token.balanceRaw)\n : token.amountRaw,\n sourceBalanceRaw: balanceOverride?.balanceRaw ?? token.balanceRaw,\n sourceChainId: token.chainId,\n sourceTokenAddress: token.address,\n targetTokenAddress: paymentToken.address,\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 {\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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@metamask-previews/transaction-pay-controller",
|
|
3
|
-
"version": "29.2.
|
|
3
|
+
"version": "29.2.1-preview-6acac7d",
|
|
4
4
|
"description": "Manages alternate payment strategies to provide required funds for transactions in MetaMask",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Ethereum",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"jest": "^30.4.2",
|
|
82
82
|
"rimraf": "^6.1.3",
|
|
83
83
|
"ts-jest": "^29.4.12",
|
|
84
|
-
"tsx": "^4.23.
|
|
84
|
+
"tsx": "^4.23.15",
|
|
85
85
|
"typedoc": "^0.25.13",
|
|
86
86
|
"typedoc-plugin-missing-exports": "^2.0.0",
|
|
87
87
|
"typescript": "npm:@typescript/typescript6@^6.0.2"
|