@metamask-previews/transaction-pay-controller 31.2.1-preview-906029d47 → 31.2.1-preview-e80304db4

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 (100) hide show
  1. package/CHANGELOG.md +3 -20
  2. package/dist/TransactionPayController.d.ts.map +1 -1
  3. package/dist/TransactionPayController.js +5 -4
  4. package/dist/TransactionPayController.js.map +1 -1
  5. package/dist/actions/update-payment-token.d.ts.map +1 -1
  6. package/dist/actions/update-payment-token.js +22 -51
  7. package/dist/actions/update-payment-token.js.map +1 -1
  8. package/dist/index.d.ts +1 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/strategy/across/AcrossStrategy.d.ts.map +1 -1
  12. package/dist/strategy/across/AcrossStrategy.js +1 -6
  13. package/dist/strategy/across/AcrossStrategy.js.map +1 -1
  14. package/dist/strategy/across/across-quotes.js +4 -7
  15. package/dist/strategy/across/across-quotes.js.map +1 -1
  16. package/dist/strategy/across/across-submit.d.ts.map +1 -1
  17. package/dist/strategy/across/across-submit.js +1 -3
  18. package/dist/strategy/across/across-submit.js.map +1 -1
  19. package/dist/strategy/fiat/FiatStrategy.d.ts +0 -1
  20. package/dist/strategy/fiat/FiatStrategy.d.ts.map +1 -1
  21. package/dist/strategy/fiat/FiatStrategy.js +0 -4
  22. package/dist/strategy/fiat/FiatStrategy.js.map +1 -1
  23. package/dist/strategy/fiat/fiat-direct-musd.d.ts.map +1 -1
  24. package/dist/strategy/fiat/fiat-direct-musd.js +2 -4
  25. package/dist/strategy/fiat/fiat-direct-musd.js.map +1 -1
  26. package/dist/strategy/fiat/fiat-quotes.d.ts.map +1 -1
  27. package/dist/strategy/fiat/fiat-quotes.js +2 -4
  28. package/dist/strategy/fiat/fiat-quotes.js.map +1 -1
  29. package/dist/strategy/fiat/fiat-submit.d.ts.map +1 -1
  30. package/dist/strategy/fiat/fiat-submit.js +2 -5
  31. package/dist/strategy/fiat/fiat-submit.js.map +1 -1
  32. package/dist/strategy/fiat/fiat-test-funding.d.ts.map +1 -1
  33. package/dist/strategy/fiat/fiat-test-funding.js +5 -3
  34. package/dist/strategy/fiat/fiat-test-funding.js.map +1 -1
  35. package/dist/strategy/relay/RelayStrategy.d.ts +0 -4
  36. package/dist/strategy/relay/RelayStrategy.d.ts.map +1 -1
  37. package/dist/strategy/relay/RelayStrategy.js +1 -6
  38. package/dist/strategy/relay/RelayStrategy.js.map +1 -1
  39. package/dist/strategy/relay/relay-quotes.d.ts.map +1 -1
  40. package/dist/strategy/relay/relay-quotes.js +6 -10
  41. package/dist/strategy/relay/relay-quotes.js.map +1 -1
  42. package/dist/strategy/relay/relay-submit-execute.d.ts.map +1 -1
  43. package/dist/strategy/relay/relay-submit-execute.js +1 -3
  44. package/dist/strategy/relay/relay-submit-execute.js.map +1 -1
  45. package/dist/strategy/relay/relay-submit.d.ts.map +1 -1
  46. package/dist/strategy/relay/relay-submit.js +6 -8
  47. package/dist/strategy/relay/relay-submit.js.map +1 -1
  48. package/dist/strategy/relay/relay-validation.d.ts.map +1 -1
  49. package/dist/strategy/relay/relay-validation.js +2 -5
  50. package/dist/strategy/relay/relay-validation.js.map +1 -1
  51. package/dist/strategy/server/ServerStrategy.d.ts.map +1 -1
  52. package/dist/strategy/server/ServerStrategy.js +0 -4
  53. package/dist/strategy/server/ServerStrategy.js.map +1 -1
  54. package/dist/strategy/server/server-quotes.js +4 -7
  55. package/dist/strategy/server/server-quotes.js.map +1 -1
  56. package/dist/strategy/server/server-submit.d.ts.map +1 -1
  57. package/dist/strategy/server/server-submit.js +6 -9
  58. package/dist/strategy/server/server-submit.js.map +1 -1
  59. package/dist/strategy/server/server-validation.d.ts.map +1 -1
  60. package/dist/strategy/server/server-validation.js +1 -3
  61. package/dist/strategy/server/server-validation.js.map +1 -1
  62. package/dist/types.d.ts +25 -60
  63. package/dist/types.d.ts.map +1 -1
  64. package/dist/types.js.map +1 -1
  65. package/dist/utils/account.d.ts +0 -54
  66. package/dist/utils/account.d.ts.map +1 -1
  67. package/dist/utils/account.js +0 -124
  68. package/dist/utils/account.js.map +1 -1
  69. package/dist/utils/gas-payment.d.ts +3 -12
  70. package/dist/utils/gas-payment.d.ts.map +1 -1
  71. package/dist/utils/gas-payment.js +2 -2
  72. package/dist/utils/gas-payment.js.map +1 -1
  73. package/dist/utils/max-gas-station.d.ts.map +1 -1
  74. package/dist/utils/max-gas-station.js +5 -9
  75. package/dist/utils/max-gas-station.js.map +1 -1
  76. package/dist/utils/max-quote.js +2 -3
  77. package/dist/utils/max-quote.js.map +1 -1
  78. package/dist/utils/no-op-quote.d.ts +3 -3
  79. package/dist/utils/no-op-quote.d.ts.map +1 -1
  80. package/dist/utils/no-op-quote.js +3 -4
  81. package/dist/utils/no-op-quote.js.map +1 -1
  82. package/dist/utils/perps.d.ts.map +1 -1
  83. package/dist/utils/perps.js +2 -5
  84. package/dist/utils/perps.js.map +1 -1
  85. package/dist/utils/post-quote.js +3 -4
  86. package/dist/utils/post-quote.js.map +1 -1
  87. package/dist/utils/quotes.d.ts.map +1 -1
  88. package/dist/utils/quotes.js +25 -99
  89. package/dist/utils/quotes.js.map +1 -1
  90. package/dist/utils/source-amounts.d.ts.map +1 -1
  91. package/dist/utils/source-amounts.js +4 -25
  92. package/dist/utils/source-amounts.js.map +1 -1
  93. package/dist/utils/token.d.ts +1 -84
  94. package/dist/utils/token.d.ts.map +1 -1
  95. package/dist/utils/token.js +13 -144
  96. package/dist/utils/token.js.map +1 -1
  97. package/dist/utils/validation.d.ts.map +1 -1
  98. package/dist/utils/validation.js +11 -14
  99. package/dist/utils/validation.js.map +1 -1
  100. package/package.json +3 -4
@@ -1,6 +1,7 @@
1
1
  import { Interface } from '@ethersproject/abi';
2
2
  import { abiERC20 } from '@metamask/metamask-eth-abis';
3
- import { createModuleLogger, hexToBigInt, KnownCaipNamespace, numberToHex, parseCaipAssetType, toCaipAssetType, } from '@metamask/utils';
3
+ import { createModuleLogger } from '@metamask/utils';
4
+ import { hexToBigInt, toCaipAssetType } from '@metamask/utils';
4
5
  import { BigNumber } from 'bignumber.js';
5
6
  import { CHAIN_ID_POLYGON, NATIVE_TOKEN_ADDRESS, SLIP44_COIN_TYPE_BY_CHAIN, } from '../constants.js';
6
7
  import { projectLogger } from '../logger.js';
@@ -29,76 +30,6 @@ export function isSameToken(token1, token2) {
29
30
  return (token1.address.toLowerCase() === token2.address.toLowerCase() &&
30
31
  token1.chainId === token2.chainId);
31
32
  }
