@metamask/transaction-pay-controller 23.11.0 → 23.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/dist/helpers/TransactionPayPublishHook.cjs +9 -4
  3. package/dist/helpers/TransactionPayPublishHook.cjs.map +1 -1
  4. package/dist/helpers/TransactionPayPublishHook.d.cts.map +1 -1
  5. package/dist/helpers/TransactionPayPublishHook.d.mts.map +1 -1
  6. package/dist/helpers/TransactionPayPublishHook.mjs +9 -4
  7. package/dist/helpers/TransactionPayPublishHook.mjs.map +1 -1
  8. package/dist/strategy/fiat/FiatStrategy.cjs +12 -1
  9. package/dist/strategy/fiat/FiatStrategy.cjs.map +1 -1
  10. package/dist/strategy/fiat/FiatStrategy.d.cts.map +1 -1
  11. package/dist/strategy/fiat/FiatStrategy.d.mts.map +1 -1
  12. package/dist/strategy/fiat/FiatStrategy.mjs +12 -1
  13. package/dist/strategy/fiat/FiatStrategy.mjs.map +1 -1
  14. package/dist/strategy/fiat/fiat-direct-musd.cjs +74 -23
  15. package/dist/strategy/fiat/fiat-direct-musd.cjs.map +1 -1
  16. package/dist/strategy/fiat/fiat-direct-musd.d.cts +15 -0
  17. package/dist/strategy/fiat/fiat-direct-musd.d.cts.map +1 -1
  18. package/dist/strategy/fiat/fiat-direct-musd.d.mts +15 -0
  19. package/dist/strategy/fiat/fiat-direct-musd.d.mts.map +1 -1
  20. package/dist/strategy/fiat/fiat-direct-musd.mjs +73 -23
  21. package/dist/strategy/fiat/fiat-direct-musd.mjs.map +1 -1
  22. package/dist/strategy/fiat/fiat-submit-simple.cjs +1 -1
  23. package/dist/strategy/fiat/fiat-submit-simple.cjs.map +1 -1
  24. package/dist/strategy/fiat/fiat-submit-simple.mjs +1 -1
  25. package/dist/strategy/fiat/fiat-submit-simple.mjs.map +1 -1
  26. package/dist/strategy/fiat/fiat-submit-with-transaction-data.cjs +1 -1
  27. package/dist/strategy/fiat/fiat-submit-with-transaction-data.cjs.map +1 -1
  28. package/dist/strategy/fiat/fiat-submit-with-transaction-data.mjs +1 -1
  29. package/dist/strategy/fiat/fiat-submit-with-transaction-data.mjs.map +1 -1
  30. package/dist/strategy/fiat/fiat-submit.cjs +19 -47
  31. package/dist/strategy/fiat/fiat-submit.cjs.map +1 -1
  32. package/dist/strategy/fiat/fiat-submit.d.cts.map +1 -1
  33. package/dist/strategy/fiat/fiat-submit.d.mts.map +1 -1
  34. package/dist/strategy/fiat/fiat-submit.mjs +20 -48
  35. package/dist/strategy/fiat/fiat-submit.mjs.map +1 -1
  36. package/dist/strategy/fiat/utils.cjs +24 -1
  37. package/dist/strategy/fiat/utils.cjs.map +1 -1
  38. package/dist/strategy/fiat/utils.d.cts +14 -1
  39. package/dist/strategy/fiat/utils.d.cts.map +1 -1
  40. package/dist/strategy/fiat/utils.d.mts +14 -1
  41. package/dist/strategy/fiat/utils.d.mts.map +1 -1
  42. package/dist/strategy/fiat/utils.mjs +22 -0
  43. package/dist/strategy/fiat/utils.mjs.map +1 -1
  44. package/dist/strategy/relay/RelayStrategy.cjs +1 -7
  45. package/dist/strategy/relay/RelayStrategy.cjs.map +1 -1
  46. package/dist/strategy/relay/RelayStrategy.d.cts.map +1 -1
  47. package/dist/strategy/relay/RelayStrategy.d.mts.map +1 -1
  48. package/dist/strategy/relay/RelayStrategy.mjs +1 -7
  49. package/dist/strategy/relay/RelayStrategy.mjs.map +1 -1
  50. package/dist/strategy/relay/relay-quotes.cjs +1 -29
  51. package/dist/strategy/relay/relay-quotes.cjs.map +1 -1
  52. package/dist/strategy/relay/relay-quotes.mjs +1 -29
  53. package/dist/strategy/relay/relay-quotes.mjs.map +1 -1
  54. package/dist/strategy/relay/relay-submit.cjs +30 -7
  55. package/dist/strategy/relay/relay-submit.cjs.map +1 -1
  56. package/dist/strategy/relay/relay-submit.d.cts.map +1 -1
  57. package/dist/strategy/relay/relay-submit.d.mts.map +1 -1
  58. package/dist/strategy/relay/relay-submit.mjs +29 -6
  59. package/dist/strategy/relay/relay-submit.mjs.map +1 -1
  60. package/dist/utils/error-prefix.cjs +21 -0
  61. package/dist/utils/error-prefix.cjs.map +1 -0
  62. package/dist/utils/error-prefix.d.cts +9 -0
  63. package/dist/utils/error-prefix.d.cts.map +1 -0
  64. package/dist/utils/error-prefix.d.mts +9 -0
  65. package/dist/utils/error-prefix.d.mts.map +1 -0
  66. package/dist/utils/error-prefix.mjs +17 -0
  67. package/dist/utils/error-prefix.mjs.map +1 -0
  68. package/dist/utils/provider.cjs +28 -2
  69. package/dist/utils/provider.cjs.map +1 -1
  70. package/dist/utils/provider.d.cts.map +1 -1
  71. package/dist/utils/provider.d.mts.map +1 -1
  72. package/dist/utils/provider.mjs +29 -3
  73. package/dist/utils/provider.mjs.map +1 -1
  74. package/dist/utils/source-amounts.cjs +4 -6
  75. package/dist/utils/source-amounts.cjs.map +1 -1
  76. package/dist/utils/source-amounts.d.cts.map +1 -1
  77. package/dist/utils/source-amounts.d.mts.map +1 -1
  78. package/dist/utils/source-amounts.mjs +4 -6
  79. package/dist/utils/source-amounts.mjs.map +1 -1
  80. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"source-amounts.cjs","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 type {\n TransactionPayControllerMessenger,\n TransactionPaymentToken,\n} from '..';\nimport { TransactionPayStrategy } from '..';\nimport {\n ARBITRUM_USDC_ADDRESS,\n CHAIN_ID_ARBITRUM,\n PERPS_DEPOSIT_TYPES,\n} from '../constants';\nimport { projectLogger } from '../logger';\nimport type {\n TransactionPaySourceAmount,\n TransactionData,\n TransactionPayRequiredToken,\n} from '../types';\nimport { getTokenFiatRate, isSameToken } from './token';\nimport { getTransaction } from './transaction';\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 */\nexport function updateSourceAmounts(\n transactionId: string,\n transactionData: TransactionData | undefined,\n messenger: TransactionPayControllerMessenger,\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 // 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, isQuoteRequired } =\n transactionData;\n const sourceAmounts = calculatePostQuoteSourceAmounts(\n tokens,\n paymentToken,\n isMaxAmount ?? false,\n isHyperliquidSource,\n isPolymarketDepositWallet,\n isQuoteRequired,\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 ),\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 isQuoteRequired - When true, a quote is always fetched even when source and target tokens are identical.\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 isQuoteRequired?: 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 !isQuoteRequired\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 ? token.balanceRaw : token.amountRaw,\n sourceBalanceRaw: 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 * @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): 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 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 if (token.amountRaw === '0') {\n log('Skipping token as zero amount', { tokenAddress: token.address });\n return undefined;\n }\n\n if (isMaxAmount) {\n return {\n sourceAmountHuman: paymentToken.balanceHuman,\n sourceAmountRaw: 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.cjs","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 type {\n TransactionPayControllerMessenger,\n TransactionPaymentToken,\n} from '..';\nimport { TransactionPayStrategy } from '..';\nimport {\n ARBITRUM_USDC_ADDRESS,\n CHAIN_ID_ARBITRUM,\n PERPS_DEPOSIT_TYPES,\n} from '../constants';\nimport { projectLogger } from '../logger';\nimport type {\n TransactionPaySourceAmount,\n TransactionData,\n TransactionPayRequiredToken,\n} from '../types';\nimport { getTokenFiatRate, isSameToken } from './token';\nimport { getTransaction } from './transaction';\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 */\nexport function updateSourceAmounts(\n transactionId: string,\n transactionData: TransactionData | undefined,\n messenger: TransactionPayControllerMessenger,\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 // 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 );\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 ),\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 * @returns Array of source amounts.\n */\nfunction calculatePostQuoteSourceAmounts(\n tokens: TransactionPayRequiredToken[],\n paymentToken: TransactionPaymentToken,\n isMaxAmount: boolean,\n isHyperliquidSource?: boolean,\n isPolymarketDepositWallet?: 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 sourceAmountHuman: isMaxAmount ? token.balanceHuman : token.amountHuman,\n sourceAmountRaw: isMaxAmount ? token.balanceRaw : token.amountRaw,\n sourceBalanceRaw: 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 * @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): 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 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 if (token.amountRaw === '0') {\n log('Skipping token as zero amount', { tokenAddress: token.address });\n return undefined;\n }\n\n if (isMaxAmount) {\n return {\n sourceAmountHuman: paymentToken.balanceHuman,\n sourceAmountRaw: 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 +1 @@