32
- /**
33
- * Check if a payment token is on a non-EVM chain.
34
- *
35
- * The chain is derived from the CAIP-19 asset type of the payment token.
36
- *
37
- * @param paymentToken - Payment token to check.
38
- * @returns True if the payment token is a non-EVM payment token.
39
- */
40
- export function isNonEvmPaymentToken(paymentToken) {
41
- return !getEvmAsset(paymentToken.assetId);
42
- }
43
- /**
44
- * Check if the source asset of a quote request is on an EVM chain.
45
- *
46
- * Strategies use it to support or reject a request and to select their
47
- * execution path, which is the only place the source protocol matters.
48
- *
49
- * @param request - Quote request to check.
50
- * @returns True if the source asset is on an EVM chain.
51
- */
52
- export function isEvmQuoteRequest(request) {
53
- return Boolean(getEvmAsset(request.sourceAssetId));
54
- }
55
- /**
56
- * Get the Hex chain ID and token address of the EVM source asset of a quote
57
- * request, as used by the EVM quote and execution paths.
58
- *
59
- * @param request - Quote request.
60
- * @returns The Hex chain ID and token address derived from `sourceAssetId`.
61
- */
62
- export function getEvmQuoteSource(request) {
63
- const evmAsset = getEvmAsset(request.sourceAssetId);
64
- if (!evmAsset) {
65
- throw new Error(`Unsupported non-EVM source asset: ${request.sourceAssetId}`);
66
- }
67
- return { chainId: evmAsset.chainId, tokenAddress: evmAsset.address };
68
- }
69
- /**
70
- * Derive the Hex chain ID and token address of an EVM CAIP-19 asset type.
71
- *
72
- * A `slip44` native asset maps to the chain's native token address, so the
73
- * result matches the address the EVM quote and balance paths expect.
74
- *
75
- * @param assetId - CAIP-19 asset type.
76
- * @returns The Hex chain ID and token address, or undefined if the asset is
77
- * not on an EVM chain.
78
- */
79
- export function getEvmAsset(assetId) {
80
- const { chain, assetNamespace, assetReference } = parseCaipAssetType(assetId);
81
- if (chain.namespace !== KnownCaipNamespace.Eip155) {
82
- return undefined;
83
- }
84
- const chainId = numberToHex(Number(chain.reference));
85
- const address = assetNamespace === 'slip44'
86
- ? getNativeToken(chainId)
87
- : assetReference;
88
- return { address, chainId };
89
- }
90
- /**
91
- * Add the Hex chain ID and token address derived from its asset type to an
92
- * EVM payment token.
93
- *
94
- * @param paymentToken - Payment token, if any.
95
- * @returns The payment token with its derived chain ID and address, or
96
- * undefined if no payment token is provided or it is not an EVM token.
97
- */
98
- export function getEvmPaymentToken(paymentToken) {
99
- const evmAsset = paymentToken && getEvmAsset(paymentToken.assetId);
100
- return evmAsset && { ...paymentToken, ...evmAsset };
101
- }
102
33
  /**
103
34
  * Get the token balance for a specific account and token.
104
35
  *
@@ -122,57 +53,18 @@ export function getTokenBalance(messenger, account, chainId, tokenAddress) {
122
53
  if (!assetId) {
123
54
  return '0';
124
55
  }
125
- return getRawBalance(assetsBalance, assetsInfo, accountId, assetId);
126
- }
127
- /**
128
- * Get the balance of a CAIP-19 asset held by an `InternalAccount`.
129
- *
130
- * @param messenger - Controller messenger.
131
- * @param walletAccountId - ID of the `InternalAccount` holding the asset.
132
- * @param assetId - CAIP-19 asset type as keyed in `AssetsController` state.
133
- * @returns Raw asset balance as a decimal string.
134
- */
135
- export function getAssetBalance(messenger, walletAccountId, assetId) {
136
- const { assetsBalance, assetsInfo } = messenger.call('AssetsController:getState');
137
- return getRawBalance(assetsBalance, assetsInfo, walletAccountId, assetId);
138
- }
139
- /**
140
- * Get the decimals and symbol for a CAIP-19 asset.
141
- *
142
- * @param messenger - Controller messenger.
143
- * @param assetId - CAIP-19 asset type as keyed in `AssetsController` state.
144
- * @returns The asset decimals and symbol, or undefined if the asset is not found.
145
- */
146
- export function getAssetInfo(messenger, assetId) {
147
- const { assetsInfo } = messenger.call('AssetsController:getState');
148
- const asset = assetsInfo[assetId];
149
- if (!asset) {
150
- return undefined;
151
- }
152
- return { decimals: Number(asset.decimals), symbol: asset.symbol };
153
- }
154
- /**
155
- * Get the fiat rates for a CAIP-19 asset.
156
- *
157
- * Configured EVM stablecoins are priced at exactly 1 USD.
158
- *
159
- * @param messenger - Controller messenger.
160
- * @param assetId - CAIP-19 asset type as keyed in `AssetsController` state.
161
- * @returns An object containing the USD and fiat rates, or undefined if rates are not available.
162
- */
163
- export function getAssetFiatRate(messenger, assetId) {
164
- const { assetsPrice } = messenger.call('AssetsController:getState');
165
- const price = assetsPrice[assetId];
166
- if (price?.assetPriceType !== 'fungible') {
167
- return undefined;
56
+ const amount = assetsBalance[accountId]?.[assetId]?.amount;
57
+ if (amount === undefined) {
58
+ return '0';
168
59
  }
169
- const evmAsset = getEvmAsset(assetId);
170
- const isStablecoin = evmAsset &&
171
- getStablecoins(messenger)[evmAsset.chainId]?.includes(evmAsset.address.toLowerCase());
172
- return {
173
- fiatRate: String(price.price),
174
- usdRate: isStablecoin ? '1' : String(price.usdPrice),
175
- };
60
+ // AssetsController stores human-readable decimal amounts, whereas callers of
61
+ // this function expect raw base units. Shift by the token decimals to convert.
62
+ // The asset ID resolved above is a key of `assetsInfo`, so its metadata is
63
+ // read from the snapshot already in hand rather than re-fetched.
64
+ const { decimals } = assetsInfo[assetId];
65
+ return new BigNumber(amount)
66
+ .shiftedBy(Number(decimals))
67
+ .toFixed(0, BigNumber.ROUND_DOWN);
176
68
  }
177
69
  /**
178
70
  * Get the decimals and symbol for a specific token.
@@ -430,29 +322,6 @@ export function normalizeTokenAddress(tokenAddress, chainId, target) {
430
322
  }
431
323
  return tokenAddress;
432
324
  }
433
- /**
434
- * Read a raw balance from `AssetsController` state.
435
- *
436
- * `AssetsController` stores human-readable decimal amounts, whereas callers
437
- * expect raw base units, so the amount is shifted by the asset decimals.
438
- *
439
- * @param assetsBalance - Asset balances keyed by account ID and CAIP-19 ID.
440
- * @param assetsInfo - Asset metadata keyed by CAIP-19 ID.
441
- * @param accountId - ID of the `InternalAccount` holding the asset.
442
- * @param assetId - CAIP-19 asset type as keyed in state.
443
- * @returns Raw balance as a decimal string, or `'0'` if no balance or asset
444
- * metadata is stored.
445
- */
446
- function getRawBalance(assetsBalance, assetsInfo, accountId, assetId) {
447
- const amount = assetsBalance[accountId]?.[assetId]?.amount;
448
- const decimals = assetsInfo[assetId]?.decimals;
449
- if (amount === undefined || decimals === undefined) {
450
- return '0';
451
- }
452
- return new BigNumber(amount)
453
- .shiftedBy(Number(decimals))
454
- .toFixed(0, BigNumber.ROUND_DOWN);
455
- }
456
325
  /**
457
326
  * Locate an asset's key in `AssetsController` state, reusing a previously
458
327
  * resolved identifier.
@@ -1 +1 @@
1
- {"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/utils/token.ts"],"names":[],"mappings":"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","sourcesContent":["import { Interface } from '@ethersproject/abi';\nimport type { AssetsControllerState } from '@metamask/assets-controller';\nimport { abiERC20 } from '@metamask/metamask-eth-abis';\nimport {\n createModuleLogger,\n hexToBigInt,\n KnownCaipNamespace,\n numberToHex,\n parseCaipAssetType,\n toCaipAssetType,\n} from '@metamask/utils';\nimport type { CaipAssetType, Hex } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport {\n CHAIN_ID_POLYGON,\n NATIVE_TOKEN_ADDRESS,\n SLIP44_COIN_TYPE_BY_CHAIN,\n} from '../constants.js';\nimport { projectLogger } from '../logger.js';\nimport type {\n EvmPaymentToken,\n FiatRates,\n QuoteRequest,\n TransactionPayControllerMessenger,\n TransactionPaymentToken,\n} from '../types.js';\nimport { getStablecoins, isChainExcludedFromInfura } from './feature-flags.js';\nimport { rpcRequest } from './provider.js';\n\nconst log = createModuleLogger(projectLogger, 'token');\n\n/**\n * Resolved CAIP-19 asset IDs, keyed by chain ID and lower-cased address.\n *\n * An asset ID is a static property of its chain and address, so one resolved\n * from any state snapshot stays correct for the lifetime of the process.\n */\nconst assetIds = new Map<string, CaipAssetType>();\n\n/**\n * Check if two tokens are the same (same address and chain).\n *\n * @param token1 - First token identifier.\n * @param token1.address - Token address.\n * @param token1.chainId - Token chain ID.\n * @param token2 - Second token identifier.\n * @param token2.address - Token address.\n * @param token2.chainId - Token chain ID.\n * @returns True if tokens are the same, false otherwise.\n */\nexport function isSameToken(\n token1: { address: Hex; chainId: Hex },\n token2: { address: Hex; chainId: Hex },\n): boolean {\n return (\n token1.address.toLowerCase() === token2.address.toLowerCase() &&\n token1.chainId === token2.chainId\n );\n}\n\n/**\n * Check if a payment token is on a non-EVM chain.\n *\n * The chain is derived from the CAIP-19 asset type of the payment token.\n *\n * @param paymentToken - Payment token to check.\n * @returns True if the payment token is a non-EVM payment token.\n */\nexport function isNonEvmPaymentToken(\n paymentToken: TransactionPaymentToken,\n): boolean {\n return !getEvmAsset(paymentToken.assetId);\n}\n\n/**\n * Check if the source asset of a quote request is on an EVM chain.\n *\n * Strategies use it to support or reject a request and to select their\n * execution path, which is the only place the source protocol matters.\n *\n * @param request - Quote request to check.\n * @returns True if the source asset is on an EVM chain.\n */\nexport function isEvmQuoteRequest(\n request: Pick<QuoteRequest, 'sourceAssetId'>,\n): boolean {\n return Boolean(getEvmAsset(request.sourceAssetId));\n}\n\n/**\n * Get the Hex chain ID and token address of the EVM source asset of a quote\n * request, as used by the EVM quote and execution paths.\n *\n * @param request - Quote request.\n * @returns The Hex chain ID and token address derived from `sourceAssetId`.\n */\nexport function getEvmQuoteSource(\n request: Pick<QuoteRequest, 'sourceAssetId'>,\n): { chainId: Hex; tokenAddress: Hex } {\n const evmAsset = getEvmAsset(request.sourceAssetId);\n\n if (!evmAsset) {\n throw new Error(\n `Unsupported non-EVM source asset: ${request.sourceAssetId}`,\n );\n }\n\n return { chainId: evmAsset.chainId, tokenAddress: evmAsset.address };\n}\n\n/**\n * Derive the Hex chain ID and token address of an EVM CAIP-19 asset type.\n *\n * A `slip44` native asset maps to the chain's native token address, so the\n * result matches the address the EVM quote and balance paths expect.\n *\n * @param assetId - CAIP-19 asset type.\n * @returns The Hex chain ID and token address, or undefined if the asset is\n * not on an EVM chain.\n */\nexport function getEvmAsset(\n assetId: CaipAssetType,\n): { address: Hex; chainId: Hex } | undefined {\n const { chain, assetNamespace, assetReference } = parseCaipAssetType(assetId);\n\n if (chain.namespace !== KnownCaipNamespace.Eip155) {\n return undefined;\n }\n\n const chainId = numberToHex(Number(chain.reference));\n\n const address =\n assetNamespace === 'slip44'\n ? getNativeToken(chainId)\n : (assetReference as Hex);\n\n return { address, chainId };\n}\n\n/**\n * Add the Hex chain ID and token address derived from its asset type to an\n * EVM payment token.\n *\n * @param paymentToken - Payment token, if any.\n * @returns The payment token with its derived chain ID and address, or\n * undefined if no payment token is provided or it is not an EVM token.\n */\nexport function getEvmPaymentToken(\n paymentToken: TransactionPaymentToken | undefined,\n): EvmPaymentToken | undefined {\n const evmAsset = paymentToken && getEvmAsset(paymentToken.assetId);\n\n return evmAsset && { ...paymentToken, ...evmAsset };\n}\n\n/**\n * Get the token balance for a specific account and token.\n *\n * @param messenger - Controller messenger.\n * @param account - Address of the account.\n * @param chainId - Id of the chain.\n * @param tokenAddress - Address of the token contract.\n * @returns Raw token balance as a decimal string.\n */\nexport function getTokenBalance(\n messenger: TransactionPayControllerMessenger,\n account: Hex,\n chainId: Hex,\n tokenAddress: Hex,\n): string {\n const { accountIdByAddress } = messenger.call('AccountsController:getState');\n\n // Fall back to the lower-cased address, as a miss is usually attributable to\n // a checksummed address being passed. Mirrors AccountsController#getAccountByAddress.\n const accountId =\n accountIdByAddress[account] ??\n accountIdByAddress[account.toLowerCase() as Hex];\n\n if (!accountId) {\n return '0';\n }\n\n const { assetsBalance, assetsInfo } = messenger.call(\n 'AssetsController:getState',\n );\n const assetId = getControllerAssetId(assetsInfo, chainId, tokenAddress);\n\n if (!assetId) {\n return '0';\n }\n\n return getRawBalance(assetsBalance, assetsInfo, accountId, assetId);\n}\n\n/**\n * Get the balance of a CAIP-19 asset held by an `InternalAccount`.\n *\n * @param messenger - Controller messenger.\n * @param walletAccountId - ID of the `InternalAccount` holding the asset.\n * @param assetId - CAIP-19 asset type as keyed in `AssetsController` state.\n * @returns Raw asset balance as a decimal string.\n */\nexport function getAssetBalance(\n messenger: TransactionPayControllerMessenger,\n walletAccountId: string,\n assetId: CaipAssetType,\n): string {\n const { assetsBalance, assetsInfo } = messenger.call(\n 'AssetsController:getState',\n );\n\n return getRawBalance(assetsBalance, assetsInfo, walletAccountId, assetId);\n}\n\n/**\n * Get the decimals and symbol for a CAIP-19 asset.\n *\n * @param messenger - Controller messenger.\n * @param assetId - CAIP-19 asset type as keyed in `AssetsController` state.\n * @returns The asset decimals and symbol, or undefined if the asset is not found.\n */\nexport function getAssetInfo(\n messenger: TransactionPayControllerMessenger,\n assetId: CaipAssetType,\n): { decimals: number; symbol: string } | undefined {\n const { assetsInfo } = messenger.call('AssetsController:getState');\n const asset = assetsInfo[assetId];\n\n if (!asset) {\n return undefined;\n }\n\n return { decimals: Number(asset.decimals), symbol: asset.symbol };\n}\n\n/**\n * Get the fiat rates for a CAIP-19 asset.\n *\n * Configured EVM stablecoins are priced at exactly 1 USD.\n *\n * @param messenger - Controller messenger.\n * @param assetId - CAIP-19 asset type as keyed in `AssetsController` state.\n * @returns An object containing the USD and fiat rates, or undefined if rates are not available.\n */\nexport function getAssetFiatRate(\n messenger: TransactionPayControllerMessenger,\n assetId: CaipAssetType,\n): FiatRates | undefined {\n const { assetsPrice } = messenger.call('AssetsController:getState');\n const price = assetsPrice[assetId];\n\n if (price?.assetPriceType !== 'fungible') {\n return undefined;\n }\n\n const evmAsset = getEvmAsset(assetId);\n\n const isStablecoin =\n evmAsset &&\n getStablecoins(messenger)[evmAsset.chainId]?.includes(\n evmAsset.address.toLowerCase() as Hex,\n );\n\n return {\n fiatRate: String(price.price),\n usdRate: isStablecoin ? '1' : String(price.usdPrice),\n };\n}\n\n/**\n * Get the decimals and symbol for a specific token.\n *\n * `AssetsController` is the sole source of truth: a token absent from unified\n * state is treated as non-existent rather than being reconstructed from network\n * configuration. Consumers must therefore ensure unified asset state covers\n * every chain Pay is used on.\n *\n * @param messenger - Controller messenger.\n * @param tokenAddress - Address of the token contract.\n * @param chainId - Id of the chain.\n * @returns The token decimals and symbol, or undefined if the token is not found.\n */\nexport function getTokenInfo(\n messenger: TransactionPayControllerMessenger,\n tokenAddress: Hex,\n chainId: Hex,\n): { decimals: number; symbol: string } | undefined {\n const { assetsInfo } = messenger.call('AssetsController:getState');\n const assetId = getControllerAssetId(assetsInfo, chainId, tokenAddress);\n const token = assetId ? assetsInfo[assetId] : undefined;\n\n if (!token) {\n return undefined;\n }\n\n return { decimals: Number(token.decimals), symbol: token.symbol };\n}\n\n/**\n * Calculate fiat rates for a specific token.\n *\n * @param messenger - Controller messenger.\n * @param tokenAddress - Address of the token contract.\n * @param chainId - Id of the chain.\n * @returns An object containing the USD and fiat rates, or undefined if rates are not available.\n */\nexport function getTokenFiatRate(\n messenger: TransactionPayControllerMessenger,\n tokenAddress: Hex,\n chainId: Hex,\n): FiatRates | undefined {\n const { assetsInfo, assetsPrice } = messenger.call(\n 'AssetsController:getState',\n );\n const assetId = getControllerAssetId(assetsInfo, chainId, tokenAddress);\n const price = assetId ? assetsPrice[assetId] : undefined;\n\n if (price?.assetPriceType !== 'fungible') {\n return undefined;\n }\n\n const isStablecoin = getStablecoins(messenger)[chainId]?.includes(\n tokenAddress.toLowerCase() as Hex,\n );\n\n return {\n fiatRate: String(price.price),\n usdRate: isStablecoin ? '1' : String(price.usdPrice),\n };\n}\n\n/**\n * Calculate the human-readable, raw, USD, and fiat representations of a token amount.\n *\n * @param rawInput - Raw token amount (decimal string, hex, or BigNumber).\n * @param decimals - Number of decimals for the token.\n * @param fiatRates - Fiat rates for the token.\n * @returns Object containing the amount in raw, human-readable, USD, and fiat formats.\n */\nexport function computeTokenAmounts(\n rawInput: BigNumber.Value,\n decimals: number,\n fiatRates: FiatRates,\n): {\n raw: string;\n human: string;\n usd: string;\n fiat: string;\n} {\n const rawValue = new BigNumber(rawInput);\n const humanValue = rawValue.shiftedBy(-decimals);\n\n return {\n raw: rawValue.toFixed(0),\n human: humanValue.toString(10),\n usd: humanValue.multipliedBy(fiatRates.usdRate).toString(10),\n fiat: humanValue.multipliedBy(fiatRates.fiatRate).toString(10),\n };\n}\n\n/**\n * Compute a raw token amount from a fiat (USD) amount.\n * This is the inverse of `computeTokenAmounts` — it goes from USD to raw.\n *\n * @param fiatAmount - Amount in fiat/USD.\n * @param decimals - Token decimals.\n * @param usdRate - USD rate for the token (price per one unit of the token).\n * @returns Raw token amount string, or undefined if the conversion produces an invalid result.\n */\nexport function computeRawFromFiatAmount(\n fiatAmount: BigNumber.Value,\n decimals: number,\n usdRate: BigNumber.Value,\n): string | undefined {\n const rate = new BigNumber(usdRate);\n if (!rate.isFinite() || !rate.gt(0)) {\n return undefined;\n }\n\n const humanAmount = new BigNumber(fiatAmount).dividedBy(rate);\n if (!humanAmount.isFinite() || !humanAmount.gt(0)) {\n return undefined;\n }\n\n const raw = humanAmount\n .shiftedBy(decimals)\n .decimalPlaces(0, BigNumber.ROUND_DOWN)\n .toFixed(0);\n\n return new BigNumber(raw).gt(0) ? raw : undefined;\n}\n\n/**\n * Get the native token address for a given chain ID.\n *\n * @param chainId - Chain ID.\n * @returns - Native token address for the given chain ID.\n */\nexport function getNativeToken(chainId: Hex): Hex {\n switch (chainId) {\n case '0x89':\n return '0x0000000000000000000000000000000000001010';\n default:\n return NATIVE_TOKEN_ADDRESS;\n }\n}\n\n/**\n * Get the live on-chain token balance via an RPC `eth_call` to the ERC-20\n * `balanceOf` function, or `eth_getBalance` for native tokens.\n *\n * Unlike {@link getTokenBalance}, this bypasses cached AssetsController state\n * and reads directly from the chain.\n *\n * Uses the Infura RPC endpoint for the chain when one is configured, falling\n * back to the chain's default endpoint. This avoids errors on custom mainnet\n * RPC endpoints that may not support pending block queries.\n *\n * @param messenger - Controller messenger.\n * @param account - Address of the account.\n * @param chainId - Chain ID.\n * @param tokenAddress - Address of the token contract.\n * @returns Raw token balance as a decimal string.\n */\nexport async function getLiveTokenBalance(\n messenger: TransactionPayControllerMessenger,\n account: Hex,\n chainId: Hex,\n tokenAddress: Hex,\n): Promise<string> {\n const options = {\n preferInfura: !isChainExcludedFromInfura(messenger, chainId),\n };\n const isNative =\n tokenAddress.toLowerCase() === getNativeToken(chainId).toLowerCase();\n\n if (isNative) {\n const result = await requestBalanceWithFallback((blockTag) =>\n rpcRequest<string>({\n messenger,\n chainId,\n method: 'eth_getBalance',\n params: [account, blockTag],\n options,\n }),\n );\n\n return new BigNumber(result, 16).toString(10);\n }\n\n const calldata = new Interface(abiERC20).encodeFunctionData('balanceOf', [\n account,\n ]) as Hex;\n\n const result = await requestBalanceWithFallback((blockTag) =>\n rpcRequest<string>({\n messenger,\n chainId,\n method: 'eth_call',\n params: [{ to: tokenAddress, data: calldata }, blockTag],\n options,\n }),\n );\n\n return new BigNumber(result, 16).toString(10);\n}\n\n/**\n * Read the live on-chain token balance, returning `undefined` instead of\n * throwing when the RPC request fails.\n *\n * An RPC failure means the balance is *unknown*, which is not the same as the\n * balance being *insufficient*. Callers use the balance to fail fast before\n * simulation and submission; when it cannot be read they should skip that\n * optimisation rather than block the user, since simulation and the on-chain\n * transaction still catch a genuine shortfall.\n *\n * @param messenger - Controller messenger.\n * @param account - Address of the account.\n * @param chainId - Chain ID.\n * @param tokenAddress - Address of the token contract.\n * @returns Raw token balance as a decimal string, or `undefined` if the\n * balance could not be read.\n */\nexport async function getLiveTokenBalanceOrUndefined(\n messenger: TransactionPayControllerMessenger,\n account: Hex,\n chainId: Hex,\n tokenAddress: Hex,\n): Promise<string | undefined> {\n try {\n return await getLiveTokenBalance(messenger, account, chainId, tokenAddress);\n } catch (error) {\n log('Could not read live token balance, skipping balance check', {\n account,\n chainId,\n error,\n tokenAddress,\n });\n\n return undefined;\n }\n}\n\n/**\n * Request a balance using the `pending` block tag, falling back to `latest`\n * if the `pending` query throws.\n *\n * Some custom RPC endpoints do not support `pending` block queries. When the\n * `pending` request fails, retry with `latest` so a live balance can still be\n * resolved.\n *\n * @param request - Function that performs the balance request for a given\n * block tag.\n * @returns Raw balance result as a hex string.\n */\nasync function requestBalanceWithFallback(\n request: (blockTag: 'pending' | 'latest') => Promise<string>,\n): Promise<string> {\n try {\n return await request('pending');\n } catch (error) {\n log('Pending balance query failed, falling back to latest', error);\n return request('latest');\n }\n}\n\n/**\n * Build a CAIP-19 asset type identifier for an EVM token.\n *\n * The identifier is derived purely from its arguments, so it describes the\n * token as the wider ecosystem names it rather than as this wallet happens to\n * have indexed it. Use it for identifiers sent to third parties, such as Ramps\n * order assets; use `AssetsController` state for anything read back from the\n * wallet's own asset metadata, balances, or prices.\n *\n * For native tokens the SLIP-44 coin type is resolved automatically from\n * a built-in chain→coin-type map, falling back to 60 (ETH). Callers can\n * override via the optional third parameter.\n *\n * @param chainId - Hex chain ID (e.g. `0x1`).\n * @param tokenAddress - Token contract address, or the native token address.\n * @param slip44CoinType - Optional SLIP-44 coin type override for native tokens.\n * @returns CAIP-19 asset type string.\n */\nexport function buildCaipAssetType(\n chainId: Hex,\n tokenAddress: Hex,\n slip44CoinType?: number,\n): CaipAssetType {\n const chainReference = String(hexToBigInt(chainId));\n const isNative =\n tokenAddress.toLowerCase() === getNativeToken(chainId).toLowerCase();\n\n if (isNative) {\n const coinType = slip44CoinType ?? SLIP44_COIN_TYPE_BY_CHAIN[chainId] ?? 60;\n\n return toCaipAssetType(\n 'eip155',\n chainReference,\n 'slip44',\n String(coinType),\n );\n }\n\n return toCaipAssetType('eip155', chainReference, 'erc20', tokenAddress);\n}\n\nexport enum TokenAddressTarget {\n Relay = 'relay',\n MetaMask = 'metamask',\n}\n\n/**\n * Normalize token address formats between MetaMask and Relay for Polygon native\n * token handling.\n *\n * MetaMask uses Polygon's native token contract-like address (`0x...1010`),\n * while Relay expects the zero address for native tokens.\n *\n * @param tokenAddress - Token address to normalize.\n * @param chainId - Chain ID for the token.\n * @param target - Optional target system format.\n * @returns Normalized token address for the target system, or the original\n * address if no target is provided.\n */\nexport function normalizeTokenAddress(\n tokenAddress: Hex,\n chainId: Hex,\n target?: TokenAddressTarget,\n): Hex {\n if (chainId !== CHAIN_ID_POLYGON) {\n return tokenAddress;\n }\n\n const nativeTokenAddress = getNativeToken(chainId).toLowerCase() as Hex;\n const normalizedTokenAddress = tokenAddress.toLowerCase();\n\n if (\n target === TokenAddressTarget.Relay &&\n normalizedTokenAddress === nativeTokenAddress\n ) {\n return NATIVE_TOKEN_ADDRESS;\n }\n\n if (\n target === TokenAddressTarget.MetaMask &&\n normalizedTokenAddress === NATIVE_TOKEN_ADDRESS.toLowerCase()\n ) {\n return nativeTokenAddress;\n }\n\n return tokenAddress;\n}\n\n/**\n * Read a raw balance from `AssetsController` state.\n *\n * `AssetsController` stores human-readable decimal amounts, whereas callers\n * expect raw base units, so the amount is shifted by the asset decimals.\n *\n * @param assetsBalance - Asset balances keyed by account ID and CAIP-19 ID.\n * @param assetsInfo - Asset metadata keyed by CAIP-19 ID.\n * @param accountId - ID of the `InternalAccount` holding the asset.\n * @param assetId - CAIP-19 asset type as keyed in state.\n * @returns Raw balance as a decimal string, or `'0'` if no balance or asset\n * metadata is stored.\n */\nfunction getRawBalance(\n assetsBalance: AssetsControllerState['assetsBalance'],\n assetsInfo: AssetsControllerState['assetsInfo'],\n accountId: string,\n assetId: CaipAssetType,\n): string {\n const amount = assetsBalance[accountId]?.[assetId]?.amount;\n const decimals = assetsInfo[assetId]?.decimals;\n\n if (amount === undefined || decimals === undefined) {\n return '0';\n }\n\n return new BigNumber(amount)\n .shiftedBy(Number(decimals))\n .toFixed(0, BigNumber.ROUND_DOWN);\n}\n\n/**\n * Locate an asset's key in `AssetsController` state, reusing a previously\n * resolved identifier.\n *\n * A cached identifier is still confirmed against the given state, since an\n * asset `AssetsController` has not indexed yet is unresolvable no matter how\n * its identifier would be spelled. That check is a keyed read, so the cache\n * saves the scan below without letting a stale identifier through.\n *\n * Only resolved checksummed identifiers are cached, so an asset missing at\n * first lookup, or resolved only to a lowercase duplicate, is retried and\n * picked up once `AssetsController` indexes its checksummed entry.\n *\n * @param assetsInfo - Asset metadata keyed by CAIP-19 ID.\n * @param chainId - Hex chain ID.\n * @param tokenAddress - Token address, or the native token address.\n * @returns The CAIP-19 asset ID as keyed in state, or undefined when the asset\n * is absent.\n */\nfunction getControllerAssetId(\n assetsInfo: AssetsControllerState['assetsInfo'],\n chainId: Hex,\n tokenAddress: Hex,\n): CaipAssetType | undefined {\n const cacheKey = `${chainId}:${tokenAddress.toLowerCase()}`;\n const cached = assetIds.get(cacheKey);\n\n if (cached && assetsInfo[cached]) {\n return cached;\n }\n\n const assetId = resolveControllerAssetId(assetsInfo, chainId, tokenAddress);\n\n // Only cache checksummed (mixed-case) keys, so a lowercase duplicate seen\n // before the canonical entry is indexed cannot stick for the session.\n if (assetId && assetId !== assetId.toLowerCase()) {\n assetIds.set(cacheKey, assetId);\n }\n\n return assetId;\n}\n\n/**\n * Locate an asset's key in `AssetsController` state.\n *\n * An ERC-20 identifier is fully derivable from the chain and address, so for a\n * checksummed address the derived form is tried against state first —\n * client-selected tokens are already checksummed and hit immediately. Other\n * addresses, such as lowercase ones recovered from transaction calldata, fall\n * through to a case-insensitive search, which avoids paying for a checksum on\n * the hot path. That search prefers the checksummed key, since state may also\n * hold a stale lowercase duplicate without price or balance data.\n *\n * A native identifier cannot be derived, because MetaMask keys natives\n * inconsistently across chains — `slip44:`, `erc20:` with the zero address, or\n * a chain-specific sentinel address — so the chain's sole entry of type\n * `native` is searched for instead.\n *\n * An asset absent from state is unresolvable rather than assumed, so a derived\n * identifier state cannot confirm is never returned.\n *\n * @param assetsInfo - Asset metadata keyed by CAIP-19 ID.\n * @param chainId - Hex chain ID.\n * @param tokenAddress - Token address, or the native token address.\n * @returns The CAIP-19 asset ID as keyed in state, or undefined when the asset\n * is absent.\n */\nfunction resolveControllerAssetId(\n assetsInfo: AssetsControllerState['assetsInfo'],\n chainId: Hex,\n tokenAddress: Hex,\n): CaipAssetType | undefined {\n const chainPrefix = `eip155:${hexToBigInt(chainId)}/`;\n const isNative =\n tokenAddress.toLowerCase() === getNativeToken(chainId).toLowerCase();\n\n if (isNative) {\n for (const [key, metadata] of Object.entries(assetsInfo)) {\n if (key.startsWith(chainPrefix) && metadata.type === 'native') {\n return key as CaipAssetType;\n }\n }\n\n return undefined;\n }\n\n const derivedAssetId = `${chainPrefix}erc20:${tokenAddress}` as CaipAssetType;\n const target = derivedAssetId.toLowerCase();\n\n // Mixed-case input is already checksummed, so an exact hit is canonical.\n if (derivedAssetId !== target && assetsInfo[derivedAssetId]) {\n return derivedAssetId;\n }\n\n // Prefer the checksummed (mixed-case) key over a lowercase duplicate.\n let lowercaseMatch: CaipAssetType | undefined;\n\n for (const key of Object.keys(assetsInfo)) {\n if (key.toLowerCase() !== target) {\n continue;\n }\n\n if (key !== target) {\n return key as CaipAssetType;\n }\n\n lowercaseMatch = key as CaipAssetType;\n }\n\n return lowercaseMatch;\n}\n"]}
1
+ {"version":3,"file":"token.js","sourceRoot":"","sources":["../../src/utils/token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErD,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,yBAAyB,GAC1B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,EAAE,cAAc,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,MAAM,GAAG,GAAG,kBAAkB,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAyB,CAAC;AAElD;;;;;;;;;;GAUG;AACH,MAAM,UAAU,WAAW,CACzB,MAAsC,EACtC,MAAsC;IAEtC,OAAO,CACL,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE;QAC7D,MAAM,CAAC,OAAO,KAAK,MAAM,CAAC,OAAO,CAClC,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAC7B,SAA4C,EAC5C,OAAY,EACZ,OAAY,EACZ,YAAiB;IAEjB,MAAM,EAAE,kBAAkB,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;IAE7E,6EAA6E;IAC7E,sFAAsF;IACtF,MAAM,SAAS,GACb,kBAAkB,CAAC,OAAO,CAAC;QAC3B,kBAAkB,CAAC,OAAO,CAAC,WAAW,EAAS,CAAC,CAAC;IAEnD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,GAAG,CAAC;IACb,CAAC;IAED,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,GAAG,SAAS,CAAC,IAAI,CAClD,2BAA2B,CAC5B,CAAC;IACF,MAAM,OAAO,GAAG,oBAAoB,CAAC,UAAU,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;IAExE,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,GAAG,CAAC;IACb,CAAC;IAED,MAAM,MAAM,GAAG,aAAa,CAAC,SAAS,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAE3D,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,GAAG,CAAC;IACb,CAAC;IAED,6EAA6E;IAC7E,+EAA+E;IAC/E,2EAA2E;IAC3E,iEAAiE;IACjE,MAAM,EAAE,QAAQ,EAAE,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;IAEzC,OAAO,IAAI,SAAS,CAAC,MAAM,CAAC;SACzB,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;SAC3B,OAAO,CAAC,CAAC,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC;AACtC,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,YAAY,CAC1B,SAA4C,EAC5C,YAAiB,EACjB,OAAY;IAEZ,MAAM,EAAE,UAAU,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;IACnE,MAAM,OAAO,GAAG,oBAAoB,CAAC,UAAU,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;IACxE,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAExD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;AACpE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAC9B,SAA4C,EAC5C,YAAiB,EACjB,OAAY;IAEZ,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,GAAG,SAAS,CAAC,IAAI,CAChD,2BAA2B,CAC5B,CAAC;IACF,MAAM,OAAO,GAAG,oBAAoB,CAAC,UAAU,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;IACxE,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAEzD,IAAI,KAAK,EAAE,cAAc,KAAK,UAAU,EAAE,CAAC;QACzC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,YAAY,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,EAAE,QAAQ,CAC/D,YAAY,CAAC,WAAW,EAAS,CAClC,CAAC;IAEF,OAAO;QACL,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;QAC7B,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC;KACrD,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CACjC,QAAyB,EACzB,QAAgB,EAChB,SAAoB;IAOpB,MAAM,QAAQ,GAAG,IAAI,SAAS,CAAC,QAAQ,CAAC,CAAC;IACzC,MAAM,UAAU,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,CAAC;IAEjD,OAAO;QACL,GAAG,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;QACxB,KAAK,EAAE,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9B,GAAG,EAAE,UAAU,CAAC,YAAY,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5D,IAAI,EAAE,UAAU,CAAC,YAAY,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;KAC/D,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,wBAAwB,CACtC,UAA2B,EAC3B,QAAgB,EAChB,OAAwB;IAExB,MAAM,IAAI,GAAG,IAAI,SAAS,CAAC,OAAO,CAAC,CAAC;IACpC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,SAAS,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC9D,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAClD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,GAAG,GAAG,WAAW;SACpB,SAAS,CAAC,QAAQ,CAAC;SACnB,aAAa,CAAC,CAAC,EAAE,SAAS,CAAC,UAAU,CAAC;SACtC,OAAO,CAAC,CAAC,CAAC,CAAC;IAEd,OAAO,IAAI,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AACpD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,OAAY;IACzC,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,MAAM;YACT,OAAO,4CAA4C,CAAC;QACtD;YACE,OAAO,oBAAoB,CAAC;IAChC,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,SAA4C,EAC5C,OAAY,EACZ,OAAY,EACZ,YAAiB;IAEjB,MAAM,OAAO,GAAG;QACd,YAAY,EAAE,CAAC,yBAAyB,CAAC,SAAS,EAAE,OAAO,CAAC;KAC7D,CAAC;IACF,MAAM,QAAQ,GACZ,YAAY,CAAC,WAAW,EAAE,KAAK,cAAc,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;IAEvE,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,MAAM,GAAG,MAAM,0BAA0B,CAAC,CAAC,QAAQ,EAAE,EAAE,CAC3D,UAAU,CAAS;YACjB,SAAS;YACT,OAAO;YACP,MAAM,EAAE,gBAAgB;YACxB,MAAM,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC;YAC3B,OAAO;SACR,CAAC,CACH,CAAC;QAEF,OAAO,IAAI,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,SAAS,CAAC,QAAQ,CAAC,CAAC,kBAAkB,CAAC,WAAW,EAAE;QACvE,OAAO;KACR,CAAQ,CAAC;IAEV,MAAM,MAAM,GAAG,MAAM,0BAA0B,CAAC,CAAC,QAAQ,EAAE,EAAE,CAC3D,UAAU,CAAS;QACjB,SAAS;QACT,OAAO;QACP,MAAM,EAAE,UAAU;QAClB,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,QAAQ,CAAC;QACxD,OAAO;KACR,CAAC,CACH,CAAC;IAEF,OAAO,IAAI,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AAChD,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAClD,SAA4C,EAC5C,OAAY,EACZ,OAAY,EACZ,YAAiB;IAEjB,IAAI,CAAC;QACH,OAAO,MAAM,mBAAmB,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;IAC9E,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,2DAA2D,EAAE;YAC/D,OAAO;YACP,OAAO;YACP,KAAK;YACL,YAAY;SACb,CAAC,CAAC;QAEH,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,KAAK,UAAU,0BAA0B,CACvC,OAA4D;IAE5D,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC,SAAS,CAAC,CAAC;IAClC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,sDAAsD,EAAE,KAAK,CAAC,CAAC;QACnE,OAAO,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC3B,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAAY,EACZ,YAAiB,EACjB,cAAuB;IAEvB,MAAM,cAAc,GAAG,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;IACpD,MAAM,QAAQ,GACZ,YAAY,CAAC,WAAW,EAAE,KAAK,cAAc,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;IAEvE,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,QAAQ,GAAG,cAAc,IAAI,yBAAyB,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QAE5E,OAAO,eAAe,CACpB,QAAQ,EACR,cAAc,EACd,QAAQ,EACR,MAAM,CAAC,QAAQ,CAAC,CACjB,CAAC;IACJ,CAAC;IAED,OAAO,eAAe,CAAC,QAAQ,EAAE,cAAc,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;AAC1E,CAAC;AAED,MAAM,CAAN,IAAY,kBAGX;AAHD,WAAY,kBAAkB;IAC5B,qCAAe,CAAA;IACf,2CAAqB,CAAA;AACvB,CAAC,EAHW,kBAAkB,KAAlB,kBAAkB,QAG7B;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,qBAAqB,CACnC,YAAiB,EACjB,OAAY,EACZ,MAA2B;IAE3B,IAAI,OAAO,KAAK,gBAAgB,EAAE,CAAC;QACjC,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,MAAM,kBAAkB,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC,WAAW,EAAS,CAAC;IACxE,MAAM,sBAAsB,GAAG,YAAY,CAAC,WAAW,EAAE,CAAC;IAE1D,IACE,MAAM,KAAK,kBAAkB,CAAC,KAAK;QACnC,sBAAsB,KAAK,kBAAkB,EAC7C,CAAC;QACD,OAAO,oBAAoB,CAAC;IAC9B,CAAC;IAED,IACE,MAAM,KAAK,kBAAkB,CAAC,QAAQ;QACtC,sBAAsB,KAAK,oBAAoB,CAAC,WAAW,EAAE,EAC7D,CAAC;QACD,OAAO,kBAAkB,CAAC;IAC5B,CAAC;IAED,OAAO,YAAY,CAAC;AACtB,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,SAAS,oBAAoB,CAC3B,UAA+C,EAC/C,OAAY,EACZ,YAAiB;IAEjB,MAAM,QAAQ,GAAG,GAAG,OAAO,IAAI,YAAY,CAAC,WAAW,EAAE,EAAE,CAAC;IAC5D,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAEtC,IAAI,MAAM,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACjC,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,OAAO,GAAG,wBAAwB,CAAC,UAAU,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;IAE5E,0EAA0E;IAC1E,sEAAsE;IACtE,IAAI,OAAO,IAAI,OAAO,KAAK,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;QACjD,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAClC,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,SAAS,wBAAwB,CAC/B,UAA+C,EAC/C,OAAY,EACZ,YAAiB;IAEjB,MAAM,WAAW,GAAG,UAAU,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC;IACtD,MAAM,QAAQ,GACZ,YAAY,CAAC,WAAW,EAAE,KAAK,cAAc,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;IAEvE,IAAI,QAAQ,EAAE,CAAC;QACb,KAAK,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;YACzD,IAAI,GAAG,CAAC,UAAU,CAAC,WAAW,CAAC,IAAI,QAAQ,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC9D,OAAO,GAAoB,CAAC;YAC9B,CAAC;QACH,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,cAAc,GAAG,GAAG,WAAW,SAAS,YAAY,EAAmB,CAAC;IAC9E,MAAM,MAAM,GAAG,cAAc,CAAC,WAAW,EAAE,CAAC;IAE5C,yEAAyE;IACzE,IAAI,cAAc,KAAK,MAAM,IAAI,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;QAC5D,OAAO,cAAc,CAAC;IACxB,CAAC;IAED,sEAAsE;IACtE,IAAI,cAAyC,CAAC;IAE9C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QAC1C,IAAI,GAAG,CAAC,WAAW,EAAE,KAAK,MAAM,EAAE,CAAC;YACjC,SAAS;QACX,CAAC;QAED,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;YACnB,OAAO,GAAoB,CAAC;QAC9B,CAAC;QAED,cAAc,GAAG,GAAoB,CAAC;IACxC,CAAC;IAED,OAAO,cAAc,CAAC;AACxB,CAAC","sourcesContent":["import { Interface } from '@ethersproject/abi';\nimport type { AssetsControllerState } from '@metamask/assets-controller';\nimport { abiERC20 } from '@metamask/metamask-eth-abis';\nimport { createModuleLogger } from '@metamask/utils';\nimport type { CaipAssetType, Hex } from '@metamask/utils';\nimport { hexToBigInt, toCaipAssetType } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport {\n CHAIN_ID_POLYGON,\n NATIVE_TOKEN_ADDRESS,\n SLIP44_COIN_TYPE_BY_CHAIN,\n} from '../constants.js';\nimport { projectLogger } from '../logger.js';\nimport type { FiatRates, TransactionPayControllerMessenger } from '../types.js';\nimport { getStablecoins, isChainExcludedFromInfura } from './feature-flags.js';\nimport { rpcRequest } from './provider.js';\n\nconst log = createModuleLogger(projectLogger, 'token');\n\n/**\n * Resolved CAIP-19 asset IDs, keyed by chain ID and lower-cased address.\n *\n * An asset ID is a static property of its chain and address, so one resolved\n * from any state snapshot stays correct for the lifetime of the process.\n */\nconst assetIds = new Map<string, CaipAssetType>();\n\n/**\n * Check if two tokens are the same (same address and chain).\n *\n * @param token1 - First token identifier.\n * @param token1.address - Token address.\n * @param token1.chainId - Token chain ID.\n * @param token2 - Second token identifier.\n * @param token2.address - Token address.\n * @param token2.chainId - Token chain ID.\n * @returns True if tokens are the same, false otherwise.\n */\nexport function isSameToken(\n token1: { address: Hex; chainId: Hex },\n token2: { address: Hex; chainId: Hex },\n): boolean {\n return (\n token1.address.toLowerCase() === token2.address.toLowerCase() &&\n token1.chainId === token2.chainId\n );\n}\n\n/**\n * Get the token balance for a specific account and token.\n *\n * @param messenger - Controller messenger.\n * @param account - Address of the account.\n * @param chainId - Id of the chain.\n * @param tokenAddress - Address of the token contract.\n * @returns Raw token balance as a decimal string.\n */\nexport function getTokenBalance(\n messenger: TransactionPayControllerMessenger,\n account: Hex,\n chainId: Hex,\n tokenAddress: Hex,\n): string {\n const { accountIdByAddress } = messenger.call('AccountsController:getState');\n\n // Fall back to the lower-cased address, as a miss is usually attributable to\n // a checksummed address being passed. Mirrors AccountsController#getAccountByAddress.\n const accountId =\n accountIdByAddress[account] ??\n accountIdByAddress[account.toLowerCase() as Hex];\n\n if (!accountId) {\n return '0';\n }\n\n const { assetsBalance, assetsInfo } = messenger.call(\n 'AssetsController:getState',\n );\n const assetId = getControllerAssetId(assetsInfo, chainId, tokenAddress);\n\n if (!assetId) {\n return '0';\n }\n\n const amount = assetsBalance[accountId]?.[assetId]?.amount;\n\n if (amount === undefined) {\n return '0';\n }\n\n // AssetsController stores human-readable decimal amounts, whereas callers of\n // this function expect raw base units. Shift by the token decimals to convert.\n // The asset ID resolved above is a key of `assetsInfo`, so its metadata is\n // read from the snapshot already in hand rather than re-fetched.\n const { decimals } = assetsInfo[assetId];\n\n return new BigNumber(amount)\n .shiftedBy(Number(decimals))\n .toFixed(0, BigNumber.ROUND_DOWN);\n}\n\n/**\n * Get the decimals and symbol for a specific token.\n *\n * `AssetsController` is the sole source of truth: a token absent from unified\n * state is treated as non-existent rather than being reconstructed from network\n * configuration. Consumers must therefore ensure unified asset state covers\n * every chain Pay is used on.\n *\n * @param messenger - Controller messenger.\n * @param tokenAddress - Address of the token contract.\n * @param chainId - Id of the chain.\n * @returns The token decimals and symbol, or undefined if the token is not found.\n */\nexport function getTokenInfo(\n messenger: TransactionPayControllerMessenger,\n tokenAddress: Hex,\n chainId: Hex,\n): { decimals: number; symbol: string } | undefined {\n const { assetsInfo } = messenger.call('AssetsController:getState');\n const assetId = getControllerAssetId(assetsInfo, chainId, tokenAddress);\n const token = assetId ? assetsInfo[assetId] : undefined;\n\n if (!token) {\n return undefined;\n }\n\n return { decimals: Number(token.decimals), symbol: token.symbol };\n}\n\n/**\n * Calculate fiat rates for a specific token.\n *\n * @param messenger - Controller messenger.\n * @param tokenAddress - Address of the token contract.\n * @param chainId - Id of the chain.\n * @returns An object containing the USD and fiat rates, or undefined if rates are not available.\n */\nexport function getTokenFiatRate(\n messenger: TransactionPayControllerMessenger,\n tokenAddress: Hex,\n chainId: Hex,\n): FiatRates | undefined {\n const { assetsInfo, assetsPrice } = messenger.call(\n 'AssetsController:getState',\n );\n const assetId = getControllerAssetId(assetsInfo, chainId, tokenAddress);\n const price = assetId ? assetsPrice[assetId] : undefined;\n\n if (price?.assetPriceType !== 'fungible') {\n return undefined;\n }\n\n const isStablecoin = getStablecoins(messenger)[chainId]?.includes(\n tokenAddress.toLowerCase() as Hex,\n );\n\n return {\n fiatRate: String(price.price),\n usdRate: isStablecoin ? '1' : String(price.usdPrice),\n };\n}\n\n/**\n * Calculate the human-readable, raw, USD, and fiat representations of a token amount.\n *\n * @param rawInput - Raw token amount (decimal string, hex, or BigNumber).\n * @param decimals - Number of decimals for the token.\n * @param fiatRates - Fiat rates for the token.\n * @returns Object containing the amount in raw, human-readable, USD, and fiat formats.\n */\nexport function computeTokenAmounts(\n rawInput: BigNumber.Value,\n decimals: number,\n fiatRates: FiatRates,\n): {\n raw: string;\n human: string;\n usd: string;\n fiat: string;\n} {\n const rawValue = new BigNumber(rawInput);\n const humanValue = rawValue.shiftedBy(-decimals);\n\n return {\n raw: rawValue.toFixed(0),\n human: humanValue.toString(10),\n usd: humanValue.multipliedBy(fiatRates.usdRate).toString(10),\n fiat: humanValue.multipliedBy(fiatRates.fiatRate).toString(10),\n };\n}\n\n/**\n * Compute a raw token amount from a fiat (USD) amount.\n * This is the inverse of `computeTokenAmounts` — it goes from USD to raw.\n *\n * @param fiatAmount - Amount in fiat/USD.\n * @param decimals - Token decimals.\n * @param usdRate - USD rate for the token (price per one unit of the token).\n * @returns Raw token amount string, or undefined if the conversion produces an invalid result.\n */\nexport function computeRawFromFiatAmount(\n fiatAmount: BigNumber.Value,\n decimals: number,\n usdRate: BigNumber.Value,\n): string | undefined {\n const rate = new BigNumber(usdRate);\n if (!rate.isFinite() || !rate.gt(0)) {\n return undefined;\n }\n\n const humanAmount = new BigNumber(fiatAmount).dividedBy(rate);\n if (!humanAmount.isFinite() || !humanAmount.gt(0)) {\n return undefined;\n }\n\n const raw = humanAmount\n .shiftedBy(decimals)\n .decimalPlaces(0, BigNumber.ROUND_DOWN)\n .toFixed(0);\n\n return new BigNumber(raw).gt(0) ? raw : undefined;\n}\n\n/**\n * Get the native token address for a given chain ID.\n *\n * @param chainId - Chain ID.\n * @returns - Native token address for the given chain ID.\n */\nexport function getNativeToken(chainId: Hex): Hex {\n switch (chainId) {\n case '0x89':\n return '0x0000000000000000000000000000000000001010';\n default:\n return NATIVE_TOKEN_ADDRESS;\n }\n}\n\n/**\n * Get the live on-chain token balance via an RPC `eth_call` to the ERC-20\n * `balanceOf` function, or `eth_getBalance` for native tokens.\n *\n * Unlike {@link getTokenBalance}, this bypasses cached AssetsController state\n * and reads directly from the chain.\n *\n * Uses the Infura RPC endpoint for the chain when one is configured, falling\n * back to the chain's default endpoint. This avoids errors on custom mainnet\n * RPC endpoints that may not support pending block queries.\n *\n * @param messenger - Controller messenger.\n * @param account - Address of the account.\n * @param chainId - Chain ID.\n * @param tokenAddress - Address of the token contract.\n * @returns Raw token balance as a decimal string.\n */\nexport async function getLiveTokenBalance(\n messenger: TransactionPayControllerMessenger,\n account: Hex,\n chainId: Hex,\n tokenAddress: Hex,\n): Promise<string> {\n const options = {\n preferInfura: !isChainExcludedFromInfura(messenger, chainId),\n };\n const isNative =\n tokenAddress.toLowerCase() === getNativeToken(chainId).toLowerCase();\n\n if (isNative) {\n const result = await requestBalanceWithFallback((blockTag) =>\n rpcRequest<string>({\n messenger,\n chainId,\n method: 'eth_getBalance',\n params: [account, blockTag],\n options,\n }),\n );\n\n return new BigNumber(result, 16).toString(10);\n }\n\n const calldata = new Interface(abiERC20).encodeFunctionData('balanceOf', [\n account,\n ]) as Hex;\n\n const result = await requestBalanceWithFallback((blockTag) =>\n rpcRequest<string>({\n messenger,\n chainId,\n method: 'eth_call',\n params: [{ to: tokenAddress, data: calldata }, blockTag],\n options,\n }),\n );\n\n return new BigNumber(result, 16).toString(10);\n}\n\n/**\n * Read the live on-chain token balance, returning `undefined` instead of\n * throwing when the RPC request fails.\n *\n * An RPC failure means the balance is *unknown*, which is not the same as the\n * balance being *insufficient*. Callers use the balance to fail fast before\n * simulation and submission; when it cannot be read they should skip that\n * optimisation rather than block the user, since simulation and the on-chain\n * transaction still catch a genuine shortfall.\n *\n * @param messenger - Controller messenger.\n * @param account - Address of the account.\n * @param chainId - Chain ID.\n * @param tokenAddress - Address of the token contract.\n * @returns Raw token balance as a decimal string, or `undefined` if the\n * balance could not be read.\n */\nexport async function getLiveTokenBalanceOrUndefined(\n messenger: TransactionPayControllerMessenger,\n account: Hex,\n chainId: Hex,\n tokenAddress: Hex,\n): Promise<string | undefined> {\n try {\n return await getLiveTokenBalance(messenger, account, chainId, tokenAddress);\n } catch (error) {\n log('Could not read live token balance, skipping balance check', {\n account,\n chainId,\n error,\n tokenAddress,\n });\n\n return undefined;\n }\n}\n\n/**\n * Request a balance using the `pending` block tag, falling back to `latest`\n * if the `pending` query throws.\n *\n * Some custom RPC endpoints do not support `pending` block queries. When the\n * `pending` request fails, retry with `latest` so a live balance can still be\n * resolved.\n *\n * @param request - Function that performs the balance request for a given\n * block tag.\n * @returns Raw balance result as a hex string.\n */\nasync function requestBalanceWithFallback(\n request: (blockTag: 'pending' | 'latest') => Promise<string>,\n): Promise<string> {\n try {\n return await request('pending');\n } catch (error) {\n log('Pending balance query failed, falling back to latest', error);\n return request('latest');\n }\n}\n\n/**\n * Build a CAIP-19 asset type identifier for an EVM token.\n *\n * The identifier is derived purely from its arguments, so it describes the\n * token as the wider ecosystem names it rather than as this wallet happens to\n * have indexed it. Use it for identifiers sent to third parties, such as Ramps\n * order assets; use `AssetsController` state for anything read back from the\n * wallet's own asset metadata, balances, or prices.\n *\n * For native tokens the SLIP-44 coin type is resolved automatically from\n * a built-in chain→coin-type map, falling back to 60 (ETH). Callers can\n * override via the optional third parameter.\n *\n * @param chainId - Hex chain ID (e.g. `0x1`).\n * @param tokenAddress - Token contract address, or the native token address.\n * @param slip44CoinType - Optional SLIP-44 coin type override for native tokens.\n * @returns CAIP-19 asset type string.\n */\nexport function buildCaipAssetType(\n chainId: Hex,\n tokenAddress: Hex,\n slip44CoinType?: number,\n): CaipAssetType {\n const chainReference = String(hexToBigInt(chainId));\n const isNative =\n tokenAddress.toLowerCase() === getNativeToken(chainId).toLowerCase();\n\n if (isNative) {\n const coinType = slip44CoinType ?? SLIP44_COIN_TYPE_BY_CHAIN[chainId] ?? 60;\n\n return toCaipAssetType(\n 'eip155',\n chainReference,\n 'slip44',\n String(coinType),\n );\n }\n\n return toCaipAssetType('eip155', chainReference, 'erc20', tokenAddress);\n}\n\nexport enum TokenAddressTarget {\n Relay = 'relay',\n MetaMask = 'metamask',\n}\n\n/**\n * Normalize token address formats between MetaMask and Relay for Polygon native\n * token handling.\n *\n * MetaMask uses Polygon's native token contract-like address (`0x...1010`),\n * while Relay expects the zero address for native tokens.\n *\n * @param tokenAddress - Token address to normalize.\n * @param chainId - Chain ID for the token.\n * @param target - Optional target system format.\n * @returns Normalized token address for the target system, or the original\n * address if no target is provided.\n */\nexport function normalizeTokenAddress(\n tokenAddress: Hex,\n chainId: Hex,\n target?: TokenAddressTarget,\n): Hex {\n if (chainId !== CHAIN_ID_POLYGON) {\n return tokenAddress;\n }\n\n const nativeTokenAddress = getNativeToken(chainId).toLowerCase() as Hex;\n const normalizedTokenAddress = tokenAddress.toLowerCase();\n\n if (\n target === TokenAddressTarget.Relay &&\n normalizedTokenAddress === nativeTokenAddress\n ) {\n return NATIVE_TOKEN_ADDRESS;\n }\n\n if (\n target === TokenAddressTarget.MetaMask &&\n normalizedTokenAddress === NATIVE_TOKEN_ADDRESS.toLowerCase()\n ) {\n return nativeTokenAddress;\n }\n\n return tokenAddress;\n}\n\n/**\n * Locate an asset's key in `AssetsController` state, reusing a previously\n * resolved identifier.\n *\n * A cached identifier is still confirmed against the given state, since an\n * asset `AssetsController` has not indexed yet is unresolvable no matter how\n * its identifier would be spelled. That check is a keyed read, so the cache\n * saves the scan below without letting a stale identifier through.\n *\n * Only resolved checksummed identifiers are cached, so an asset missing at\n * first lookup, or resolved only to a lowercase duplicate, is retried and\n * picked up once `AssetsController` indexes its checksummed entry.\n *\n * @param assetsInfo - Asset metadata keyed by CAIP-19 ID.\n * @param chainId - Hex chain ID.\n * @param tokenAddress - Token address, or the native token address.\n * @returns The CAIP-19 asset ID as keyed in state, or undefined when the asset\n * is absent.\n */\nfunction getControllerAssetId(\n assetsInfo: AssetsControllerState['assetsInfo'],\n chainId: Hex,\n tokenAddress: Hex,\n): CaipAssetType | undefined {\n const cacheKey = `${chainId}:${tokenAddress.toLowerCase()}`;\n const cached = assetIds.get(cacheKey);\n\n if (cached && assetsInfo[cached]) {\n return cached;\n }\n\n const assetId = resolveControllerAssetId(assetsInfo, chainId, tokenAddress);\n\n // Only cache checksummed (mixed-case) keys, so a lowercase duplicate seen\n // before the canonical entry is indexed cannot stick for the session.\n if (assetId && assetId !== assetId.toLowerCase()) {\n assetIds.set(cacheKey, assetId);\n }\n\n return assetId;\n}\n\n/**\n * Locate an asset's key in `AssetsController` state.\n *\n * An ERC-20 identifier is fully derivable from the chain and address, so for a\n * checksummed address the derived form is tried against state first —\n * client-selected tokens are already checksummed and hit immediately. Other\n * addresses, such as lowercase ones recovered from transaction calldata, fall\n * through to a case-insensitive search, which avoids paying for a checksum on\n * the hot path. That search prefers the checksummed key, since state may also\n * hold a stale lowercase duplicate without price or balance data.\n *\n * A native identifier cannot be derived, because MetaMask keys natives\n * inconsistently across chains — `slip44:`, `erc20:` with the zero address, or\n * a chain-specific sentinel address — so the chain's sole entry of type\n * `native` is searched for instead.\n *\n * An asset absent from state is unresolvable rather than assumed, so a derived\n * identifier state cannot confirm is never returned.\n *\n * @param assetsInfo - Asset metadata keyed by CAIP-19 ID.\n * @param chainId - Hex chain ID.\n * @param tokenAddress - Token address, or the native token address.\n * @returns The CAIP-19 asset ID as keyed in state, or undefined when the asset\n * is absent.\n */\nfunction resolveControllerAssetId(\n assetsInfo: AssetsControllerState['assetsInfo'],\n chainId: Hex,\n tokenAddress: Hex,\n): CaipAssetType | undefined {\n const chainPrefix = `eip155:${hexToBigInt(chainId)}/`;\n const isNative =\n tokenAddress.toLowerCase() === getNativeToken(chainId).toLowerCase();\n\n if (isNative) {\n for (const [key, metadata] of Object.entries(assetsInfo)) {\n if (key.startsWith(chainPrefix) && metadata.type === 'native') {\n return key as CaipAssetType;\n }\n }\n\n return undefined;\n }\n\n const derivedAssetId = `${chainPrefix}erc20:${tokenAddress}` as CaipAssetType;\n const target = derivedAssetId.toLowerCase();\n\n // Mixed-case input is already checksummed, so an exact hit is canonical.\n if (derivedAssetId !== target && assetsInfo[derivedAssetId]) {\n return derivedAssetId;\n }\n\n // Prefer the checksummed (mixed-case) key over a lowercase duplicate.\n let lowercaseMatch: CaipAssetType | undefined;\n\n for (const key of Object.keys(assetsInfo)) {\n if (key.toLowerCase() !== target) {\n continue;\n }\n\n if (key !== target) {\n return key as CaipAssetType;\n }\n\n lowercaseMatch = key as CaipAssetType;\n }\n\n return lowercaseMatch;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,cAAc,EACd,iCAAiC,EACjC,mBAAmB,EACpB,MAAM,aAAa,CAAC;AAMrB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAcvD,MAAM,MAAM,qBAAqB,GAAG;IAClC,SAAS,EAAE,iCAAiC,CAAC;IAC7C,KAAK,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC;IACpC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,UAAU,EAAE,eAAe,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,qBAAqB,CAAC,SAAS,IAAI;IAC7C;;;OAGG;IACH,eAAe,EAAE,CACf,KAAK,EAAE,mBAAmB,CAAC,SAAS,CAAC,KAClC,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAAC;IAC1C,SAAS,EAAE,iCAAiC,CAAC;IAC7C,MAAM,EAAE,mBAAmB,CAAC,SAAS,CAAC,EAAE,CAAC;IACzC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF,qBAAa,UAAW,SAAQ,KAAK;IACnC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAE9B,QAAQ,CAAC,MAAM,CAAC,EAAE,mBAAmB,CAAC,OAAO,CAAC,EAAE,CAAC;IAEjD,YAAY,IAAI,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,mBAAmB,CAAC,OAAO,CAAC,EAAE,EAKxE;CACF;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,SAAS,EAC5C,OAAO,EAAE,qBAAqB,CAAC,SAAS,CAAC,GACxC,OAAO,CAAC,IAAI,CAAC,CAkCf;AAED,wBAAsB,sBAAsB,CAAC,EAC3C,SAAS,EACT,KAAK,EACL,MAAM,EACN,UAAU,GACX,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CA0CvC;AAsCD,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAEhE"}
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,cAAc,EACd,iCAAiC,EACjC,mBAAmB,EACpB,MAAM,aAAa,CAAC;AAMrB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAavD,MAAM,MAAM,qBAAqB,GAAG;IAClC,SAAS,EAAE,iCAAiC,CAAC;IAC7C,KAAK,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC;IACpC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,UAAU,EAAE,eAAe,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,qBAAqB,CAAC,SAAS,IAAI;IAC7C;;;OAGG;IACH,eAAe,EAAE,CACf,KAAK,EAAE,mBAAmB,CAAC,SAAS,CAAC,KAClC,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAAC;IAC1C,SAAS,EAAE,iCAAiC,CAAC;IAC7C,MAAM,EAAE,mBAAmB,CAAC,SAAS,CAAC,EAAE,CAAC;IACzC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF,qBAAa,UAAW,SAAQ,KAAK;IACnC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAE9B,QAAQ,CAAC,MAAM,CAAC,EAAE,mBAAmB,CAAC,OAAO,CAAC,EAAE,CAAC;IAEjD,YAAY,IAAI,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,mBAAmB,CAAC,OAAO,CAAC,EAAE,EAKxE;CACF;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,SAAS,EAC5C,OAAO,EAAE,qBAAqB,CAAC,SAAS,CAAC,GACxC,OAAO,CAAC,IAAI,CAAC,CAkCf;AAED,wBAAsB,sBAAsB,CAAC,EAC3C,SAAS,EACT,KAAK,EACL,MAAM,EACN,UAAU,GACX,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuCvC;AAoCD,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAEhE"}
@@ -3,7 +3,7 @@ import { abiERC20 } from '@metamask/metamask-eth-abis';
3
3
  import { BigNumber } from 'bignumber.js';
4
4
  import { createModuleLogger, projectLogger } from '../logger.js';
5
5
  import { simulateQuoteTransactions, TransactionPaySimulationError, } from './simulation.js';
6
- import { getEvmQuoteSource, getLiveTokenBalanceOrUndefined, getNativeToken, getTokenInfo, normalizeTokenAddress, TokenAddressTarget, } from './token.js';
6
+ import { getLiveTokenBalanceOrUndefined, getNativeToken, getTokenInfo, normalizeTokenAddress, TokenAddressTarget, } from './token.js';
7
7
  const log = createModuleLogger(projectLogger, 'validation');
8
8
  const erc20Interface = new Interface(abiERC20);
9
9
  export class QuoteError extends Error {
@@ -54,13 +54,12 @@ export async function validateQuotes(request) {
54
54
  }
55
55
  export async function validateQuoteExecution({ messenger, quote, signal, simulation, }) {
56
56
  throwIfAborted(signal);
57
- const { chainId: sourceChainId, tokenAddress: sourceTokenAddress } = getEvmQuoteSource(quote.request);
58
57
  const liveBalance = await getLiveSourceBalance(quote, messenger);
59
58
  log('Live source balance', {
60
59
  from: quote.request.from,
61
60
  liveBalance,
62
- sourceChainId,
63
- sourceTokenAddress,
61
+ sourceChainId: quote.request.sourceChainId,
62
+ sourceTokenAddress: quote.request.sourceTokenAddress,
64
63
  });
65
64
  throwIfAborted(signal);
66
65
  log('Checking quote source amount', {
@@ -71,8 +70,8 @@ export async function validateQuoteExecution({ messenger, quote, signal, simulat
71
70
  });
72
71
  validateRequiredSourceAmount(messenger, quote, liveBalance);
73
72
  log('Checking decoded source transfers', {
74
- sourceChainId,
75
- sourceTokenAddress,
73
+ sourceChainId: quote.request.sourceChainId,
74
+ sourceTokenAddress: quote.request.sourceTokenAddress,
76
75
  transactions: simulation.transactions,
77
76
  });
78
77
  validateDecodedSourceTransfers(messenger, quote, liveBalance, simulation.transactions);
@@ -80,14 +79,13 @@ export async function validateQuoteExecution({ messenger, quote, signal, simulat
80
79
  await validateSimulation(messenger, quote, simulation, signal);
81
80
  }
82
81
  async function validateSimulation(messenger, quote, simulation, signal) {
83
- const { chainId } = getEvmQuoteSource(quote.request);
84
82
  log('Starting simulation', {
85
- chainId,
83
+ chainId: quote.request.sourceChainId,
86
84
  transactions: simulation.transactions,
87
85
  });
88
86
  try {
89
87
  await simulateQuoteTransactions({
90
- chainId,
88
+ chainId: quote.request.sourceChainId,
91
89
  messenger,
92
90
  transactions: simulation.transactions,
93
91
  });
@@ -122,8 +120,8 @@ export function isQuoteError(error) {
122
120
  * @returns Detail rows: Required / Current / Missing.
123
121
  */
124
122
  function formatBalanceShortfall(messenger, quote, required, balance) {
125
- const { chainId, tokenAddress } = getEvmQuoteSource(quote.request);
126
- const tokenInfo = getTokenInfo(messenger, tokenAddress, chainId);
123
+ const { sourceChainId, sourceTokenAddress } = quote.request;
124
+ const tokenInfo = getTokenInfo(messenger, sourceTokenAddress, sourceChainId);
127
125
  const requiredBn = new BigNumber(required);
128
126
  const balanceBn = new BigNumber(balance);
129
127
  // Only ever called when balance < required, so the shortfall is positive.
@@ -158,8 +156,7 @@ function formatTokenAmount(rawAmount, tokenInfo) {
158
156
  * request failed.
159
157
  */
160
158
  async function getLiveSourceBalance(quote, messenger) {
161
- const { from } = quote.request;
162
- const { chainId: sourceChainId, tokenAddress: sourceTokenAddress } = getEvmQuoteSource(quote.request);
159
+ const { from, sourceChainId, sourceTokenAddress } = quote.request;
163
160
  const normalizedSourceTokenAddress = normalizeTokenAddress(sourceTokenAddress, sourceChainId, TokenAddressTarget.MetaMask);
164
161
  return await getLiveTokenBalanceOrUndefined(messenger, from, sourceChainId, normalizedSourceTokenAddress);
165
162
  }
@@ -222,7 +219,7 @@ function isSourceTokenTransfer(quote, transaction) {
222
219
  if (!transaction?.to || !transaction.data) {
223
220
  return false;
224
221
  }
225
- const { chainId: sourceChainId, tokenAddress: sourceTokenAddress } = getEvmQuoteSource(quote.request);
222
+ const { sourceChainId, sourceTokenAddress } = quote.request;
226
223
  const isNativeSource = sourceTokenAddress.toLowerCase() ===
227
224
  getNativeToken(sourceChainId).toLowerCase();
228
225
  if (isNativeSource) {
@@ -1 +1 @@
1
- {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAEvD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAMjE,OAAO,EAEL,yBAAyB,EACzB,6BAA6B,GAC9B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,iBAAiB,EACjB,8BAA8B,EAC9B,cAAc,EACd,YAAY,EACZ,qBAAqB,EACrB,kBAAkB,GACnB,MAAM,YAAY,CAAC;AAEpB,MAAM,GAAG,GAAG,kBAAkB,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;AAE5D,MAAM,cAAc,GAAG,IAAI,SAAS,CAAC,QAAQ,CAAC,CAAC;AAsB/C,MAAM,OAAO,UAAW,SAAQ,KAAK;IAC1B,IAAI,CAAiB;IAErB,MAAM,CAAkC;IAEjD,YAAY,IAAoB,EAAE,MAAuC;QACvE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAAyC;IAEzC,MAAM,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAE/D,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,MAAM,eAAe,CAAC,KAAK,CAAC,CAAC;YAEhD,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,SAAS;YACX,CAAC;YAED,MAAM,sBAAsB,CAAC;gBAC3B,SAAS;gBACT,KAAK;gBACL,MAAM;gBACN,UAAU;aACX,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;gBACpB,MAAM,KAAK,CAAC;YACd,CAAC;YAED,MAAM,UAAU,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;YAEvC,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,KAAK,6BAA6B,EAAE,CAAC;gBAC7D,kEAAkE;gBAClE,iEAAiE;gBACjE,oEAAoE;gBACpE,MAAM,IAAI,UAAU,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAChD,CAAC;YAED,MAAM,UAAU,CAAC;QACnB,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,EAC3C,SAAS,EACT,KAAK,EACL,MAAM,EACN,UAAU,GACY;IACtB,cAAc,CAAC,MAAM,CAAC,CAAC;IAEvB,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,kBAAkB,EAAE,GAChE,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAEnC,MAAM,WAAW,GAAG,MAAM,oBAAoB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IAEjE,GAAG,CAAC,qBAAqB,EAAE;QACzB,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI;QACxB,WAAW;QACX,aAAa;QACb,kBAAkB;KACnB,CAAC,CAAC;IAEH,cAAc,CAAC,MAAM,CAAC,CAAC;IAEvB,GAAG,CAAC,8BAA8B,EAAE;QAClC,kBAAkB,EAAE,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,eAAe,CAAC;QAC1D,WAAW,EAAE,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC;QAC/C,WAAW;QACX,cAAc,EAAE,KAAK,CAAC,YAAY,CAAC,GAAG;KACvC,CAAC,CAAC;IAEH,4BAA4B,CAAC,SAAS,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC;IAE5D,GAAG,CAAC,mCAAmC,EAAE;QACvC,aAAa;QACb,kBAAkB;QAClB,YAAY,EAAE,UAAU,CAAC,YAAY;KACtC,CAAC,CAAC;IAEH,8BAA8B,CAC5B,SAAS,EACT,KAAK,EACL,WAAW,EACX,UAAU,CAAC,YAAY,CACxB,CAAC;IAEF,cAAc,CAAC,MAAM,CAAC,CAAC;IAEvB,MAAM,kBAAkB,CAAC,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;AACjE,CAAC;AAED,KAAK,UAAU,kBAAkB,CAC/B,SAA4C,EAC5C,KAAmC,EACnC,UAA2B,EAC3B,MAAoB;IAEpB,MAAM,EAAE,OAAO,EAAE,GAAG,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAErD,GAAG,CAAC,qBAAqB,EAAE;QACzB,OAAO;QACP,YAAY,EAAE,UAAU,CAAC,YAAY;KACtC,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,yBAAyB,CAAC;YAC9B,OAAO;YACP,SAAS;YACT,YAAY,EAAE,UAAU,CAAC,YAAY;SACtC,CAAC,CAAC;QAEH,GAAG,CAAC,mBAAmB,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,cAAc,CAAC,MAAM,CAAC,CAAC;QAEvB,IAAI,KAAK,YAAY,6BAA6B,EAAE,CAAC;YACnD,MAAM,IAAI,UAAU,CAAC;gBACnB,OAAO,EAAE,yBAAyB;gBAClC,MAAM,EAAE,mBAAmB;gBAC3B,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC;aACxB,CAAC,CAAC;QACL,CAAC;QAED,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,OAAO,KAAK,YAAY,UAAU,CAAC;AACrC,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAS,sBAAsB,CAC7B,SAA4C,EAC5C,KAAmC,EACnC,QAAgB,EAChB,OAAe;IAEf,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,GAAG,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACnE,MAAM,SAAS,GAAG,YAAY,CAAC,SAAS,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC;IAEjE,MAAM,UAAU,GAAG,IAAI,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC3C,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,OAAO,CAAC,CAAC;IAEzC,0EAA0E;IAC1E,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAE5C,OAAO;QACL,aAAa,iBAAiB,CAAC,UAAU,EAAE,SAAS,CAAC,EAAE;QACvD,YAAY,iBAAiB,CAAC,SAAS,EAAE,SAAS,CAAC,EAAE;QACrD,YAAY,iBAAiB,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE;KACpD,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,iBAAiB,CACxB,SAAoB,EACpB,SAA2D;IAE3D,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,SAAS,CAAC,OAAO,EAAE,CAAC;IAC7B,CAAC;IAED,MAAM,KAAK,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC;IAEjE,OAAO,GAAG,KAAK,IAAI,SAAS,CAAC,MAAM,EAAE,CAAC;AACxC,CAAC;AAED;;;;;;;GAOG;AACH,KAAK,UAAU,oBAAoB,CACjC,KAAmC,EACnC,SAA4C;IAE5C,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC;IAC/B,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,kBAAkB,EAAE,GAChE,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACnC,MAAM,4BAA4B,GAAG,qBAAqB,CACxD,kBAAkB,EAClB,aAAa,EACb,kBAAkB,CAAC,QAAQ,CAC5B,CAAC;IAEF,OAAO,MAAM,8BAA8B,CACzC,SAAS,EACT,IAAI,EACJ,aAAa,EACb,4BAA4B,CAC7B,CAAC;AACJ,CAAC;AAED,SAAS,4BAA4B,CACnC,SAA4C,EAC5C,KAAmC,EACnC,WAA+B;IAE/B,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;QAC9B,GAAG,CAAC,8DAA8D,CAAC,CAAC;QACpE,OAAO;IACT,CAAC;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,WAAW,IAAI,KAAK,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC;QAC/D,GAAG,CAAC,oCAAoC,EAAE;YACxC,kBAAkB,EAAE,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,eAAe,CAAC;YAC1D,WAAW,EAAE,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC;SAChD,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IAED,MAAM,cAAc,GAAG,IAAI,SAAS,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IAC7D,MAAM,OAAO,GAAG,IAAI,SAAS,CAAC,WAAW,CAAC,CAAC;IAE3C,IAAI,OAAO,CAAC,sBAAsB,CAAC,cAAc,CAAC,EAAE,CAAC;QACnD,GAAG,CAAC,kCAAkC,CAAC,CAAC;QACxC,OAAO;IACT,CAAC;IAED,MAAM,IAAI,UAAU,CAAC;QACnB,OAAO,EAAE,uCAAuC;QAChD,MAAM,EAAE,6BAA6B;QACrC,MAAM,EAAE,sBAAsB,CAC5B,SAAS,EACT,KAAK,EACL,cAAc,CAAC,OAAO,EAAE,EACxB,OAAO,CAAC,OAAO,EAAE,CAClB;KACF,CAAC,CAAC;AACL,CAAC;AAED,SAAS,8BAA8B,CACrC,SAA4C,EAC5C,KAAmC,EACnC,WAA+B,EAC/B,YAAqC;IAErC,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;QAC9B,GAAG,CAAC,kEAAkE,CAAC,CAAC;QACxE,OAAO;IACT,CAAC;IAED,4EAA4E;IAC5E,4EAA4E;IAC5E,kEAAkE;IAClE,IACE,YAAY,CAAC,MAAM,KAAK,CAAC;QACzB,CAAC,qBAAqB,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,EAC9C,CAAC;QACD,GAAG,CACD,4EAA4E,CAC7E,CAAC;QACF,OAAO;IACT,CAAC;IAED,sEAAsE;IACtE,2CAA2C;IAC3C,MAAM,cAAc,GAAG,oBAAoB,CACzC,YAAY,CAAC,CAAC,CAAC,CAAC,IAAW,CAClB,CAAC;IAEZ,MAAM,OAAO,GAAG,IAAI,SAAS,CAAC,WAAW,CAAC,CAAC;IAE3C,GAAG,CAAC,gCAAgC,EAAE;QACpC,WAAW;QACX,cAAc;KACf,CAAC,CAAC;IAEH,IAAI,OAAO,CAAC,sBAAsB,CAAC,cAAc,CAAC,EAAE,CAAC;QACnD,GAAG,CAAC,uCAAuC,CAAC,CAAC;QAC7C,OAAO;IACT,CAAC;IAED,MAAM,IAAI,UAAU,CAAC;QACnB,OAAO,EAAE,kDAAkD;QAC3D,MAAM,EAAE,+BAA+B;QACvC,MAAM,EAAE,sBAAsB,CAC5B,SAAS,EACT,KAAK,EACL,cAAc,EACd,WAAW,CACZ;KACF,CAAC,CAAC;AACL,CAAC;AAED,SAAS,qBAAqB,CAC5B,KAAmC,EACnC,WAA8C;IAE9C,IAAI,CAAC,WAAW,EAAE,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;QAC1C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,kBAAkB,EAAE,GAChE,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAEnC,MAAM,cAAc,GAClB,kBAAkB,CAAC,WAAW,EAAE;QAChC,cAAc,CAAC,aAAa,CAAC,CAAC,WAAW,EAAE,CAAC;IAE9C,IAAI,cAAc,EAAE,CAAC;QACnB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,4BAA4B,GAAG,qBAAqB,CACxD,kBAAkB,EAClB,aAAa,EACb,kBAAkB,CAAC,QAAQ,CAC5B,CAAC,WAAW,EAAE,CAAC;IAEhB,MAAM,YAAY,GAAG,qBAAqB,CACxC,WAAW,CAAC,EAAE,EACd,aAAa,EACb,kBAAkB,CAAC,QAAQ,CAC5B,CAAC,WAAW,EAAE,CAAC;IAEhB,IAAI,YAAY,KAAK,4BAA4B,EAAE,CAAC;QAClD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,oBAAoB,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC;AAC9D,CAAC;AAED,SAAS,oBAAoB,CAAC,IAAS;IACrC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,cAAc,CAAC,kBAAkB,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QACnE,OAAO,IAAI,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IAClC,IAAI,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,IAAI,UAAU,CAAC;QACpB,OAAO,EAAE,yBAAyB;QAClC,MAAM,EAAE,mBAAmB;QAC3B,MAAM,EAAE,CAAE,KAAe,CAAC,OAAO,CAAC;KACnC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,cAAc,CAAC,MAAoB;IAC1C,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAC9C,CAAC;AACH,CAAC","sourcesContent":["import { Interface } from '@ethersproject/abi';\nimport { abiERC20 } from '@metamask/metamask-eth-abis';\nimport type { Hex } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport { createModuleLogger, projectLogger } from '../logger.js';\nimport type {\n QuoteErrorInfo,\n TransactionPayControllerMessenger,\n TransactionPayQuote,\n} from '../types.js';\nimport {\n SimulationTransaction,\n simulateQuoteTransactions,\n TransactionPaySimulationError,\n} from './simulation.js';\nimport type { QuoteSimulation } from './simulation.js';\nimport {\n getEvmQuoteSource,\n getLiveTokenBalanceOrUndefined,\n getNativeToken,\n getTokenInfo,\n normalizeTokenAddress,\n TokenAddressTarget,\n} from './token.js';\n\nconst log = createModuleLogger(projectLogger, 'validation');\n\nconst erc20Interface = new Interface(abiERC20);\n\nexport type QuoteExecutionRequest = {\n messenger: TransactionPayControllerMessenger;\n quote: TransactionPayQuote<unknown>;\n signal?: AbortSignal;\n simulation: QuoteSimulation;\n};\n\nexport type ValidateQuotesRequest<QuoteType> = {\n /**\n * Build the simulation for a quote, or return `undefined` to skip\n * validating it.\n */\n buildSimulation: (\n quote: TransactionPayQuote<QuoteType>,\n ) => Promise<QuoteSimulation | undefined>;\n messenger: TransactionPayControllerMessenger;\n quotes: TransactionPayQuote<QuoteType>[];\n signal?: AbortSignal;\n};\n\nexport class QuoteError extends Error {\n readonly info: QuoteErrorInfo;\n\n readonly quotes?: TransactionPayQuote<unknown>[];\n\n constructor(info: QuoteErrorInfo, quotes?: TransactionPayQuote<unknown>[]) {\n super(info.message);\n this.name = 'QuoteError';\n this.info = info;\n this.quotes = quotes;\n }\n}\n\n/**\n * Validate each quote in turn by checking the live source balance and\n * simulating the transactions it would submit.\n *\n * @param request - Validation request.\n */\nexport async function validateQuotes<QuoteType>(\n request: ValidateQuotesRequest<QuoteType>,\n): Promise<void> {\n const { buildSimulation, messenger, quotes, signal } = request;\n\n for (const quote of quotes) {\n try {\n const simulation = await buildSimulation(quote);\n\n if (!simulation) {\n continue;\n }\n\n await validateQuoteExecution({\n messenger,\n quote,\n signal,\n simulation,\n });\n } catch (error) {\n if (signal?.aborted) {\n throw error;\n }\n\n const quoteError = toQuoteError(error);\n\n if (quoteError.info.reason === 'insufficient-source-balance') {\n // Backwards compatibility: keep the entire quote batch (including\n // quotes that may have passed or not yet been validated) so that\n // clients can still show quote details alongside the balance error.\n throw new QuoteError(quoteError.info, quotes);\n }\n\n throw quoteError;\n }\n }\n}\n\nexport async function validateQuoteExecution({\n messenger,\n quote,\n signal,\n simulation,\n}: QuoteExecutionRequest): Promise<void> {\n throwIfAborted(signal);\n\n const { chainId: sourceChainId, tokenAddress: sourceTokenAddress } =\n getEvmQuoteSource(quote.request);\n\n const liveBalance = await getLiveSourceBalance(quote, messenger);\n\n log('Live source balance', {\n from: quote.request.from,\n liveBalance,\n sourceChainId,\n sourceTokenAddress,\n });\n\n throwIfAborted(signal);\n\n log('Checking quote source amount', {\n hasPaymentOverride: Boolean(quote.request.paymentOverride),\n isPostQuote: Boolean(quote.request.isPostQuote),\n liveBalance,\n requiredAmount: quote.sourceAmount.raw,\n });\n\n validateRequiredSourceAmount(messenger, quote, liveBalance);\n\n log('Checking decoded source transfers', {\n sourceChainId,\n sourceTokenAddress,\n transactions: simulation.transactions,\n });\n\n validateDecodedSourceTransfers(\n messenger,\n quote,\n liveBalance,\n simulation.transactions,\n );\n\n throwIfAborted(signal);\n\n await validateSimulation(messenger, quote, simulation, signal);\n}\n\nasync function validateSimulation(\n messenger: TransactionPayControllerMessenger,\n quote: TransactionPayQuote<unknown>,\n simulation: QuoteSimulation,\n signal?: AbortSignal,\n): Promise<void> {\n const { chainId } = getEvmQuoteSource(quote.request);\n\n log('Starting simulation', {\n chainId,\n transactions: simulation.transactions,\n });\n\n try {\n await simulateQuoteTransactions({\n chainId,\n messenger,\n transactions: simulation.transactions,\n });\n\n log('Simulation passed');\n } catch (error) {\n throwIfAborted(signal);\n\n if (error instanceof TransactionPaySimulationError) {\n throw new QuoteError({\n message: 'Quote simulation failed',\n reason: 'simulation-failed',\n detail: [error.message],\n });\n }\n\n throw error;\n }\n}\n\nexport function isQuoteError(error: unknown): error is QuoteError {\n return error instanceof QuoteError;\n}\n\n/**\n * Format an amount shortfall into display-ready detail rows.\n *\n * Amounts are formatted using the source token decimals and symbol when\n * available (e.g. `Required: 1.5 USDC`), falling back to the raw atomic values\n * otherwise.\n *\n * @param messenger - Controller messenger.\n * @param quote - Quote being validated.\n * @param required - Required amount (raw string).\n * @param balance - Current balance (raw string).\n * @returns Detail rows: Required / Current / Missing.\n */\nfunction formatBalanceShortfall(\n messenger: TransactionPayControllerMessenger,\n quote: TransactionPayQuote<unknown>,\n required: string,\n balance: string,\n): string[] {\n const { chainId, tokenAddress } = getEvmQuoteSource(quote.request);\n const tokenInfo = getTokenInfo(messenger, tokenAddress, chainId);\n\n const requiredBn = new BigNumber(required);\n const balanceBn = new BigNumber(balance);\n\n // Only ever called when balance < required, so the shortfall is positive.\n const missing = requiredBn.minus(balanceBn);\n\n return [\n `Required: ${formatTokenAmount(requiredBn, tokenInfo)}`,\n `Current: ${formatTokenAmount(balanceBn, tokenInfo)}`,\n `Missing: ${formatTokenAmount(missing, tokenInfo)}`,\n ];\n}\n\n/**\n * Format an atomic token amount as a human-readable string with symbol.\n *\n * @param rawAmount - Amount in atomic units.\n * @param tokenInfo - Source token decimals and symbol, if available.\n * @returns Human-readable amount suffixed with the token symbol when known,\n * otherwise the raw atomic value.\n */\nfunction formatTokenAmount(\n rawAmount: BigNumber,\n tokenInfo: { decimals: number; symbol: string } | undefined,\n): string {\n if (!tokenInfo) {\n return rawAmount.toFixed();\n }\n\n const human = rawAmount.shiftedBy(-tokenInfo.decimals).toFixed();\n\n return `${human} ${tokenInfo.symbol}`;\n}\n\n/**\n * Read the live source token balance, or `undefined` if it cannot be read.\n *\n * @param quote - Quote being validated.\n * @param messenger - Controller messenger.\n * @returns Raw balance as a decimal string, or `undefined` when the RPC\n * request failed.\n */\nasync function getLiveSourceBalance(\n quote: TransactionPayQuote<unknown>,\n messenger: TransactionPayControllerMessenger,\n): Promise<string | undefined> {\n const { from } = quote.request;\n const { chainId: sourceChainId, tokenAddress: sourceTokenAddress } =\n getEvmQuoteSource(quote.request);\n const normalizedSourceTokenAddress = normalizeTokenAddress(\n sourceTokenAddress,\n sourceChainId,\n TokenAddressTarget.MetaMask,\n );\n\n return await getLiveTokenBalanceOrUndefined(\n messenger,\n from,\n sourceChainId,\n normalizedSourceTokenAddress,\n );\n}\n\nfunction validateRequiredSourceAmount(\n messenger: TransactionPayControllerMessenger,\n quote: TransactionPayQuote<unknown>,\n liveBalance: string | undefined,\n): void {\n if (liveBalance === undefined) {\n log('Skipping quote source amount check: live balance unavailable');\n return;\n }\n\n if (quote.request.isPostQuote || quote.request.paymentOverride) {\n log('Skipping quote source amount check', {\n hasPaymentOverride: Boolean(quote.request.paymentOverride),\n isPostQuote: Boolean(quote.request.isPostQuote),\n });\n return;\n }\n\n const requiredAmount = new BigNumber(quote.sourceAmount.raw);\n const balance = new BigNumber(liveBalance);\n\n if (balance.isGreaterThanOrEqualTo(requiredAmount)) {\n log('Quote source amount check passed');\n return;\n }\n\n throw new QuoteError({\n message: 'Insufficient source balance for quote',\n reason: 'insufficient-source-balance',\n detail: formatBalanceShortfall(\n messenger,\n quote,\n requiredAmount.toFixed(),\n balance.toFixed(),\n ),\n });\n}\n\nfunction validateDecodedSourceTransfers(\n messenger: TransactionPayControllerMessenger,\n quote: TransactionPayQuote<unknown>,\n liveBalance: string | undefined,\n transactions: SimulationTransaction[],\n): void {\n if (liveBalance === undefined) {\n log('Skipping decoded source transfer check: live balance unavailable');\n return;\n }\n\n // Only valid for a single source-token transfer. Multi-step batches produce\n // or transform the source-token balance mid-batch, so comparing against the\n // starting balance is wrong; rely on the full simulation instead.\n if (\n transactions.length !== 1 ||\n !isSourceTokenTransfer(quote, transactions[0])\n ) {\n log(\n 'Skipping decoded source transfer check: not a single source-token transfer',\n );\n return;\n }\n\n // `isSourceTokenTransfer` has already confirmed the data decodes to a\n // transfer, so the amount is defined here.\n const requiredAmount = decodeTransferAmount(\n transactions[0].data as Hex,\n ) as string;\n\n const balance = new BigNumber(liveBalance);\n\n log('Decoded source transfer amount', {\n liveBalance,\n requiredAmount,\n });\n\n if (balance.isGreaterThanOrEqualTo(requiredAmount)) {\n log('Decoded source transfers check passed');\n return;\n }\n\n throw new QuoteError({\n message: 'Insufficient source balance for decoded transfer',\n reason: 'insufficient-transfer-balance',\n detail: formatBalanceShortfall(\n messenger,\n quote,\n requiredAmount,\n liveBalance,\n ),\n });\n}\n\nfunction isSourceTokenTransfer(\n quote: TransactionPayQuote<unknown>,\n transaction: SimulationTransaction | undefined,\n): boolean {\n if (!transaction?.to || !transaction.data) {\n return false;\n }\n\n const { chainId: sourceChainId, tokenAddress: sourceTokenAddress } =\n getEvmQuoteSource(quote.request);\n\n const isNativeSource =\n sourceTokenAddress.toLowerCase() ===\n getNativeToken(sourceChainId).toLowerCase();\n\n if (isNativeSource) {\n return false;\n }\n\n const normalizedSourceTokenAddress = normalizeTokenAddress(\n sourceTokenAddress,\n sourceChainId,\n TokenAddressTarget.MetaMask,\n ).toLowerCase();\n\n const normalizedTo = normalizeTokenAddress(\n transaction.to,\n sourceChainId,\n TokenAddressTarget.MetaMask,\n ).toLowerCase();\n\n if (normalizedTo !== normalizedSourceTokenAddress) {\n return false;\n }\n\n return decodeTransferAmount(transaction.data) !== undefined;\n}\n\nfunction decodeTransferAmount(data: Hex): string | undefined {\n try {\n const result = erc20Interface.decodeFunctionData('transfer', data);\n return new BigNumber(result._value.toString()).toString(10);\n } catch {\n return undefined;\n }\n}\n\nfunction toQuoteError(error: unknown): QuoteError {\n if (isQuoteError(error)) {\n return error;\n }\n\n return new QuoteError({\n message: 'Quote simulation failed',\n reason: 'simulation-failed',\n detail: [(error as Error).message],\n });\n}\n\nfunction throwIfAborted(signal?: AbortSignal): void {\n if (signal?.aborted) {\n throw new Error('Quote validation aborted');\n }\n}\n"]}
1
+ {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"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","sourcesContent":["import { Interface } from '@ethersproject/abi';\nimport { abiERC20 } from '@metamask/metamask-eth-abis';\nimport type { Hex } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport { createModuleLogger, projectLogger } from '../logger.js';\nimport type {\n QuoteErrorInfo,\n TransactionPayControllerMessenger,\n TransactionPayQuote,\n} from '../types.js';\nimport {\n SimulationTransaction,\n simulateQuoteTransactions,\n TransactionPaySimulationError,\n} from './simulation.js';\nimport type { QuoteSimulation } from './simulation.js';\nimport {\n getLiveTokenBalanceOrUndefined,\n getNativeToken,\n getTokenInfo,\n normalizeTokenAddress,\n TokenAddressTarget,\n} from './token.js';\n\nconst log = createModuleLogger(projectLogger, 'validation');\n\nconst erc20Interface = new Interface(abiERC20);\n\nexport type QuoteExecutionRequest = {\n messenger: TransactionPayControllerMessenger;\n quote: TransactionPayQuote<unknown>;\n signal?: AbortSignal;\n simulation: QuoteSimulation;\n};\n\nexport type ValidateQuotesRequest<QuoteType> = {\n /**\n * Build the simulation for a quote, or return `undefined` to skip\n * validating it.\n */\n buildSimulation: (\n quote: TransactionPayQuote<QuoteType>,\n ) => Promise<QuoteSimulation | undefined>;\n messenger: TransactionPayControllerMessenger;\n quotes: TransactionPayQuote<QuoteType>[];\n signal?: AbortSignal;\n};\n\nexport class QuoteError extends Error {\n readonly info: QuoteErrorInfo;\n\n readonly quotes?: TransactionPayQuote<unknown>[];\n\n constructor(info: QuoteErrorInfo, quotes?: TransactionPayQuote<unknown>[]) {\n super(info.message);\n this.name = 'QuoteError';\n this.info = info;\n this.quotes = quotes;\n }\n}\n\n/**\n * Validate each quote in turn by checking the live source balance and\n * simulating the transactions it would submit.\n *\n * @param request - Validation request.\n */\nexport async function validateQuotes<QuoteType>(\n request: ValidateQuotesRequest<QuoteType>,\n): Promise<void> {\n const { buildSimulation, messenger, quotes, signal } = request;\n\n for (const quote of quotes) {\n try {\n const simulation = await buildSimulation(quote);\n\n if (!simulation) {\n continue;\n }\n\n await validateQuoteExecution({\n messenger,\n quote,\n signal,\n simulation,\n });\n } catch (error) {\n if (signal?.aborted) {\n throw error;\n }\n\n const quoteError = toQuoteError(error);\n\n if (quoteError.info.reason === 'insufficient-source-balance') {\n // Backwards compatibility: keep the entire quote batch (including\n // quotes that may have passed or not yet been validated) so that\n // clients can still show quote details alongside the balance error.\n throw new QuoteError(quoteError.info, quotes);\n }\n\n throw quoteError;\n }\n }\n}\n\nexport async function validateQuoteExecution({\n messenger,\n quote,\n signal,\n simulation,\n}: QuoteExecutionRequest): Promise<void> {\n throwIfAborted(signal);\n\n const liveBalance = await getLiveSourceBalance(quote, messenger);\n\n log('Live source balance', {\n from: quote.request.from,\n liveBalance,\n sourceChainId: quote.request.sourceChainId,\n sourceTokenAddress: quote.request.sourceTokenAddress,\n });\n\n throwIfAborted(signal);\n\n log('Checking quote source amount', {\n hasPaymentOverride: Boolean(quote.request.paymentOverride),\n isPostQuote: Boolean(quote.request.isPostQuote),\n liveBalance,\n requiredAmount: quote.sourceAmount.raw,\n });\n\n validateRequiredSourceAmount(messenger, quote, liveBalance);\n\n log('Checking decoded source transfers', {\n sourceChainId: quote.request.sourceChainId,\n sourceTokenAddress: quote.request.sourceTokenAddress,\n transactions: simulation.transactions,\n });\n\n validateDecodedSourceTransfers(\n messenger,\n quote,\n liveBalance,\n simulation.transactions,\n );\n\n throwIfAborted(signal);\n\n await validateSimulation(messenger, quote, simulation, signal);\n}\n\nasync function validateSimulation(\n messenger: TransactionPayControllerMessenger,\n quote: TransactionPayQuote<unknown>,\n simulation: QuoteSimulation,\n signal?: AbortSignal,\n): Promise<void> {\n log('Starting simulation', {\n chainId: quote.request.sourceChainId,\n transactions: simulation.transactions,\n });\n\n try {\n await simulateQuoteTransactions({\n chainId: quote.request.sourceChainId,\n messenger,\n transactions: simulation.transactions,\n });\n\n log('Simulation passed');\n } catch (error) {\n throwIfAborted(signal);\n\n if (error instanceof TransactionPaySimulationError) {\n throw new QuoteError({\n message: 'Quote simulation failed',\n reason: 'simulation-failed',\n detail: [error.message],\n });\n }\n\n throw error;\n }\n}\n\nexport function isQuoteError(error: unknown): error is QuoteError {\n return error instanceof QuoteError;\n}\n\n/**\n * Format an amount shortfall into display-ready detail rows.\n *\n * Amounts are formatted using the source token decimals and symbol when\n * available (e.g. `Required: 1.5 USDC`), falling back to the raw atomic values\n * otherwise.\n *\n * @param messenger - Controller messenger.\n * @param quote - Quote being validated.\n * @param required - Required amount (raw string).\n * @param balance - Current balance (raw string).\n * @returns Detail rows: Required / Current / Missing.\n */\nfunction formatBalanceShortfall(\n messenger: TransactionPayControllerMessenger,\n quote: TransactionPayQuote<unknown>,\n required: string,\n balance: string,\n): string[] {\n const { sourceChainId, sourceTokenAddress } = quote.request;\n const tokenInfo = getTokenInfo(messenger, sourceTokenAddress, sourceChainId);\n\n const requiredBn = new BigNumber(required);\n const balanceBn = new BigNumber(balance);\n\n // Only ever called when balance < required, so the shortfall is positive.\n const missing = requiredBn.minus(balanceBn);\n\n return [\n `Required: ${formatTokenAmount(requiredBn, tokenInfo)}`,\n `Current: ${formatTokenAmount(balanceBn, tokenInfo)}`,\n `Missing: ${formatTokenAmount(missing, tokenInfo)}`,\n ];\n}\n\n/**\n * Format an atomic token amount as a human-readable string with symbol.\n *\n * @param rawAmount - Amount in atomic units.\n * @param tokenInfo - Source token decimals and symbol, if available.\n * @returns Human-readable amount suffixed with the token symbol when known,\n * otherwise the raw atomic value.\n */\nfunction formatTokenAmount(\n rawAmount: BigNumber,\n tokenInfo: { decimals: number; symbol: string } | undefined,\n): string {\n if (!tokenInfo) {\n return rawAmount.toFixed();\n }\n\n const human = rawAmount.shiftedBy(-tokenInfo.decimals).toFixed();\n\n return `${human} ${tokenInfo.symbol}`;\n}\n\n/**\n * Read the live source token balance, or `undefined` if it cannot be read.\n *\n * @param quote - Quote being validated.\n * @param messenger - Controller messenger.\n * @returns Raw balance as a decimal string, or `undefined` when the RPC\n * request failed.\n */\nasync function getLiveSourceBalance(\n quote: TransactionPayQuote<unknown>,\n messenger: TransactionPayControllerMessenger,\n): Promise<string | undefined> {\n const { from, sourceChainId, sourceTokenAddress } = quote.request;\n const normalizedSourceTokenAddress = normalizeTokenAddress(\n sourceTokenAddress,\n sourceChainId,\n TokenAddressTarget.MetaMask,\n );\n\n return await getLiveTokenBalanceOrUndefined(\n messenger,\n from,\n sourceChainId,\n normalizedSourceTokenAddress,\n );\n}\n\nfunction validateRequiredSourceAmount(\n messenger: TransactionPayControllerMessenger,\n quote: TransactionPayQuote<unknown>,\n liveBalance: string | undefined,\n): void {\n if (liveBalance === undefined) {\n log('Skipping quote source amount check: live balance unavailable');\n return;\n }\n\n if (quote.request.isPostQuote || quote.request.paymentOverride) {\n log('Skipping quote source amount check', {\n hasPaymentOverride: Boolean(quote.request.paymentOverride),\n isPostQuote: Boolean(quote.request.isPostQuote),\n });\n return;\n }\n\n const requiredAmount = new BigNumber(quote.sourceAmount.raw);\n const balance = new BigNumber(liveBalance);\n\n if (balance.isGreaterThanOrEqualTo(requiredAmount)) {\n log('Quote source amount check passed');\n return;\n }\n\n throw new QuoteError({\n message: 'Insufficient source balance for quote',\n reason: 'insufficient-source-balance',\n detail: formatBalanceShortfall(\n messenger,\n quote,\n requiredAmount.toFixed(),\n balance.toFixed(),\n ),\n });\n}\n\nfunction validateDecodedSourceTransfers(\n messenger: TransactionPayControllerMessenger,\n quote: TransactionPayQuote<unknown>,\n liveBalance: string | undefined,\n transactions: SimulationTransaction[],\n): void {\n if (liveBalance === undefined) {\n log('Skipping decoded source transfer check: live balance unavailable');\n return;\n }\n\n // Only valid for a single source-token transfer. Multi-step batches produce\n // or transform the source-token balance mid-batch, so comparing against the\n // starting balance is wrong; rely on the full simulation instead.\n if (\n transactions.length !== 1 ||\n !isSourceTokenTransfer(quote, transactions[0])\n ) {\n log(\n 'Skipping decoded source transfer check: not a single source-token transfer',\n );\n return;\n }\n\n // `isSourceTokenTransfer` has already confirmed the data decodes to a\n // transfer, so the amount is defined here.\n const requiredAmount = decodeTransferAmount(\n transactions[0].data as Hex,\n ) as string;\n\n const balance = new BigNumber(liveBalance);\n\n log('Decoded source transfer amount', {\n liveBalance,\n requiredAmount,\n });\n\n if (balance.isGreaterThanOrEqualTo(requiredAmount)) {\n log('Decoded source transfers check passed');\n return;\n }\n\n throw new QuoteError({\n message: 'Insufficient source balance for decoded transfer',\n reason: 'insufficient-transfer-balance',\n detail: formatBalanceShortfall(\n messenger,\n quote,\n requiredAmount,\n liveBalance,\n ),\n });\n}\n\nfunction isSourceTokenTransfer(\n quote: TransactionPayQuote<unknown>,\n transaction: SimulationTransaction | undefined,\n): boolean {\n if (!transaction?.to || !transaction.data) {\n return false;\n }\n\n const { sourceChainId, sourceTokenAddress } = quote.request;\n\n const isNativeSource =\n sourceTokenAddress.toLowerCase() ===\n getNativeToken(sourceChainId).toLowerCase();\n\n if (isNativeSource) {\n return false;\n }\n\n const normalizedSourceTokenAddress = normalizeTokenAddress(\n sourceTokenAddress,\n sourceChainId,\n TokenAddressTarget.MetaMask,\n ).toLowerCase();\n\n const normalizedTo = normalizeTokenAddress(\n transaction.to,\n sourceChainId,\n TokenAddressTarget.MetaMask,\n ).toLowerCase();\n\n if (normalizedTo !== normalizedSourceTokenAddress) {\n return false;\n }\n\n return decodeTransferAmount(transaction.data) !== undefined;\n}\n\nfunction decodeTransferAmount(data: Hex): string | undefined {\n try {\n const result = erc20Interface.decodeFunctionData('transfer', data);\n return new BigNumber(result._value.toString()).toString(10);\n } catch {\n return undefined;\n }\n}\n\nfunction toQuoteError(error: unknown): QuoteError {\n if (isQuoteError(error)) {\n return error;\n }\n\n return new QuoteError({\n message: 'Quote simulation failed',\n reason: 'simulation-failed',\n detail: [(error as Error).message],\n });\n}\n\nfunction throwIfAborted(signal?: AbortSignal): void {\n if (signal?.aborted) {\n throw new Error('Quote validation aborted');\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@metamask-previews/transaction-pay-controller",
3
- "version": "31.2.1-preview-906029d47",
3
+ "version": "31.2.1-preview-e80304db4",
4
4
  "description": "Manages alternate payment strategies to provide required funds for transactions in MetaMask",
5
5
  "keywords": [
6
6
  "Ethereum",
@@ -56,13 +56,12 @@
56
56
  "@ethersproject/abi": "^5.7.0",
57
57
  "@ethersproject/contracts": "^5.7.0",
58
58
  "@ethersproject/providers": "^5.8.0",
59
- "@metamask/account-tree-controller": "^11.2.0",
60
59
  "@metamask/accounts-controller": "^40.2.0",
61
60
  "@metamask/assets-controller": "^20.4.1",
62
61
  "@metamask/base-controller": "^10.1.0",
63
62
  "@metamask/controller-utils": "^13.0.0",
64
63
  "@metamask/gas-fee-controller": "^27.1.1",
65
- "@metamask/keyring-controller": "^28.2.0",
64
+ "@metamask/keyring-controller": "^28.2.1",
66
65
  "@metamask/messenger": "^3.0.0",
67
66
  "@metamask/metamask-eth-abis": "^3.1.1",
68
67
  "@metamask/network-controller": "^38.0.1",
@@ -70,7 +69,7 @@
70
69
  "@metamask/remote-feature-flag-controller": "^7.0.0",
71
70
  "@metamask/sentinel-api-service": "^2.1.0",
72
71
  "@metamask/transaction-controller": "^74.0.0",
73
- "@metamask/utils": "^12.0.1",
72
+ "@metamask/utils": "^12.0.2",
74
73
  "bignumber.js": "^9.1.2",
75
74
  "bn.js": "^5.2.5",
76
75
  "immer": "^11.1.18",