1
- {"version":3,"file":"source-amounts.d.cts","sourceRoot":"","sources":["../../src/utils/source-amounts.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,iCAAiC,EAElC,qBAAW;AAQZ,OAAO,KAAK,EAEV,eAAe,EAEhB,qBAAiB;AAMlB;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,aAAa,EAAE,MAAM,EACrB,eAAe,EAAE,eAAe,GAAG,SAAS,EAC5C,SAAS,EAAE,iCAAiC,GAC3C,IAAI,CA+CN"}
1
+ {"version":3,"file":"source-amounts.d.cts","sourceRoot":"","sources":["../../src/utils/source-amounts.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,iCAAiC,EAElC,qBAAW;AAQZ,OAAO,KAAK,EAEV,eAAe,EAEhB,qBAAiB;AAMlB;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,aAAa,EAAE,MAAM,EACrB,eAAe,EAAE,eAAe,GAAG,SAAS,EAC5C,SAAS,EAAE,iCAAiC,GAC3C,IAAI,CA6CN"}
@@ -1 +1 @@
1
- {"version":3,"file":"source-amounts.d.mts","sourceRoot":"","sources":["../../src/utils/source-amounts.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,iCAAiC,EAElC,qBAAW;AAQZ,OAAO,KAAK,EAEV,eAAe,EAEhB,qBAAiB;AAMlB;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,aAAa,EAAE,MAAM,EACrB,eAAe,EAAE,eAAe,GAAG,SAAS,EAC5C,SAAS,EAAE,iCAAiC,GAC3C,IAAI,CA+CN"}
1
+ {"version":3,"file":"source-amounts.d.mts","sourceRoot":"","sources":["../../src/utils/source-amounts.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,iCAAiC,EAElC,qBAAW;AAQZ,OAAO,KAAK,EAEV,eAAe,EAEhB,qBAAiB;AAMlB;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,aAAa,EAAE,MAAM,EACrB,eAAe,EAAE,eAAe,GAAG,SAAS,EAC5C,SAAS,EAAE,iCAAiC,GAC3C,IAAI,CA6CN"}
@@ -24,8 +24,8 @@ export function updateSourceAmounts(transactionId, transactionData, messenger) {
24
24
  // For post-quote flows, source amounts are calculated differently
25
25
  // The source is the transaction's required token, not the selected token
26
26
  if (isPostQuote) {
27
- const { isHyperliquidSource, isPolymarketDepositWallet, isQuoteRequired } = transactionData;
28
- const sourceAmounts = calculatePostQuoteSourceAmounts(tokens, paymentToken, isMaxAmount ?? false, isHyperliquidSource, isPolymarketDepositWallet, isQuoteRequired);
27
+ const { isHyperliquidSource, isPolymarketDepositWallet } = transactionData;
28
+ const sourceAmounts = calculatePostQuoteSourceAmounts(tokens, paymentToken, isMaxAmount ?? false, isHyperliquidSource, isPolymarketDepositWallet);
29
29
  log('Updated post-quote source amounts', { transactionId, sourceAmounts });
30
30
  transactionData.sourceAmounts = sourceAmounts;
31
31
  return;
@@ -47,10 +47,9 @@ export function updateSourceAmounts(transactionId, transactionData, messenger) {
47
47
  * @param isMaxAmount - Whether the transaction is a maximum amount transaction.
48
48
  * @param isHyperliquidSource - Whether the source is HyperLiquid (perps withdrawal).
49
49
  * @param isPolymarketDepositWallet - Whether the source is a Polymarket deposit wallet.
50
- * @param isQuoteRequired - When true, a quote is always fetched even when source and target tokens are identical.
51
50
  * @returns Array of source amounts.
52
51
  */
53
- function calculatePostQuoteSourceAmounts(tokens, paymentToken, isMaxAmount, isHyperliquidSource, isPolymarketDepositWallet, isQuoteRequired) {
52
+ function calculatePostQuoteSourceAmounts(tokens, paymentToken, isMaxAmount, isHyperliquidSource, isPolymarketDepositWallet) {
54
53
  return tokens
55
54
  .filter((token) => {
56
55
  if (token.skipIfBalance) {
@@ -66,8 +65,7 @@ function calculatePostQuoteSourceAmounts(tokens, paymentToken, isMaxAmount, isHy
66
65
  // the strategy renormalizes to a different effective source.
67
66
  if (isSameToken(token, paymentToken) &&
68
67
  !isHyperliquidSource &&
69
- !isPolymarketDepositWallet &&
70
- !isQuoteRequired) {
68
+ !isPolymarketDepositWallet) {
71
69
  log('Skipping token as same as destination token');
72
70
  return false;
73
71
  }
@@ -1 +1 @@
1
- {"version":3,"file":"source-amounts.mjs","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 type {\n TransactionPayControllerMessenger,\n TransactionPaymentToken,\n} from '..';\nimport { TransactionPayStrategy } from '..';\nimport {\n ARBITRUM_USDC_ADDRESS,\n CHAIN_ID_ARBITRUM,\n PERPS_DEPOSIT_TYPES,\n} from '../constants';\nimport { projectLogger } from '../logger';\nimport type {\n TransactionPaySourceAmount,\n TransactionData,\n TransactionPayRequiredToken,\n} from '../types';\nimport { getTokenFiatRate, isSameToken } from './token';\nimport { getTransaction } from './transaction';\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 */\nexport function updateSourceAmounts(\n transactionId: string,\n transactionData: TransactionData | undefined,\n messenger: TransactionPayControllerMessenger,\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 // 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, isQuoteRequired } =\n transactionData;\n const sourceAmounts = calculatePostQuoteSourceAmounts(\n tokens,\n paymentToken,\n isMaxAmount ?? false,\n isHyperliquidSource,\n isPolymarketDepositWallet,\n isQuoteRequired,\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 ),\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 isQuoteRequired - When true, a quote is always fetched even when source and target tokens are identical.\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 isQuoteRequired?: 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 !isQuoteRequired\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 ? token.balanceRaw : token.amountRaw,\n sourceBalanceRaw: 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 * @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): 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 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 if (token.amountRaw === '0') {\n log('Skipping token as zero amount', { tokenAddress: token.address });\n return undefined;\n }\n\n if (isMaxAmount) {\n return {\n sourceAmountHuman: paymentToken.balanceHuman,\n sourceAmountRaw: 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.mjs","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 type {\n TransactionPayControllerMessenger,\n TransactionPaymentToken,\n} from '..';\nimport { TransactionPayStrategy } from '..';\nimport {\n ARBITRUM_USDC_ADDRESS,\n CHAIN_ID_ARBITRUM,\n PERPS_DEPOSIT_TYPES,\n} from '../constants';\nimport { projectLogger } from '../logger';\nimport type {\n TransactionPaySourceAmount,\n TransactionData,\n TransactionPayRequiredToken,\n} from '../types';\nimport { getTokenFiatRate, isSameToken } from './token';\nimport { getTransaction } from './transaction';\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 */\nexport function updateSourceAmounts(\n transactionId: string,\n transactionData: TransactionData | undefined,\n messenger: TransactionPayControllerMessenger,\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 // 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 );\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 ),\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 * @returns Array of source amounts.\n */\nfunction calculatePostQuoteSourceAmounts(\n tokens: TransactionPayRequiredToken[],\n paymentToken: TransactionPaymentToken,\n isMaxAmount: boolean,\n isHyperliquidSource?: boolean,\n isPolymarketDepositWallet?: 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 sourceAmountHuman: isMaxAmount ? token.balanceHuman : token.amountHuman,\n sourceAmountRaw: isMaxAmount ? token.balanceRaw : token.amountRaw,\n sourceBalanceRaw: 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 * @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): 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 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 if (token.amountRaw === '0') {\n log('Skipping token as zero amount', { tokenAddress: token.address });\n return undefined;\n }\n\n if (isMaxAmount) {\n return {\n sourceAmountHuman: paymentToken.balanceHuman,\n sourceAmountRaw: 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/transaction-pay-controller",
3
- "version": "23.11.0",
3
+ "version": "23.13.0",
4
4
  "description": "Manages alternate payment strategies to provide required funds for transactions in MetaMask",
5
5
  "keywords": [
6
6
  "Ethereum",
@@ -58,7 +58,7 @@
58
58
  "@ethersproject/contracts": "^5.7.0",
59
59
  "@ethersproject/providers": "^5.7.0",
60
60
  "@metamask/assets-controller": "^9.0.1",
61
- "@metamask/assets-controllers": "^109.1.0",
61
+ "@metamask/assets-controllers": "^109.2.0",
62
62
  "@metamask/base-controller": "^9.1.0",
63
63
  "@metamask/bridge-controller": "^75.1.1",
64
64
  "@metamask/bridge-status-controller": "^72.1.0",
@@ -68,9 +68,9 @@
68
68
  "@metamask/messenger": "^1.2.0",
69
69
  "@metamask/metamask-eth-abis": "^3.1.1",
70
70
  "@metamask/network-controller": "^32.0.0",
71
- "@metamask/ramps-controller": "^14.2.0",
71
+ "@metamask/ramps-controller": "^14.3.0",
72
72
  "@metamask/remote-feature-flag-controller": "^4.2.2",
73
- "@metamask/transaction-controller": "^68.0.1",
73
+ "@metamask/transaction-controller": "^68.1.0",
74
74
  "@metamask/utils": "^11.11.0",
75
75
  "bignumber.js": "^9.1.2",
76
76
  "bn.js": "^5.2.1",