@metamask-previews/transaction-pay-controller 31.2.1-preview-1ae799a5c → 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 +1 -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 +1 -2
@@ -1,128 +1,4 @@
1
- import { KnownCaipNamespace, parseCaipAssetType, parseCaipChainId, toCaipAccountId, } from '@metamask/utils';
2
1
  import { KEYRING_TYPES_REQUIRING_MFA, KEYRING_TYPES_SUPPORTING_7702, PaymentOverride, } from '../constants.js';
3
- /**
4
- * Get the EVM account address used by a transaction.
5
- *
6
- * The account override is an `InternalAccount` ID. It is only used as the
7
- * EVM account when it is an EVM account, since a non-EVM override selects the
8
- * account holding a non-EVM payment token.
9
- *
10
- * @param messenger - Controller messenger.
11
- * @param accountOverride - ID of the account override of the transaction, if any.
12
- * @param from - Address of the transaction sender.
13
- * @returns The EVM account address.
14
- */
15
- export function getEvmAccountAddress(messenger, accountOverride, from) {
16
- const account = accountOverride
17
- ? getAccount(messenger, accountOverride)
18
- : undefined;
19
- return account && isEvmAccount(account) ? account.address : from;
20
- }
21
- /**
22
- * Get the EVM account override of a transaction, for flows that only support
23
- * EVM accounts.
24
- *
25
- * @param messenger - Controller messenger.
26
- * @param accountOverride - ID of the account override of the transaction, if any.
27
- * @returns The address of the EVM account override, or undefined if there is
28
- * no account override.
29
- */
30
- export function getEvmAccountOverride(messenger, accountOverride) {
31
- if (!accountOverride) {
32
- return undefined;
33
- }
34
- const account = getAccount(messenger, accountOverride);
35
- if (!isEvmAccount(account)) {
36
- throw new Error('Account override is not an EVM account');
37
- }
38
- return account.address;
39
- }
40
- /**
41
- * Get the `InternalAccount` that holds an asset.
42
- *
43
- * The account override is used when it supports the asset chain. Otherwise
44
- * the account is the one in the same account group as the account override,
45
- * or as the transaction sender, that supports the asset chain.
46
- *
47
- * @param messenger - Controller messenger.
48
- * @param request - Request parameters.
49
- * @param request.accountOverride - ID of the account override of the transaction, if any.
50
- * @param request.assetId - CAIP-19 asset type of the asset.
51
- * @param request.from - Address of the transaction sender.
52
- * @returns The `InternalAccount` holding the asset.
53
- */
54
- export function getAssetAccount(messenger, { accountOverride, assetId, from, }) {
55
- const { chainId } = parseCaipAssetType(assetId);
56
- const accounts = Object.values(messenger.call('AccountsController:getState').internalAccounts.accounts);
57
- const isOnChain = (account) => account.scopes.includes(chainId);
58
- const overrideAccount = accountOverride
59
- ? getAccount(messenger, accountOverride)
60
- : undefined;
61
- const groupAccount = overrideAccount ??
62
- accounts.find((account) => account.address.toLowerCase() === from.toLowerCase());
63
- const account = overrideAccount && isOnChain(overrideAccount)
64
- ? overrideAccount
65
- : getAccountGroupAccounts(messenger, accounts, groupAccount).find(isOnChain);
66
- if (!account) {
67
- throw new Error('Asset account not found');
68
- }
69
- return account;
70
- }
71
- /**
72
- * Get the CAIP-10 account ID of an address on the chain of a CAIP-19 asset.
73
- *
74
- * @param assetId - CAIP-19 asset type.
75
- * @param address - Address of the account holding the asset.
76
- * @returns The CAIP-10 account ID.
77
- */
78
- export function getCaipAccountId(assetId, address) {
79
- const { chain } = parseCaipAssetType(assetId);
80
- return toCaipAccountId(chain.namespace, chain.reference, address);
81
- }
82
- /**
83
- * Get an `InternalAccount` by ID.
84
- *
85
- * @param messenger - Controller messenger.
86
- * @param accountId - ID of the `InternalAccount`.
87
- * @returns The `InternalAccount`.
88
- */
89
- function getAccount(messenger, accountId) {
90
- const account = messenger.call('AccountsController:getState').internalAccounts
91
- .accounts[accountId];
92
- if (!account) {
93
- throw new Error('Account override not found');
94
- }
95
- return account;
96
- }
97
- /**
98
- * Check if an `InternalAccount` is an EVM account.
99
- *
100
- * @param account - The `InternalAccount`.
101
- * @returns True if the account has an `eip155` scope.
102
- */
103
- function isEvmAccount(account) {
104
- return account.scopes.some((scope) => parseCaipChainId(scope).namespace === KnownCaipNamespace.Eip155);
105
- }
106
- /**
107
- * Get the accounts in the same account group as an account.
108
- *
109
- * @param messenger - Controller messenger.
110
- * @param accounts - All internal accounts.
111
- * @param account - Account whose group to return, if any.
112
- * @returns The accounts in the group, or an empty array if the group is not
113
- * found.
114
- */
115
- function getAccountGroupAccounts(messenger, accounts, account) {
116
- const groupId = account &&
117
- messenger.call('AccountTreeController:getAccountContext', account.id)
118
- ?.groupId;
119
- const group = groupId &&
120
- messenger.call('AccountTreeController:getAccountGroupObject', groupId);
121
- if (!group) {
122
- return [];
123
- }
124
- return accounts.filter((candidate) => group.accounts.includes(candidate.id));
125
- }
126
2
  /**
127
3
  * Check whether a given account supports EIP-7702 authorization signing.
128
4
  *
@@ -1 +1 @@
1
- {"version":3,"file":"account.js","sourceRoot":"","sources":["../../src/utils/account.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AccountsControllerState } from '@metamask/accounts-controller';\nimport {\n KnownCaipNamespace,\n parseCaipAssetType,\n parseCaipChainId,\n toCaipAccountId,\n} from '@metamask/utils';\nimport type { CaipAccountId, CaipAssetType, Hex } from '@metamask/utils';\n\nimport {\n KEYRING_TYPES_REQUIRING_MFA,\n KEYRING_TYPES_SUPPORTING_7702,\n PaymentOverride,\n} from '../constants.js';\nimport type { TransactionPayControllerMessenger } from '../types.js';\n\n/** `InternalAccount` that holds an asset. */\nexport type AssetAccount =\n AccountsControllerState['internalAccounts']['accounts'][string];\n\n/**\n * Get the EVM account address used by a transaction.\n *\n * The account override is an `InternalAccount` ID. It is only used as the\n * EVM account when it is an EVM account, since a non-EVM override selects the\n * account holding a non-EVM payment token.\n *\n * @param messenger - Controller messenger.\n * @param accountOverride - ID of the account override of the transaction, if any.\n * @param from - Address of the transaction sender.\n * @returns The EVM account address.\n */\nexport function getEvmAccountAddress(\n messenger: TransactionPayControllerMessenger,\n accountOverride: string | undefined,\n from: Hex,\n): Hex {\n const account = accountOverride\n ? getAccount(messenger, accountOverride)\n : undefined;\n\n return account && isEvmAccount(account) ? (account.address as Hex) : from;\n}\n\n/**\n * Get the EVM account override of a transaction, for flows that only support\n * EVM accounts.\n *\n * @param messenger - Controller messenger.\n * @param accountOverride - ID of the account override of the transaction, if any.\n * @returns The address of the EVM account override, or undefined if there is\n * no account override.\n */\nexport function getEvmAccountOverride(\n messenger: TransactionPayControllerMessenger,\n accountOverride: string | undefined,\n): Hex | undefined {\n if (!accountOverride) {\n return undefined;\n }\n\n const account = getAccount(messenger, accountOverride);\n\n if (!isEvmAccount(account)) {\n throw new Error('Account override is not an EVM account');\n }\n\n return account.address as Hex;\n}\n\n/**\n * Get the `InternalAccount` that holds an asset.\n *\n * The account override is used when it supports the asset chain. Otherwise\n * the account is the one in the same account group as the account override,\n * or as the transaction sender, that supports the asset chain.\n *\n * @param messenger - Controller messenger.\n * @param request - Request parameters.\n * @param request.accountOverride - ID of the account override of the transaction, if any.\n * @param request.assetId - CAIP-19 asset type of the asset.\n * @param request.from - Address of the transaction sender.\n * @returns The `InternalAccount` holding the asset.\n */\nexport function getAssetAccount(\n messenger: TransactionPayControllerMessenger,\n {\n accountOverride,\n assetId,\n from,\n }: {\n accountOverride: string | undefined;\n assetId: CaipAssetType;\n from: Hex;\n },\n): AssetAccount {\n const { chainId } = parseCaipAssetType(assetId);\n\n const accounts: AssetAccount[] = Object.values(\n messenger.call('AccountsController:getState').internalAccounts.accounts,\n );\n\n const isOnChain = (account: AssetAccount): boolean =>\n account.scopes.includes(chainId);\n\n const overrideAccount = accountOverride\n ? getAccount(messenger, accountOverride)\n : undefined;\n\n const groupAccount =\n overrideAccount ??\n accounts.find(\n (account) => account.address.toLowerCase() === from.toLowerCase(),\n );\n\n const account =\n overrideAccount && isOnChain(overrideAccount)\n ? overrideAccount\n : getAccountGroupAccounts(messenger, accounts, groupAccount).find(\n isOnChain,\n );\n\n if (!account) {\n throw new Error('Asset account not found');\n }\n\n return account;\n}\n\n/**\n * Get the CAIP-10 account ID of an address on the chain of a CAIP-19 asset.\n *\n * @param assetId - CAIP-19 asset type.\n * @param address - Address of the account holding the asset.\n * @returns The CAIP-10 account ID.\n */\nexport function getCaipAccountId(\n assetId: CaipAssetType,\n address: string,\n): CaipAccountId {\n const { chain } = parseCaipAssetType(assetId);\n\n return toCaipAccountId(chain.namespace, chain.reference, address);\n}\n\n/**\n * Get an `InternalAccount` by ID.\n *\n * @param messenger - Controller messenger.\n * @param accountId - ID of the `InternalAccount`.\n * @returns The `InternalAccount`.\n */\nfunction getAccount(\n messenger: TransactionPayControllerMessenger,\n accountId: string,\n): AssetAccount {\n const account = messenger.call('AccountsController:getState').internalAccounts\n .accounts[accountId];\n\n if (!account) {\n throw new Error('Account override not found');\n }\n\n return account;\n}\n\n/**\n * Check if an `InternalAccount` is an EVM account.\n *\n * @param account - The `InternalAccount`.\n * @returns True if the account has an `eip155` scope.\n */\nfunction isEvmAccount(account: AssetAccount): boolean {\n return account.scopes.some(\n (scope) => parseCaipChainId(scope).namespace === KnownCaipNamespace.Eip155,\n );\n}\n\n/**\n * Get the accounts in the same account group as an account.\n *\n * @param messenger - Controller messenger.\n * @param accounts - All internal accounts.\n * @param account - Account whose group to return, if any.\n * @returns The accounts in the group, or an empty array if the group is not\n * found.\n */\nfunction getAccountGroupAccounts(\n messenger: TransactionPayControllerMessenger,\n accounts: AssetAccount[],\n account: AssetAccount | undefined,\n): AssetAccount[] {\n const groupId =\n account &&\n messenger.call('AccountTreeController:getAccountContext', account.id)\n ?.groupId;\n\n const group =\n groupId &&\n messenger.call('AccountTreeController:getAccountGroupObject', groupId);\n\n if (!group) {\n return [];\n }\n\n return accounts.filter((candidate) => group.accounts.includes(candidate.id));\n}\n\n/**\n * Check whether a given account supports EIP-7702 authorization signing.\n *\n * Looks up the account's keyring via `KeyringController:getState` and returns\n * `true` only when the keyring type is in the supported list (HD Key Tree,\n * Simple Key Pair). Hardware wallets, snap keyrings, and other types return\n * `false`. Returns `false` when the keyring cannot be resolved.\n *\n * @param messenger - Controller messenger used to call KeyringController.\n * @param account - The account address to check.\n * @returns Whether the account supports EIP-7702.\n */\nexport function accountSupports7702(\n messenger: TransactionPayControllerMessenger,\n account: string,\n): boolean {\n return hasKeyringOfType(messenger, account, KEYRING_TYPES_SUPPORTING_7702);\n}\n\n/**\n * Check whether a given account's keyring requires multi-factor\n * authentication to sign.\n *\n * Looks up the account's keyring via `KeyringController:getState` and returns\n * `true` only when the keyring type is in {@link KEYRING_TYPES_REQUIRING_MFA}.\n * Returns `false` when the keyring cannot be resolved, so an unknown account\n * keeps the default behaviour rather than silently losing an optimisation.\n *\n * @param messenger - Controller messenger used to call KeyringController.\n * @param account - The account address to check.\n * @returns Whether signing with the account requires MFA.\n */\nexport function accountRequiresMfa(\n messenger: TransactionPayControllerMessenger,\n account: string | undefined,\n): boolean {\n if (!account) {\n return false;\n }\n return hasKeyringOfType(messenger, account, KEYRING_TYPES_REQUIRING_MFA);\n}\n\n/**\n * Whether the quote payer is an MPC Money account.\n *\n * Perps and Predict deposits funded by a Money Account set `paymentOverride`\n * and put that account on `refundTo`. The account signs the delegation inside\n * the client payment-override callback. Quote simulation calls that callback,\n * so an MPC keyring would prompt for MFA on every quote and again on submit.\n *\n * @param messenger - Controller messenger used to resolve the payer keyring.\n * @param request - Quote request carrying the payment override and refund recipient.\n * @returns Whether the payer is an MPC Money account.\n */\nexport function isMpcMoneyAccountPayer(\n messenger: TransactionPayControllerMessenger,\n request: {\n paymentOverride?: PaymentOverride;\n refundTo?: string;\n },\n): boolean {\n return (\n request.paymentOverride === PaymentOverride.MoneyAccount &&\n accountRequiresMfa(messenger, request.refundTo)\n );\n}\n\n/**\n * Check whether an account belongs to a keyring of one of the given types.\n *\n * @param messenger - Controller messenger used to call KeyringController.\n * @param account - The account address to check.\n * @param types - Keyring types to match against.\n * @returns Whether a keyring of one of the given types holds the account.\n */\nfunction hasKeyringOfType(\n messenger: TransactionPayControllerMessenger,\n account: string,\n types: string[],\n): boolean {\n const { keyrings } = messenger.call('KeyringController:getState');\n\n return keyrings.some(\n (keyring) =>\n types.includes(keyring.type) &&\n keyring.accounts.some(\n (keyringAccount) =>\n keyringAccount.toLowerCase() === account.toLowerCase(),\n ),\n );\n}\n"]}
1
+ {"version":3,"file":"account.js","sourceRoot":"","sources":["../../src/utils/account.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,2BAA2B,EAC3B,6BAA6B,EAC7B,eAAe,GAChB,MAAM,iBAAiB,CAAC;AAGzB;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,mBAAmB,CACjC,SAA4C,EAC5C,OAAe;IAEf,OAAO,gBAAgB,CAAC,SAAS,EAAE,OAAO,EAAE,6BAA6B,CAAC,CAAC;AAC7E,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,kBAAkB,CAChC,SAA4C,EAC5C,OAA2B;IAE3B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,gBAAgB,CAAC,SAAS,EAAE,OAAO,EAAE,2BAA2B,CAAC,CAAC;AAC3E,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,sBAAsB,CACpC,SAA4C,EAC5C,OAGC;IAED,OAAO,CACL,OAAO,CAAC,eAAe,KAAK,eAAe,CAAC,YAAY;QACxD,kBAAkB,CAAC,SAAS,EAAE,OAAO,CAAC,QAAQ,CAAC,CAChD,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,gBAAgB,CACvB,SAA4C,EAC5C,OAAe,EACf,KAAe;IAEf,MAAM,EAAE,QAAQ,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;IAElE,OAAO,QAAQ,CAAC,IAAI,CAClB,CAAC,OAAO,EAAE,EAAE,CACV,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC;QAC5B,OAAO,CAAC,QAAQ,CAAC,IAAI,CACnB,CAAC,cAAc,EAAE,EAAE,CACjB,cAAc,CAAC,WAAW,EAAE,KAAK,OAAO,CAAC,WAAW,EAAE,CACzD,CACJ,CAAC;AACJ,CAAC","sourcesContent":["import {\n KEYRING_TYPES_REQUIRING_MFA,\n KEYRING_TYPES_SUPPORTING_7702,\n PaymentOverride,\n} from '../constants.js';\nimport type { TransactionPayControllerMessenger } from '../types.js';\n\n/**\n * Check whether a given account supports EIP-7702 authorization signing.\n *\n * Looks up the account's keyring via `KeyringController:getState` and returns\n * `true` only when the keyring type is in the supported list (HD Key Tree,\n * Simple Key Pair). Hardware wallets, snap keyrings, and other types return\n * `false`. Returns `false` when the keyring cannot be resolved.\n *\n * @param messenger - Controller messenger used to call KeyringController.\n * @param account - The account address to check.\n * @returns Whether the account supports EIP-7702.\n */\nexport function accountSupports7702(\n messenger: TransactionPayControllerMessenger,\n account: string,\n): boolean {\n return hasKeyringOfType(messenger, account, KEYRING_TYPES_SUPPORTING_7702);\n}\n\n/**\n * Check whether a given account's keyring requires multi-factor\n * authentication to sign.\n *\n * Looks up the account's keyring via `KeyringController:getState` and returns\n * `true` only when the keyring type is in {@link KEYRING_TYPES_REQUIRING_MFA}.\n * Returns `false` when the keyring cannot be resolved, so an unknown account\n * keeps the default behaviour rather than silently losing an optimisation.\n *\n * @param messenger - Controller messenger used to call KeyringController.\n * @param account - The account address to check.\n * @returns Whether signing with the account requires MFA.\n */\nexport function accountRequiresMfa(\n messenger: TransactionPayControllerMessenger,\n account: string | undefined,\n): boolean {\n if (!account) {\n return false;\n }\n return hasKeyringOfType(messenger, account, KEYRING_TYPES_REQUIRING_MFA);\n}\n\n/**\n * Whether the quote payer is an MPC Money account.\n *\n * Perps and Predict deposits funded by a Money Account set `paymentOverride`\n * and put that account on `refundTo`. The account signs the delegation inside\n * the client payment-override callback. Quote simulation calls that callback,\n * so an MPC keyring would prompt for MFA on every quote and again on submit.\n *\n * @param messenger - Controller messenger used to resolve the payer keyring.\n * @param request - Quote request carrying the payment override and refund recipient.\n * @returns Whether the payer is an MPC Money account.\n */\nexport function isMpcMoneyAccountPayer(\n messenger: TransactionPayControllerMessenger,\n request: {\n paymentOverride?: PaymentOverride;\n refundTo?: string;\n },\n): boolean {\n return (\n request.paymentOverride === PaymentOverride.MoneyAccount &&\n accountRequiresMfa(messenger, request.refundTo)\n );\n}\n\n/**\n * Check whether an account belongs to a keyring of one of the given types.\n *\n * @param messenger - Controller messenger used to call KeyringController.\n * @param account - The account address to check.\n * @param types - Keyring types to match against.\n * @returns Whether a keyring of one of the given types holds the account.\n */\nfunction hasKeyringOfType(\n messenger: TransactionPayControllerMessenger,\n account: string,\n types: string[],\n): boolean {\n const { keyrings } = messenger.call('KeyringController:getState');\n\n return keyrings.some(\n (keyring) =>\n types.includes(keyring.type) &&\n keyring.accounts.some(\n (keyringAccount) =>\n keyringAccount.toLowerCase() === account.toLowerCase(),\n ),\n );\n}\n"]}
@@ -98,9 +98,7 @@ type GasStationCostRequest = {
98
98
  * to cover it. Omit to consult the gas station regardless of balance.
99
99
  */
100
100
  nativeGasCostRaw?: string;
101
- request: EvmGasPaymentSource & {
102
- sourceTokenAddress: Hex;
103
- };
101
+ request: Pick<QuoteRequest, 'from' | 'sourceChainId' | 'sourceTokenAddress'>;
104
102
  /** Combined gas estimate across every origin-chain call. */
105
103
  totalGasEstimate: number;
106
104
  /** Number of origin-chain calls the estimate covers. */
@@ -114,16 +112,9 @@ export type GasStationStepData = {
114
112
  type SponsorshipRequest = {
115
113
  /** Whether the paying account can sign EIP-7702 authorizations. */
116
114
  accountSupports7702: boolean | undefined;
117
- request: Pick<QuoteRequest, 'sourceAssetId' | 'targetChainId'>;
115
+ request: Pick<QuoteRequest, 'sourceChainId' | 'targetChainId'>;
118
116
  transaction: Pick<TransactionMeta, 'chainId' | 'isGasFeeSponsored'>;
119
117
  };
120
- /** EVM account and chain paying origin gas, derived from a quote request. */
121
- type EvmGasPaymentSource = {
122
- /** Address of the account paying origin gas. */
123
- from: Hex;
124
- /** Chain ID of the source token. */
125
- sourceChainId: Hex;
126
- };
127
118
  /**
128
119
  * Whether origin gas should be paid through the gas station.
129
120
  *
@@ -142,7 +133,7 @@ type EvmGasPaymentSource = {
142
133
  export declare function shouldUseGasStation({ messenger, nativeGasCostRaw, request, }: {
143
134
  messenger: TransactionPayControllerMessenger;
144
135
  nativeGasCostRaw: string | undefined;
145
- request: EvmGasPaymentSource;
136
+ request: Pick<QuoteRequest, 'from' | 'sourceChainId'>;
146
137
  }): boolean;
147
138
  /**
148
139
  * Resolve who pays origin gas.
@@ -1 +1 @@
1
- {"version":3,"file":"gas-payment.d.ts","sourceRoot":"","sources":["../../src/utils/gas-payment.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gas-payment.d.ts","sourceRoot":"","sources":["../../src/utils/gas-payment.ts"],"names":[],"mappings":"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"}
@@ -4,7 +4,7 @@ import { BigNumber } from 'bignumber.js';
4
4
  import { projectLogger } from '../logger.js';
5
5
  import { getFeatureFlags, isEIP7702Chain } from './feature-flags.js';
6
6
  import { calculateGasFeeTokenCost } from './gas.js';
7
- import { getEvmQuoteSource, getNativeToken, getTokenBalance } from './token.js';
7
+ import { getNativeToken, getTokenBalance } from './token.js';
8
8
  const log = createModuleLogger(projectLogger, 'gas-payment');
9
9
  export var GasPaymentMode;
10
10
  (function (GasPaymentMode) {
@@ -364,7 +364,7 @@ function isSponsoredRoute(sponsorship) {
364
364
  return undefined;
365
365
  }
366
366
  return (isGasFeeSponsored &&
367
- getEvmQuoteSource(request).chainId === transaction.chainId &&
367
+ request.sourceChainId === transaction.chainId &&
368
368
  request.targetChainId === transaction.chainId &&
369
369
  Boolean(accountSupports7702));
370
370
  }
@@ -1 +1 @@
1
- {"version":3,"file":"gas-payment.js","sourceRoot":"","sources":["../../src/utils/gas-payment.ts"],"names":[],"mappings":"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","sourcesContent":["import { toHex } from '@metamask/controller-utils';\nimport type {\n GasFeeToken,\n TransactionMeta,\n} from '@metamask/transaction-controller';\nimport type { Hex } from '@metamask/utils';\nimport { createModuleLogger } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport { projectLogger } from '../logger.js';\nimport type {\n Amount,\n QuoteRequest,\n TransactionPayControllerMessenger,\n} from '../types.js';\nimport { getFeatureFlags, isEIP7702Chain } from './feature-flags.js';\nimport { calculateGasFeeTokenCost } from './gas.js';\nimport { getEvmQuoteSource, getNativeToken, getTokenBalance } from './token.js';\n\nconst log = createModuleLogger(projectLogger, 'gas-payment');\n\nexport enum GasPaymentMode {\n /**\n * A relayer redeems a signed delegation and pays origin gas itself, so\n * nothing is submitted through the `TransactionController`.\n */\n Delegation = 'delegation',\n\n /** MetaMask sponsors origin gas through the EIP-7702 gas station hook. */\n Sponsored = 'sponsored',\n\n /**\n * The user pays origin gas in the source token, collected by the EIP-7702\n * gas station as a gas fee token.\n */\n GasFeeToken = 'gas-fee-token',\n\n /** The user pays origin gas in the chain's native token. */\n Native = 'native',\n}\n\n/**\n * Plain-language summary logged for each resolved {@link GasPaymentMode}.\n *\n * Phrased in the future tense because this is the payment MetaMask is about to\n * request, not one that has happened. For the modes that go through the\n * `TransactionController`, the outcome is only known after publish — see\n * {@link logGasPaymentOutcome}.\n *\n * Only emitted at submit time. See {@link resolveGasPayment} for why the\n * quote-time resolution is too provisional to report.\n */\nconst GAS_PAYMENT_MODE_LOG: Record<GasPaymentMode, string> = {\n [GasPaymentMode.Delegation]: 'Gas will be paid by relayer',\n [GasPaymentMode.GasFeeToken]: 'Gas will be paid with source token',\n [GasPaymentMode.Native]: 'Gas will be paid with native token',\n [GasPaymentMode.Sponsored]: 'Gas will be sponsored',\n};\n\n/**\n * Who pays origin gas, plus the `TransactionController` options that implement\n * it. Spread the options straight into `addTransaction` or\n * `addTransactionBatch`.\n */\nexport type GasPayment = {\n /**\n * Stop the `TransactionController` silently falling back to native gas when\n * a gas fee token is set. Always set in {@link GasPaymentMode.GasFeeToken},\n * because reaching that mode means the quote was priced with the gas fee\n * token and the user was shown that fee.\n */\n excludeNativeTokenForFee?: boolean;\n\n /**\n * Token to collect origin gas in. Only set in\n * {@link GasPaymentMode.GasFeeToken}.\n */\n gasFeeToken?: Hex;\n\n /**\n * Whether MetaMask sponsors origin gas. `true` in\n * {@link GasPaymentMode.Sponsored}. Otherwise `false` when sponsorship was\n * evaluated and rejected, or absent when the strategy never sponsors — the\n * `TransactionController` omits the flag entirely rather than recording\n * `false`, so the distinction is carried through.\n */\n isGasFeeSponsored?: boolean;\n\n /** The resolved mode. */\n mode: GasPaymentMode;\n};\n\ntype GasPaymentRequest = {\n /**\n * Whether a relayer will redeem a signed delegation and pay origin gas\n * (Relay `/execute`, server `gasless`, HyperLiquid withdrawals).\n */\n isDelegated?: boolean;\n\n /**\n * Whether the quote priced origin gas in the source token. Leave `undefined`\n * at quote time, before the gas station has been consulted.\n */\n isSourceGasFeeToken?: boolean;\n\n /** Source token charged when paying with a gas fee token. */\n sourceTokenAddress: Hex;\n\n /**\n * Inputs for the MetaMask sponsorship check. Omit for strategies that never\n * sponsor, so `isGasFeeSponsored` is left unset rather than explicitly\n * disabled.\n */\n sponsorship?: SponsorshipRequest;\n};\n\n/** Origin gas priced in source-token units by the gas station. */\ntype GasStationCost = {\n /** The cost, when the gas station will collect gas in the source token. */\n amount?: Amount;\n\n /**\n * Whether the gas station was actually consulted, meaning every eligibility\n * gate passed. `false` means it was skipped, so an absent `amount` says\n * nothing about whether a fee token exists.\n */\n isAvailable: boolean;\n};\n\ntype GasStationCostRequest = {\n /**\n * Whether the paying account can sign EIP-7702 authorizations. Omit when\n * unknown; only an explicit `false` blocks the gas station.\n */\n accountSupports7702?: boolean;\n\n /**\n * Account the gas station simulates the fee token transfer from. Defaults to\n * `request.from`. Predict withdrawals hold the source token in a Safe proxy,\n * so the probe must target the proxy rather than the still-empty EOA.\n */\n feeTokenAccount?: Hex;\n\n /** First origin-chain call, simulated to discover available fee tokens. */\n firstStepData: GasStationStepData;\n\n messenger: TransactionPayControllerMessenger;\n\n /**\n * Native gas cost the user would otherwise pay, in raw units. The gas\n * station is skipped when `request.from` already holds enough native token\n * to cover it. Omit to consult the gas station regardless of balance.\n */\n nativeGasCostRaw?: string;\n\n request: EvmGasPaymentSource & { sourceTokenAddress: Hex };\n\n /** Combined gas estimate across every origin-chain call. */\n totalGasEstimate: number;\n\n /** Number of origin-chain calls the estimate covers. */\n totalItemCount: number;\n};\n\nexport type GasStationStepData = {\n data: Hex;\n to: Hex;\n value?: string;\n};\n\ntype SponsorshipRequest = {\n /** Whether the paying account can sign EIP-7702 authorizations. */\n accountSupports7702: boolean | undefined;\n\n request: Pick<QuoteRequest, 'sourceAssetId' | 'targetChainId'>;\n\n transaction: Pick<TransactionMeta, 'chainId' | 'isGasFeeSponsored'>;\n};\n\n/** EVM account and chain paying origin gas, derived from a quote request. */\ntype EvmGasPaymentSource = {\n /** Address of the account paying origin gas. */\n from: Hex;\n\n /** Chain ID of the source token. */\n sourceChainId: Hex;\n};\n\n/**\n * Whether origin gas should be paid through the gas station.\n *\n * True only when the user cannot cover gas in native token *and* the gas\n * station can actually collect it on this chain. A `false` result therefore\n * means either \"native balance covers it\" or \"the gas station is unavailable\n * here\" — it does not mean the gas station was unnecessary. Cheap enough to\n * call before any network work, so it gates the expensive probe flow.\n *\n * @param request - Gas station check.\n * @param request.messenger - Controller messenger.\n * @param request.nativeGasCostRaw - Native gas cost the user would otherwise pay, in raw units.\n * @param request.request - Quote request fields identifying the payment.\n * @returns Whether the gas station should be used for origin gas.\n */\nexport function shouldUseGasStation({\n messenger,\n nativeGasCostRaw,\n request,\n}: {\n messenger: TransactionPayControllerMessenger;\n nativeGasCostRaw: string | undefined;\n request: EvmGasPaymentSource;\n}): boolean {\n const { isSufficient, nativeBalanceRaw } = checkNativeBalance({\n messenger,\n nativeGasCostRaw,\n request,\n });\n\n if (isSufficient) {\n log('Native balance is sufficient for gas', {\n nativeBalanceRaw,\n nativeGasCostRaw,\n });\n return false;\n }\n\n if (!canUseGasStation(messenger, request.sourceChainId)) {\n return false;\n }\n\n log('Requesting gas fee token as insufficient native token', {\n nativeBalanceRaw,\n nativeGasCostRaw,\n sourceChainId: request.sourceChainId,\n });\n\n return true;\n}\n\n/**\n * Whether the gas station can collect origin gas on a chain at all.\n *\n * Cheap enough to call before doing any work — it only reads feature flags.\n * Use it to skip an expensive code path that would otherwise end in\n * {@link resolveGasStationCost} returning nothing.\n *\n * @param messenger - Controller messenger.\n * @param sourceChainId - Origin chain of the payment.\n * @returns Whether the chain supports EIP-7702 and is not disabled.\n */\nfunction canUseGasStation(\n messenger: TransactionPayControllerMessenger,\n sourceChainId: Hex,\n): boolean {\n const { relayDisabledGasStationChains } = getFeatureFlags(messenger);\n\n if (relayDisabledGasStationChains.includes(sourceChainId)) {\n log('Gas station is disabled for chain', { sourceChainId });\n return false;\n }\n\n if (!isEIP7702Chain(messenger, sourceChainId)) {\n log('Gas station is unsupported on chain', { sourceChainId });\n return false;\n }\n\n return true;\n}\n\n/**\n * Resolve who pays origin gas.\n *\n * Applies the {@link GasPaymentMode} priority order, so a strategy only\n * declares which options are available rather than encoding the precedence\n * itself.\n *\n * Both stages use this. At quote time, call it without `isSourceGasFeeToken`\n * to find out whether anyone else covers gas before spending effort\n * estimating it. At submit time, call it with everything to get the\n * `TransactionController` options.\n *\n * Only the submit-time resolution is logged. Quote-time callers omit\n * `isSourceGasFeeToken`, while submit-time callers include it even when the\n * value is `undefined`. At quote time the gas station has not been consulted\n * yet, so logging would announce the user is paying gas in native token before\n * anything has ruled out the source token.\n *\n * @param request - Payment request.\n * @returns The resolved mode and its `TransactionController` options.\n */\nexport function resolveGasPayment(request: GasPaymentRequest): GasPayment {\n const gasPayment = getGasPayment(request);\n\n if ('isSourceGasFeeToken' in request) {\n log(GAS_PAYMENT_MODE_LOG[gasPayment.mode], gasPayment);\n }\n\n return gasPayment;\n}\n\n/**\n * Log whether the `TransactionController` actually charged gas the way\n * {@link resolveGasPayment} asked it to.\n *\n * `resolveGasPayment` only logs the request. The `TransactionController` makes\n * its own decision at publish time and clears `selectedGasFeeToken` when it\n * declines, so without this the logs would claim the source token paid for gas\n * even when the user was billed in native token.\n *\n * Only meaningful for {@link GasPaymentMode.GasFeeToken}; every other mode\n * either bypasses the `TransactionController` or has nothing to contradict.\n *\n * @param gasPayment - The gas payment that was requested.\n * @param transactions - Final metadata of the submitted transactions.\n */\nexport function logGasPaymentOutcome(\n gasPayment: GasPayment,\n transactions: (\n | Pick<TransactionMeta, 'id' | 'selectedGasFeeToken'>\n | undefined\n )[],\n): void {\n if (gasPayment.mode !== GasPaymentMode.GasFeeToken) {\n return;\n }\n\n const ignored = transactions.filter(\n (transaction) => transaction && !transaction.selectedGasFeeToken,\n );\n\n if (!ignored.length) {\n log('Gas was paid with source token', {\n gasFeeToken: gasPayment.gasFeeToken,\n });\n\n return;\n }\n\n log('Gas fee token was dropped, gas paid with native token instead', {\n gasFeeToken: gasPayment.gasFeeToken,\n transactionIds: ignored.map((transaction) => transaction?.id),\n });\n}\n\nfunction getGasPayment({\n isDelegated,\n isSourceGasFeeToken,\n sourceTokenAddress,\n sponsorship,\n}: GasPaymentRequest): GasPayment {\n if (isDelegated) {\n return { mode: GasPaymentMode.Delegation };\n }\n\n const isSponsored = isSponsoredRoute(sponsorship);\n\n if (isSponsored) {\n return { isGasFeeSponsored: true, mode: GasPaymentMode.Sponsored };\n }\n\n // An absent flag and an explicit `false` are not equivalent to the\n // `TransactionController`, so only set it when sponsorship was evaluated.\n const unsponsored =\n isSponsored === undefined ? {} : { isGasFeeSponsored: isSponsored };\n\n if (isSourceGasFeeToken) {\n // Reaching this mode means the quote priced origin gas in the source token\n // and the user was shown that fee, so the `TransactionController` must not\n // quietly bill them in native token instead. Without this, it drops the\n // gas fee token whenever its own balance check says native covers the gas,\n // and that check uses a live balance and a per-transaction cost while the\n // quote used cached balances and a buffered cost across every origin call.\n return {\n ...unsponsored,\n excludeNativeTokenForFee: true,\n gasFeeToken: sourceTokenAddress,\n mode: GasPaymentMode.GasFeeToken,\n };\n }\n\n return { ...unsponsored, mode: GasPaymentMode.Native };\n}\n\n/**\n * Price origin gas in source-token units, applying every gas station\n * eligibility gate first.\n *\n * The gas station is skipped when the account cannot sign EIP-7702\n * authorizations, when the user already holds enough native token, or when the\n * chain is unsupported or disabled. Callers distinguish \"gas station skipped\"\n * from \"gas station found no fee token\" via {@link GasStationCost.isAvailable}.\n *\n * @param request - Cost request.\n * @param request.accountSupports7702 - Whether the account can sign EIP-7702 authorizations.\n * @param request.feeTokenAccount - Account the fee token transfer is simulated from.\n * @param request.firstStepData - First origin-chain call to simulate.\n * @param request.messenger - Controller messenger.\n * @param request.nativeGasCostRaw - Native gas cost the user would otherwise pay, in raw units.\n * @param request.request - Quote request fields identifying the payment.\n * @param request.totalGasEstimate - Combined gas estimate across every origin-chain call.\n * @param request.totalItemCount - Number of origin-chain calls the estimate covers.\n * @returns The resolved gas station cost.\n */\nexport async function resolveGasStationCost({\n accountSupports7702,\n feeTokenAccount,\n firstStepData,\n messenger,\n nativeGasCostRaw,\n request,\n totalGasEstimate,\n totalItemCount,\n}: GasStationCostRequest): Promise<GasStationCost> {\n const { from, sourceChainId, sourceTokenAddress } = request;\n\n if (accountSupports7702 === false) {\n log('Skipping gas station as account does not support EIP-7702', { from });\n return { isAvailable: false };\n }\n\n const { isSufficient, nativeBalanceRaw } = checkNativeBalance({\n messenger,\n nativeGasCostRaw,\n request,\n });\n\n if (isSufficient) {\n log('Skipping gas station as native balance covers gas', {\n nativeBalanceRaw,\n nativeGasCostRaw,\n sourceChainId,\n });\n return { isAvailable: false };\n }\n\n if (!canUseGasStation(messenger, sourceChainId)) {\n return { isAvailable: false };\n }\n\n log('Requesting gas fee token as insufficient native token', {\n nativeBalanceRaw,\n nativeGasCostRaw,\n sourceChainId,\n sourceTokenAddress,\n });\n\n const amount = await getGasStationCostInSourceToken({\n feeTokenAccount: feeTokenAccount ?? from,\n firstStepData,\n messenger,\n sourceChainId,\n sourceTokenAddress,\n totalGasEstimate,\n totalItemCount,\n });\n\n return { amount, isAvailable: true };\n}\n\nasync function getGasStationCostInSourceToken({\n feeTokenAccount,\n firstStepData,\n messenger,\n sourceChainId,\n sourceTokenAddress,\n totalGasEstimate,\n totalItemCount,\n}: {\n feeTokenAccount: Hex;\n firstStepData: GasStationStepData;\n messenger: TransactionPayControllerMessenger;\n sourceChainId: Hex;\n sourceTokenAddress: Hex;\n totalGasEstimate: number;\n totalItemCount: number;\n}): Promise<Amount | undefined> {\n const { data, to, value } = firstStepData;\n\n let gasFeeTokens: GasFeeToken[] | undefined;\n\n try {\n gasFeeTokens = await messenger.call(\n 'TransactionController:getGasFeeTokens',\n {\n chainId: sourceChainId,\n data,\n from: feeTokenAccount,\n to,\n value: toHex(value ?? '0'),\n },\n );\n } catch (error) {\n log('Failed to estimate gas fee tokens', {\n error,\n sourceChainId,\n });\n return undefined;\n }\n\n const gasFeeToken = gasFeeTokens?.find(\n (singleGasFeeToken) =>\n singleGasFeeToken.tokenAddress.toLowerCase() ===\n sourceTokenAddress.toLowerCase(),\n );\n\n if (!gasFeeToken) {\n log('No matching source token in gas fee token estimate', {\n sourceTokenAddress,\n sourceChainId,\n });\n return undefined;\n }\n\n log('Found gas fee token', {\n amount: gasFeeToken.amount,\n gas: gasFeeToken.gas,\n sourceChainId,\n sourceTokenAddress,\n });\n\n const gasFeeTokenWithNormalizedAmount = {\n ...gasFeeToken,\n amount: toHex(\n getNormalizedGasFeeTokenAmount({\n gasFeeToken,\n totalGasEstimate,\n totalItemCount,\n }),\n ),\n };\n\n const gasFeeTokenCost = calculateGasFeeTokenCost({\n chainId: sourceChainId,\n gasFeeToken: gasFeeTokenWithNormalizedAmount,\n messenger,\n });\n\n if (!gasFeeTokenCost) {\n log('Unable to calculate gas fee token cost using fiat rates', {\n sourceTokenAddress,\n sourceChainId,\n });\n return undefined;\n }\n\n log('Estimated gas station cost for source token', {\n amount: gasFeeTokenCost.raw,\n sourceTokenAddress,\n sourceChainId,\n });\n\n return gasFeeTokenCost;\n}\n\nfunction getNormalizedGasFeeTokenAmount({\n gasFeeToken,\n totalGasEstimate,\n totalItemCount,\n}: {\n gasFeeToken: GasFeeToken;\n totalGasEstimate: number;\n totalItemCount: number;\n}): string {\n let amount = new BigNumber(gasFeeToken.amount);\n\n if (totalItemCount > 1) {\n const gas = new BigNumber(gasFeeToken.gas);\n const gasFeeAmount = new BigNumber(gasFeeToken.amount);\n\n if (totalGasEstimate > 0 && gas.isGreaterThan(0)) {\n const gasRate = gasFeeAmount.dividedBy(gas);\n amount = gasRate.multipliedBy(totalGasEstimate);\n }\n }\n\n return amount.integerValue(BigNumber.ROUND_CEIL).toFixed(0);\n}\n\n/**\n * Read the account's native balance and compare it to a gas cost.\n *\n * The balance is returned alongside the verdict so callers can log what the\n * decision was actually based on. An undefined cost is never sufficient, since\n * an unknown gas cost cannot be shown to be covered.\n *\n * @param request - Balance check.\n * @param request.messenger - Controller messenger.\n * @param request.nativeGasCostRaw - Native gas cost to cover, in raw units.\n * @param request.request - Quote request fields identifying the payment.\n * @returns The raw native balance and whether it covers the cost.\n */\nfunction checkNativeBalance({\n messenger,\n nativeGasCostRaw,\n request,\n}: {\n messenger: TransactionPayControllerMessenger;\n nativeGasCostRaw: string | undefined;\n request: EvmGasPaymentSource;\n}): { isSufficient: boolean; nativeBalanceRaw: string } {\n const { from, sourceChainId } = request;\n\n const nativeBalanceRaw = getTokenBalance(\n messenger,\n from,\n sourceChainId,\n getNativeToken(sourceChainId),\n );\n\n return {\n isSufficient:\n nativeGasCostRaw !== undefined &&\n new BigNumber(nativeBalanceRaw).isGreaterThanOrEqualTo(nativeGasCostRaw),\n nativeBalanceRaw,\n };\n}\n\n/**\n * Whether MetaMask sponsors origin gas for a route.\n *\n * Sponsorship runs through the EIP-7702 gas station hook, so it requires an\n * account that can sign authorizations and a payment that never leaves the\n * transaction's own chain.\n *\n * Returns `undefined` when there is nothing to evaluate, which callers keep\n * distinct from an explicit `false`.\n *\n * @param sponsorship - Sponsorship inputs, or `undefined` when the strategy never sponsors.\n * @returns Whether the route is sponsored.\n */\nfunction isSponsoredRoute(\n sponsorship: SponsorshipRequest | undefined,\n): boolean | undefined {\n if (!sponsorship) {\n return undefined;\n }\n\n const { accountSupports7702, request, transaction } = sponsorship;\n const { isGasFeeSponsored } = transaction;\n\n if (isGasFeeSponsored === undefined) {\n return undefined;\n }\n\n return (\n isGasFeeSponsored &&\n getEvmQuoteSource(request).chainId === transaction.chainId &&\n request.targetChainId === transaction.chainId &&\n Boolean(accountSupports7702)\n );\n}\n"]}
1
+ {"version":3,"file":"gas-payment.js","sourceRoot":"","sources":["../../src/utils/gas-payment.ts"],"names":[],"mappings":"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","sourcesContent":["import { toHex } from '@metamask/controller-utils';\nimport type {\n GasFeeToken,\n TransactionMeta,\n} from '@metamask/transaction-controller';\nimport type { Hex } from '@metamask/utils';\nimport { createModuleLogger } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport { projectLogger } from '../logger.js';\nimport type {\n Amount,\n QuoteRequest,\n TransactionPayControllerMessenger,\n} from '../types.js';\nimport { getFeatureFlags, isEIP7702Chain } from './feature-flags.js';\nimport { calculateGasFeeTokenCost } from './gas.js';\nimport { getNativeToken, getTokenBalance } from './token.js';\n\nconst log = createModuleLogger(projectLogger, 'gas-payment');\n\nexport enum GasPaymentMode {\n /**\n * A relayer redeems a signed delegation and pays origin gas itself, so\n * nothing is submitted through the `TransactionController`.\n */\n Delegation = 'delegation',\n\n /** MetaMask sponsors origin gas through the EIP-7702 gas station hook. */\n Sponsored = 'sponsored',\n\n /**\n * The user pays origin gas in the source token, collected by the EIP-7702\n * gas station as a gas fee token.\n */\n GasFeeToken = 'gas-fee-token',\n\n /** The user pays origin gas in the chain's native token. */\n Native = 'native',\n}\n\n/**\n * Plain-language summary logged for each resolved {@link GasPaymentMode}.\n *\n * Phrased in the future tense because this is the payment MetaMask is about to\n * request, not one that has happened. For the modes that go through the\n * `TransactionController`, the outcome is only known after publish — see\n * {@link logGasPaymentOutcome}.\n *\n * Only emitted at submit time. See {@link resolveGasPayment} for why the\n * quote-time resolution is too provisional to report.\n */\nconst GAS_PAYMENT_MODE_LOG: Record<GasPaymentMode, string> = {\n [GasPaymentMode.Delegation]: 'Gas will be paid by relayer',\n [GasPaymentMode.GasFeeToken]: 'Gas will be paid with source token',\n [GasPaymentMode.Native]: 'Gas will be paid with native token',\n [GasPaymentMode.Sponsored]: 'Gas will be sponsored',\n};\n\n/**\n * Who pays origin gas, plus the `TransactionController` options that implement\n * it. Spread the options straight into `addTransaction` or\n * `addTransactionBatch`.\n */\nexport type GasPayment = {\n /**\n * Stop the `TransactionController` silently falling back to native gas when\n * a gas fee token is set. Always set in {@link GasPaymentMode.GasFeeToken},\n * because reaching that mode means the quote was priced with the gas fee\n * token and the user was shown that fee.\n */\n excludeNativeTokenForFee?: boolean;\n\n /**\n * Token to collect origin gas in. Only set in\n * {@link GasPaymentMode.GasFeeToken}.\n */\n gasFeeToken?: Hex;\n\n /**\n * Whether MetaMask sponsors origin gas. `true` in\n * {@link GasPaymentMode.Sponsored}. Otherwise `false` when sponsorship was\n * evaluated and rejected, or absent when the strategy never sponsors — the\n * `TransactionController` omits the flag entirely rather than recording\n * `false`, so the distinction is carried through.\n */\n isGasFeeSponsored?: boolean;\n\n /** The resolved mode. */\n mode: GasPaymentMode;\n};\n\ntype GasPaymentRequest = {\n /**\n * Whether a relayer will redeem a signed delegation and pay origin gas\n * (Relay `/execute`, server `gasless`, HyperLiquid withdrawals).\n */\n isDelegated?: boolean;\n\n /**\n * Whether the quote priced origin gas in the source token. Leave `undefined`\n * at quote time, before the gas station has been consulted.\n */\n isSourceGasFeeToken?: boolean;\n\n /** Source token charged when paying with a gas fee token. */\n sourceTokenAddress: Hex;\n\n /**\n * Inputs for the MetaMask sponsorship check. Omit for strategies that never\n * sponsor, so `isGasFeeSponsored` is left unset rather than explicitly\n * disabled.\n */\n sponsorship?: SponsorshipRequest;\n};\n\n/** Origin gas priced in source-token units by the gas station. */\ntype GasStationCost = {\n /** The cost, when the gas station will collect gas in the source token. */\n amount?: Amount;\n\n /**\n * Whether the gas station was actually consulted, meaning every eligibility\n * gate passed. `false` means it was skipped, so an absent `amount` says\n * nothing about whether a fee token exists.\n */\n isAvailable: boolean;\n};\n\ntype GasStationCostRequest = {\n /**\n * Whether the paying account can sign EIP-7702 authorizations. Omit when\n * unknown; only an explicit `false` blocks the gas station.\n */\n accountSupports7702?: boolean;\n\n /**\n * Account the gas station simulates the fee token transfer from. Defaults to\n * `request.from`. Predict withdrawals hold the source token in a Safe proxy,\n * so the probe must target the proxy rather than the still-empty EOA.\n */\n feeTokenAccount?: Hex;\n\n /** First origin-chain call, simulated to discover available fee tokens. */\n firstStepData: GasStationStepData;\n\n messenger: TransactionPayControllerMessenger;\n\n /**\n * Native gas cost the user would otherwise pay, in raw units. The gas\n * station is skipped when `request.from` already holds enough native token\n * to cover it. Omit to consult the gas station regardless of balance.\n */\n nativeGasCostRaw?: string;\n\n request: Pick<QuoteRequest, 'from' | 'sourceChainId' | 'sourceTokenAddress'>;\n\n /** Combined gas estimate across every origin-chain call. */\n totalGasEstimate: number;\n\n /** Number of origin-chain calls the estimate covers. */\n totalItemCount: number;\n};\n\nexport type GasStationStepData = {\n data: Hex;\n to: Hex;\n value?: string;\n};\n\ntype SponsorshipRequest = {\n /** Whether the paying account can sign EIP-7702 authorizations. */\n accountSupports7702: boolean | undefined;\n\n request: Pick<QuoteRequest, 'sourceChainId' | 'targetChainId'>;\n\n transaction: Pick<TransactionMeta, 'chainId' | 'isGasFeeSponsored'>;\n};\n\n/**\n * Whether origin gas should be paid through the gas station.\n *\n * True only when the user cannot cover gas in native token *and* the gas\n * station can actually collect it on this chain. A `false` result therefore\n * means either \"native balance covers it\" or \"the gas station is unavailable\n * here\" — it does not mean the gas station was unnecessary. Cheap enough to\n * call before any network work, so it gates the expensive probe flow.\n *\n * @param request - Gas station check.\n * @param request.messenger - Controller messenger.\n * @param request.nativeGasCostRaw - Native gas cost the user would otherwise pay, in raw units.\n * @param request.request - Quote request fields identifying the payment.\n * @returns Whether the gas station should be used for origin gas.\n */\nexport function shouldUseGasStation({\n messenger,\n nativeGasCostRaw,\n request,\n}: {\n messenger: TransactionPayControllerMessenger;\n nativeGasCostRaw: string | undefined;\n request: Pick<QuoteRequest, 'from' | 'sourceChainId'>;\n}): boolean {\n const { isSufficient, nativeBalanceRaw } = checkNativeBalance({\n messenger,\n nativeGasCostRaw,\n request,\n });\n\n if (isSufficient) {\n log('Native balance is sufficient for gas', {\n nativeBalanceRaw,\n nativeGasCostRaw,\n });\n return false;\n }\n\n if (!canUseGasStation(messenger, request.sourceChainId)) {\n return false;\n }\n\n log('Requesting gas fee token as insufficient native token', {\n nativeBalanceRaw,\n nativeGasCostRaw,\n sourceChainId: request.sourceChainId,\n });\n\n return true;\n}\n\n/**\n * Whether the gas station can collect origin gas on a chain at all.\n *\n * Cheap enough to call before doing any work — it only reads feature flags.\n * Use it to skip an expensive code path that would otherwise end in\n * {@link resolveGasStationCost} returning nothing.\n *\n * @param messenger - Controller messenger.\n * @param sourceChainId - Origin chain of the payment.\n * @returns Whether the chain supports EIP-7702 and is not disabled.\n */\nfunction canUseGasStation(\n messenger: TransactionPayControllerMessenger,\n sourceChainId: QuoteRequest['sourceChainId'],\n): boolean {\n const { relayDisabledGasStationChains } = getFeatureFlags(messenger);\n\n if (relayDisabledGasStationChains.includes(sourceChainId)) {\n log('Gas station is disabled for chain', { sourceChainId });\n return false;\n }\n\n if (!isEIP7702Chain(messenger, sourceChainId)) {\n log('Gas station is unsupported on chain', { sourceChainId });\n return false;\n }\n\n return true;\n}\n\n/**\n * Resolve who pays origin gas.\n *\n * Applies the {@link GasPaymentMode} priority order, so a strategy only\n * declares which options are available rather than encoding the precedence\n * itself.\n *\n * Both stages use this. At quote time, call it without `isSourceGasFeeToken`\n * to find out whether anyone else covers gas before spending effort\n * estimating it. At submit time, call it with everything to get the\n * `TransactionController` options.\n *\n * Only the submit-time resolution is logged. Quote-time callers omit\n * `isSourceGasFeeToken`, while submit-time callers include it even when the\n * value is `undefined`. At quote time the gas station has not been consulted\n * yet, so logging would announce the user is paying gas in native token before\n * anything has ruled out the source token.\n *\n * @param request - Payment request.\n * @returns The resolved mode and its `TransactionController` options.\n */\nexport function resolveGasPayment(request: GasPaymentRequest): GasPayment {\n const gasPayment = getGasPayment(request);\n\n if ('isSourceGasFeeToken' in request) {\n log(GAS_PAYMENT_MODE_LOG[gasPayment.mode], gasPayment);\n }\n\n return gasPayment;\n}\n\n/**\n * Log whether the `TransactionController` actually charged gas the way\n * {@link resolveGasPayment} asked it to.\n *\n * `resolveGasPayment` only logs the request. The `TransactionController` makes\n * its own decision at publish time and clears `selectedGasFeeToken` when it\n * declines, so without this the logs would claim the source token paid for gas\n * even when the user was billed in native token.\n *\n * Only meaningful for {@link GasPaymentMode.GasFeeToken}; every other mode\n * either bypasses the `TransactionController` or has nothing to contradict.\n *\n * @param gasPayment - The gas payment that was requested.\n * @param transactions - Final metadata of the submitted transactions.\n */\nexport function logGasPaymentOutcome(\n gasPayment: GasPayment,\n transactions: (\n | Pick<TransactionMeta, 'id' | 'selectedGasFeeToken'>\n | undefined\n )[],\n): void {\n if (gasPayment.mode !== GasPaymentMode.GasFeeToken) {\n return;\n }\n\n const ignored = transactions.filter(\n (transaction) => transaction && !transaction.selectedGasFeeToken,\n );\n\n if (!ignored.length) {\n log('Gas was paid with source token', {\n gasFeeToken: gasPayment.gasFeeToken,\n });\n\n return;\n }\n\n log('Gas fee token was dropped, gas paid with native token instead', {\n gasFeeToken: gasPayment.gasFeeToken,\n transactionIds: ignored.map((transaction) => transaction?.id),\n });\n}\n\nfunction getGasPayment({\n isDelegated,\n isSourceGasFeeToken,\n sourceTokenAddress,\n sponsorship,\n}: GasPaymentRequest): GasPayment {\n if (isDelegated) {\n return { mode: GasPaymentMode.Delegation };\n }\n\n const isSponsored = isSponsoredRoute(sponsorship);\n\n if (isSponsored) {\n return { isGasFeeSponsored: true, mode: GasPaymentMode.Sponsored };\n }\n\n // An absent flag and an explicit `false` are not equivalent to the\n // `TransactionController`, so only set it when sponsorship was evaluated.\n const unsponsored =\n isSponsored === undefined ? {} : { isGasFeeSponsored: isSponsored };\n\n if (isSourceGasFeeToken) {\n // Reaching this mode means the quote priced origin gas in the source token\n // and the user was shown that fee, so the `TransactionController` must not\n // quietly bill them in native token instead. Without this, it drops the\n // gas fee token whenever its own balance check says native covers the gas,\n // and that check uses a live balance and a per-transaction cost while the\n // quote used cached balances and a buffered cost across every origin call.\n return {\n ...unsponsored,\n excludeNativeTokenForFee: true,\n gasFeeToken: sourceTokenAddress,\n mode: GasPaymentMode.GasFeeToken,\n };\n }\n\n return { ...unsponsored, mode: GasPaymentMode.Native };\n}\n\n/**\n * Price origin gas in source-token units, applying every gas station\n * eligibility gate first.\n *\n * The gas station is skipped when the account cannot sign EIP-7702\n * authorizations, when the user already holds enough native token, or when the\n * chain is unsupported or disabled. Callers distinguish \"gas station skipped\"\n * from \"gas station found no fee token\" via {@link GasStationCost.isAvailable}.\n *\n * @param request - Cost request.\n * @param request.accountSupports7702 - Whether the account can sign EIP-7702 authorizations.\n * @param request.feeTokenAccount - Account the fee token transfer is simulated from.\n * @param request.firstStepData - First origin-chain call to simulate.\n * @param request.messenger - Controller messenger.\n * @param request.nativeGasCostRaw - Native gas cost the user would otherwise pay, in raw units.\n * @param request.request - Quote request fields identifying the payment.\n * @param request.totalGasEstimate - Combined gas estimate across every origin-chain call.\n * @param request.totalItemCount - Number of origin-chain calls the estimate covers.\n * @returns The resolved gas station cost.\n */\nexport async function resolveGasStationCost({\n accountSupports7702,\n feeTokenAccount,\n firstStepData,\n messenger,\n nativeGasCostRaw,\n request,\n totalGasEstimate,\n totalItemCount,\n}: GasStationCostRequest): Promise<GasStationCost> {\n const { from, sourceChainId, sourceTokenAddress } = request;\n\n if (accountSupports7702 === false) {\n log('Skipping gas station as account does not support EIP-7702', { from });\n return { isAvailable: false };\n }\n\n const { isSufficient, nativeBalanceRaw } = checkNativeBalance({\n messenger,\n nativeGasCostRaw,\n request,\n });\n\n if (isSufficient) {\n log('Skipping gas station as native balance covers gas', {\n nativeBalanceRaw,\n nativeGasCostRaw,\n sourceChainId,\n });\n return { isAvailable: false };\n }\n\n if (!canUseGasStation(messenger, sourceChainId)) {\n return { isAvailable: false };\n }\n\n log('Requesting gas fee token as insufficient native token', {\n nativeBalanceRaw,\n nativeGasCostRaw,\n sourceChainId,\n sourceTokenAddress,\n });\n\n const amount = await getGasStationCostInSourceToken({\n feeTokenAccount: feeTokenAccount ?? from,\n firstStepData,\n messenger,\n sourceChainId,\n sourceTokenAddress,\n totalGasEstimate,\n totalItemCount,\n });\n\n return { amount, isAvailable: true };\n}\n\nasync function getGasStationCostInSourceToken({\n feeTokenAccount,\n firstStepData,\n messenger,\n sourceChainId,\n sourceTokenAddress,\n totalGasEstimate,\n totalItemCount,\n}: {\n feeTokenAccount: Hex;\n firstStepData: GasStationStepData;\n messenger: TransactionPayControllerMessenger;\n sourceChainId: Hex;\n sourceTokenAddress: Hex;\n totalGasEstimate: number;\n totalItemCount: number;\n}): Promise<Amount | undefined> {\n const { data, to, value } = firstStepData;\n\n let gasFeeTokens: GasFeeToken[] | undefined;\n\n try {\n gasFeeTokens = await messenger.call(\n 'TransactionController:getGasFeeTokens',\n {\n chainId: sourceChainId,\n data,\n from: feeTokenAccount,\n to,\n value: toHex(value ?? '0'),\n },\n );\n } catch (error) {\n log('Failed to estimate gas fee tokens', {\n error,\n sourceChainId,\n });\n return undefined;\n }\n\n const gasFeeToken = gasFeeTokens?.find(\n (singleGasFeeToken) =>\n singleGasFeeToken.tokenAddress.toLowerCase() ===\n sourceTokenAddress.toLowerCase(),\n );\n\n if (!gasFeeToken) {\n log('No matching source token in gas fee token estimate', {\n sourceTokenAddress,\n sourceChainId,\n });\n return undefined;\n }\n\n log('Found gas fee token', {\n amount: gasFeeToken.amount,\n gas: gasFeeToken.gas,\n sourceChainId,\n sourceTokenAddress,\n });\n\n const gasFeeTokenWithNormalizedAmount = {\n ...gasFeeToken,\n amount: toHex(\n getNormalizedGasFeeTokenAmount({\n gasFeeToken,\n totalGasEstimate,\n totalItemCount,\n }),\n ),\n };\n\n const gasFeeTokenCost = calculateGasFeeTokenCost({\n chainId: sourceChainId,\n gasFeeToken: gasFeeTokenWithNormalizedAmount,\n messenger,\n });\n\n if (!gasFeeTokenCost) {\n log('Unable to calculate gas fee token cost using fiat rates', {\n sourceTokenAddress,\n sourceChainId,\n });\n return undefined;\n }\n\n log('Estimated gas station cost for source token', {\n amount: gasFeeTokenCost.raw,\n sourceTokenAddress,\n sourceChainId,\n });\n\n return gasFeeTokenCost;\n}\n\nfunction getNormalizedGasFeeTokenAmount({\n gasFeeToken,\n totalGasEstimate,\n totalItemCount,\n}: {\n gasFeeToken: GasFeeToken;\n totalGasEstimate: number;\n totalItemCount: number;\n}): string {\n let amount = new BigNumber(gasFeeToken.amount);\n\n if (totalItemCount > 1) {\n const gas = new BigNumber(gasFeeToken.gas);\n const gasFeeAmount = new BigNumber(gasFeeToken.amount);\n\n if (totalGasEstimate > 0 && gas.isGreaterThan(0)) {\n const gasRate = gasFeeAmount.dividedBy(gas);\n amount = gasRate.multipliedBy(totalGasEstimate);\n }\n }\n\n return amount.integerValue(BigNumber.ROUND_CEIL).toFixed(0);\n}\n\n/**\n * Read the account's native balance and compare it to a gas cost.\n *\n * The balance is returned alongside the verdict so callers can log what the\n * decision was actually based on. An undefined cost is never sufficient, since\n * an unknown gas cost cannot be shown to be covered.\n *\n * @param request - Balance check.\n * @param request.messenger - Controller messenger.\n * @param request.nativeGasCostRaw - Native gas cost to cover, in raw units.\n * @param request.request - Quote request fields identifying the payment.\n * @returns The raw native balance and whether it covers the cost.\n */\nfunction checkNativeBalance({\n messenger,\n nativeGasCostRaw,\n request,\n}: {\n messenger: TransactionPayControllerMessenger;\n nativeGasCostRaw: string | undefined;\n request: Pick<QuoteRequest, 'from' | 'sourceChainId'>;\n}): { isSufficient: boolean; nativeBalanceRaw: string } {\n const { from, sourceChainId } = request;\n\n const nativeBalanceRaw = getTokenBalance(\n messenger,\n from,\n sourceChainId,\n getNativeToken(sourceChainId),\n );\n\n return {\n isSufficient:\n nativeGasCostRaw !== undefined &&\n new BigNumber(nativeBalanceRaw).isGreaterThanOrEqualTo(nativeGasCostRaw),\n nativeBalanceRaw,\n };\n}\n\n/**\n * Whether MetaMask sponsors origin gas for a route.\n *\n * Sponsorship runs through the EIP-7702 gas station hook, so it requires an\n * account that can sign authorizations and a payment that never leaves the\n * transaction's own chain.\n *\n * Returns `undefined` when there is nothing to evaluate, which callers keep\n * distinct from an explicit `false`.\n *\n * @param sponsorship - Sponsorship inputs, or `undefined` when the strategy never sponsors.\n * @returns Whether the route is sponsored.\n */\nfunction isSponsoredRoute(\n sponsorship: SponsorshipRequest | undefined,\n): boolean | undefined {\n if (!sponsorship) {\n return undefined;\n }\n\n const { accountSupports7702, request, transaction } = sponsorship;\n const { isGasFeeSponsored } = transaction;\n\n if (isGasFeeSponsored === undefined) {\n return undefined;\n }\n\n return (\n isGasFeeSponsored &&\n request.sourceChainId === transaction.chainId &&\n request.targetChainId === transaction.chainId &&\n Boolean(accountSupports7702)\n );\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"max-gas-station.d.ts","sourceRoot":"","sources":["../../src/utils/max-gas-station.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,YAAY,EACZ,iCAAiC,EACjC,mBAAmB,EACpB,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAQ3D;;;GAGG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,8DAA8D;IAC9D,SAAS,EAAE,MAAM,EAAE,CAAC;IAEpB;;;OAGG;IACH,sBAAsB,CAAC,EAAE;QACvB,+BAA+B;QAC/B,aAAa,EAAE,kBAAkB,CAAC;QAElC,iDAAiD;QACjD,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;IAEF,iEAAiE;IACjE,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,oBAAoB,CAAC,aAAa,IAAI;IAChD,0DAA0D;IAC1D,eAAe,EAAE,CACf,KAAK,EAAE,mBAAmB,CAAC,aAAa,CAAC,KACtC,yBAAyB,CAAC;IAE/B,SAAS,EAAE,iCAAiC,CAAC;IAE7C,4CAA4C;IAC5C,KAAK,EAAE,mBAAmB,CAAC,aAAa,CAAC,CAAC;IAE1C,oEAAoE;IACpE,OAAO,EAAE,CACP,OAAO,EAAE,YAAY,KAClB,OAAO,CAAC,mBAAmB,CAAC,aAAa,CAAC,CAAC,CAAC;CAClD,CAAC;AAUF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,qBAAqB,CAAC,aAAa,EACvD,OAAO,EAAE,oBAAoB,CAAC,aAAa,CAAC,GAC3C,OAAO,CAAC,mBAAmB,CAAC,aAAa,CAAC,CAAC,CA8E7C"}
1
+ {"version":3,"file":"max-gas-station.d.ts","sourceRoot":"","sources":["../../src/utils/max-gas-station.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,YAAY,EACZ,iCAAiC,EACjC,mBAAmB,EACpB,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAQ3D;;;GAGG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,8DAA8D;IAC9D,SAAS,EAAE,MAAM,EAAE,CAAC;IAEpB;;;OAGG;IACH,sBAAsB,CAAC,EAAE;QACvB,+BAA+B;QAC/B,aAAa,EAAE,kBAAkB,CAAC;QAElC,iDAAiD;QACjD,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;IAEF,iEAAiE;IACjE,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,oBAAoB,CAAC,aAAa,IAAI;IAChD,0DAA0D;IAC1D,eAAe,EAAE,CACf,KAAK,EAAE,mBAAmB,CAAC,aAAa,CAAC,KACtC,yBAAyB,CAAC;IAE/B,SAAS,EAAE,iCAAiC,CAAC;IAE7C,4CAA4C;IAC5C,KAAK,EAAE,mBAAmB,CAAC,aAAa,CAAC,CAAC;IAE1C,oEAAoE;IACpE,OAAO,EAAE,CACP,OAAO,EAAE,YAAY,KAClB,OAAO,CAAC,mBAAmB,CAAC,aAAa,CAAC,CAAC,CAAC;CAClD,CAAC;AAUF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,qBAAqB,CAAC,aAAa,EACvD,OAAO,EAAE,oBAAoB,CAAC,aAAa,CAAC,GAC3C,OAAO,CAAC,mBAAmB,CAAC,aAAa,CAAC,CAAC,CA2E7C"}
@@ -3,7 +3,7 @@ import { BigNumber } from 'bignumber.js';
3
3
  import { projectLogger } from '../logger.js';
4
4
  import { getGasStationCostBuffer } from './feature-flags.js';
5
5
  import { resolveGasStationCost, shouldUseGasStation } from './gas-payment.js';
6
- import { getEvmQuoteSource, getTokenInfo } from './token.js';
6
+ import { getTokenInfo } from './token.js';
7
7
  const log = createModuleLogger(projectLogger, 'max-gas-station');
8
8
  const PROBE_AMOUNT_PERCENTAGE = 0.25;
9
9
  /**
@@ -43,10 +43,7 @@ export async function getMaxGasStationQuote(request) {
43
43
  nativeGasCostRaw: quote.fees.isSourceGasFeeToken
44
44
  ? undefined
45
45
  : quote.fees.sourceNetwork.max.raw,
46
- request: {
47
- from: context.request.from,
48
- sourceChainId: getEvmQuoteSource(context.request).chainId,
49
- },
46
+ request: context.request,
50
47
  });
51
48
  if (!useGasStation) {
52
49
  return fallback(quote, 'Native balance covers gas or gas station is unavailable');
@@ -127,8 +124,7 @@ function getAdjustedQuoteValidationError({ adjustedQuote, adjustedSourceAmount,
127
124
  */
128
125
  async function getProbeGasCost(context) {
129
126
  const { getQuoteDetails, messenger, request, requote } = context;
130
- const { chainId: sourceChainId, tokenAddress: sourceTokenAddress } = getEvmQuoteSource(request);
131
- const sourceToken = getTokenInfo(messenger, sourceTokenAddress, sourceChainId);
127
+ const sourceToken = getTokenInfo(messenger, request.sourceTokenAddress, request.sourceChainId);
132
128
  if (!sourceToken) {
133
129
  return undefined;
134
130
  }
@@ -167,8 +163,8 @@ async function getProbeGasCost(context) {
167
163
  messenger,
168
164
  request: {
169
165
  from: request.from,
170
- sourceChainId,
171
- sourceTokenAddress,
166
+ sourceChainId: request.sourceChainId,
167
+ sourceTokenAddress: request.sourceTokenAddress,
172
168
  },
173
169
  totalGasEstimate: sum(gasLimits),
174
170
  totalItemCount: gasStationTransactions.totalItemCount,
@@ -1 +1 @@
1
- {"version":3,"file":"max-gas-station.js","sourceRoot":"","sources":["../../src/utils/max-gas-station.ts"],"names":[],"mappings":"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","sourcesContent":["import { createModuleLogger } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport { projectLogger } from '../logger.js';\nimport type {\n QuoteRequest,\n TransactionPayControllerMessenger,\n TransactionPayQuote,\n} from '../types.js';\nimport { getGasStationCostBuffer } from './feature-flags.js';\nimport type { GasStationStepData } from './gas-payment.js';\nimport { resolveGasStationCost, shouldUseGasStation } from './gas-payment.js';\nimport { getEvmQuoteSource, getTokenInfo } from './token.js';\n\nconst log = createModuleLogger(projectLogger, 'max-gas-station');\n\nconst PROBE_AMOUNT_PERCENTAGE = 0.25;\n\n/**\n * Provider-agnostic view of a quote, supplied by each strategy so the max gas\n * station flow never reads a strategy's original quote shape directly.\n */\nexport type MaxGasStationQuoteDetails = {\n /** Gas limits estimated for each origin-chain transaction. */\n gasLimits: number[];\n\n /**\n * Origin-chain transactions the gas station simulates to price gas in the\n * source token, or `undefined` when the quote has none.\n */\n gasStationTransactions?: {\n /** First origin-chain call. */\n firstStepData: GasStationStepData;\n\n /** Number of origin-chain calls in the quote. */\n totalItemCount: number;\n };\n\n /** Whether origin gas is paid by someone other than the user. */\n isGasless: boolean;\n};\n\n/**\n * Callbacks bridging the max gas station flow to a specific strategy.\n */\nexport type MaxGasStationRequest<OriginalQuote> = {\n /** Map a strategy quote to the details the flow needs. */\n getQuoteDetails: (\n quote: TransactionPayQuote<OriginalQuote>,\n ) => MaxGasStationQuoteDetails;\n\n messenger: TransactionPayControllerMessenger;\n\n /** Quote for the full max source amount. */\n quote: TransactionPayQuote<OriginalQuote>;\n\n /** Fetch a quote for an adjusted request from the same provider. */\n requote: (\n request: QuoteRequest,\n ) => Promise<TransactionPayQuote<OriginalQuote>>;\n};\n\ntype MaxGasStationContext<OriginalQuote> = Pick<\n MaxGasStationRequest<OriginalQuote>,\n 'getQuoteDetails' | 'messenger' | 'requote'\n> & {\n /** Request of the original max quote, captured before any re-quote. */\n request: QuoteRequest;\n};\n\n/**\n * Re-quote a max-amount flow so origin gas can be paid from the source token.\n *\n * A max quote commits the whole source balance, so when the user cannot cover\n * origin gas in native token the gas station has nothing left to collect. This\n * estimates the gas cost in source-token units via a smaller probe quote, then\n * re-quotes with the source amount reduced by that cost plus a configurable\n * buffer.\n *\n * The adjusted quote is accepted only if it is still priced with the source\n * gas fee token and the reduced amount plus its gas cost still fits the\n * original amount. Otherwise, and on any failure, the original quote is\n * returned.\n *\n * @param request - Max gas station request.\n * @returns The adjusted quote, or the original quote on fallback.\n */\nexport async function getMaxGasStationQuote<OriginalQuote>(\n request: MaxGasStationRequest<OriginalQuote>,\n): Promise<TransactionPayQuote<OriginalQuote>> {\n const { getQuoteDetails, messenger, quote, requote } = request;\n const context: MaxGasStationContext<OriginalQuote> = {\n getQuoteDetails,\n messenger,\n request: quote.request,\n requote,\n };\n const details = getQuoteDetails(quote);\n\n if (details.isGasless) {\n return fallback(quote, 'Quote is gasless');\n }\n\n // A source-token gas fee means the native balance is irrelevant, so skip the\n // balance check entirely rather than comparing against a cost the user will\n // never pay in native token.\n const useGasStation = shouldUseGasStation({\n messenger,\n nativeGasCostRaw: quote.fees.isSourceGasFeeToken\n ? undefined\n : quote.fees.sourceNetwork.max.raw,\n request: {\n from: context.request.from,\n sourceChainId: getEvmQuoteSource(context.request).chainId,\n },\n });\n\n if (!useGasStation) {\n return fallback(\n quote,\n 'Native balance covers gas or gas station is unavailable',\n );\n }\n\n const sourceAmount = new BigNumber(context.request.sourceTokenAmount);\n const probeGasCost = await getProbeGasCost(context);\n\n if (!probeGasCost) {\n return fallback(quote, 'Unable to estimate gas-station source token cost');\n }\n\n const reservedGasCost = probeGasCost\n .multipliedBy(getGasStationCostBuffer(messenger))\n .integerValue(BigNumber.ROUND_UP);\n\n const adjustedSourceAmount = sourceAmount\n .minus(reservedGasCost)\n .integerValue(BigNumber.ROUND_DOWN);\n\n if (!adjustedSourceAmount.isGreaterThan(0)) {\n return fallback(\n quote,\n 'Insufficient balance for gas station after max adjustment',\n );\n }\n\n const adjustedQuote = await getAdjustedQuote(\n adjustedSourceAmount,\n reservedGasCost,\n context,\n );\n\n if (!adjustedQuote) {\n return fallback(quote, 'Adjusted quote request failed');\n }\n\n const validationError = getAdjustedQuoteValidationError({\n adjustedQuote,\n adjustedSourceAmount,\n sourceAmount,\n });\n\n if (validationError) {\n return fallback(quote, validationError.message, validationError.params);\n }\n\n return adjustedQuote;\n}\n\nasync function getAdjustedQuote<OriginalQuote>(\n adjustedSourceAmount: BigNumber,\n reservedGasCost: BigNumber,\n { request, requote }: MaxGasStationContext<OriginalQuote>,\n): Promise<TransactionPayQuote<OriginalQuote> | undefined> {\n log('Requesting adjusted max quote', {\n adjustedAmount: adjustedSourceAmount.toString(10),\n gasCostInSourceToken: reservedGasCost.toString(10),\n originalAmount: request.sourceTokenAmount,\n });\n\n try {\n return await requote({\n ...request,\n sourceTokenAmount: adjustedSourceAmount.toFixed(0, BigNumber.ROUND_DOWN),\n });\n } catch (error) {\n log('Adjusted quote request failed', { error });\n return undefined;\n }\n}\n\nfunction getAdjustedQuoteValidationError<OriginalQuote>({\n adjustedQuote,\n adjustedSourceAmount,\n sourceAmount,\n}: {\n adjustedQuote: TransactionPayQuote<OriginalQuote>;\n adjustedSourceAmount: BigNumber;\n sourceAmount: BigNumber;\n}): { message: string; params?: Record<string, unknown> } | undefined {\n if (!adjustedQuote.fees.isSourceGasFeeToken) {\n return {\n message: 'Adjusted quote did not return source gas fee token pricing',\n };\n }\n\n const adjustedGasCost = new BigNumber(\n adjustedQuote.fees.sourceNetwork.max.raw,\n );\n\n const isAffordable = adjustedSourceAmount\n .plus(adjustedGasCost)\n .isLessThanOrEqualTo(sourceAmount);\n\n if (!isAffordable) {\n return {\n message: 'Adjusted quote fails affordability validation',\n params: {\n adjustedGasCost: adjustedGasCost.toString(10),\n adjustedSourceAmount: adjustedSourceAmount.toString(10),\n },\n };\n }\n\n return undefined;\n}\n\n/**\n * Estimate origin gas in source-token units using a quote for a fraction of\n * the source amount, leaving balance for the gas station to collect.\n *\n * @param context - Max gas station context.\n * @returns The probe's gas cost in raw source-token units, or `undefined` if\n * unknown.\n */\nasync function getProbeGasCost<OriginalQuote>(\n context: MaxGasStationContext<OriginalQuote>,\n): Promise<BigNumber | undefined> {\n const { getQuoteDetails, messenger, request, requote } = context;\n const { chainId: sourceChainId, tokenAddress: sourceTokenAddress } =\n getEvmQuoteSource(request);\n const sourceToken = getTokenInfo(\n messenger,\n sourceTokenAddress,\n sourceChainId,\n );\n\n if (!sourceToken) {\n return undefined;\n }\n\n const probeAmount = getProbeAmount(\n request.sourceTokenAmount,\n sourceToken.decimals,\n );\n\n if (!probeAmount || probeAmount === request.sourceTokenAmount) {\n return undefined;\n }\n\n log('Requesting probe quote for gas station estimation', {\n originalSourceAmount: request.sourceTokenAmount,\n probeAmount,\n });\n\n let probeQuote: TransactionPayQuote<OriginalQuote> | undefined;\n\n try {\n probeQuote = await requote({ ...request, sourceTokenAmount: probeAmount });\n } catch (error) {\n log('Probe quote request failed', { error });\n return undefined;\n }\n\n if (!probeQuote) {\n return undefined;\n }\n\n const { gasLimits, gasStationTransactions } = getQuoteDetails(probeQuote);\n\n if (probeQuote.fees.isSourceGasFeeToken) {\n const { raw } = probeQuote.fees.sourceNetwork.max;\n log('Probe gas cost already in source token units', { raw });\n return new BigNumber(raw);\n }\n\n if (!gasStationTransactions) {\n return undefined;\n }\n\n // Native balance and chain eligibility were already checked before the probe\n // flow started, so only the source-token pricing is needed here.\n const gasStationCost = await resolveGasStationCost({\n firstStepData: gasStationTransactions.firstStepData,\n messenger,\n request: {\n from: request.from,\n sourceChainId,\n sourceTokenAddress,\n },\n totalGasEstimate: sum(gasLimits),\n totalItemCount: gasStationTransactions.totalItemCount,\n });\n\n if (!gasStationCost.amount) {\n return undefined;\n }\n\n return new BigNumber(gasStationCost.amount.raw);\n}\n\nfunction getProbeAmount(\n sourceAmountRaw: string,\n sourceDecimals: number,\n): string | undefined {\n const sourceAmount = new BigNumber(sourceAmountRaw);\n\n if (sourceAmount.isLessThanOrEqualTo(0)) {\n return undefined;\n }\n\n const probeRawAmount = sourceAmount\n .multipliedBy(PROBE_AMOUNT_PERCENTAGE)\n .integerValue(BigNumber.ROUND_FLOOR);\n\n // Minimum probe size: ~0.01 token for tokens with >=2 decimals,\n // otherwise one raw unit for low-decimal tokens.\n const minimumProbeRaw = new BigNumber(1).shiftedBy(\n Math.max(sourceDecimals - 2, 0),\n );\n\n const probeRaw = BigNumber.minimum(\n sourceAmount,\n BigNumber.maximum(probeRawAmount, minimumProbeRaw),\n ).integerValue(BigNumber.ROUND_FLOOR);\n\n if (probeRaw.isLessThanOrEqualTo(0)) {\n return undefined;\n }\n\n return probeRaw.toFixed(0);\n}\n\nfunction fallback<OriginalQuote>(\n quote: TransactionPayQuote<OriginalQuote>,\n message: string,\n params?: Record<string, unknown>,\n): TransactionPayQuote<OriginalQuote> {\n log(`Using original max quote: ${message}`, params);\n return quote;\n}\n\nfunction sum(values: number[]): number {\n return values.reduce((total, value) => total + value, 0);\n}\n"]}
1
+ {"version":3,"file":"max-gas-station.js","sourceRoot":"","sources":["../../src/utils/max-gas-station.ts"],"names":[],"mappings":"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","sourcesContent":["import { createModuleLogger } from '@metamask/utils';\nimport { BigNumber } from 'bignumber.js';\n\nimport { projectLogger } from '../logger.js';\nimport type {\n QuoteRequest,\n TransactionPayControllerMessenger,\n TransactionPayQuote,\n} from '../types.js';\nimport { getGasStationCostBuffer } from './feature-flags.js';\nimport type { GasStationStepData } from './gas-payment.js';\nimport { resolveGasStationCost, shouldUseGasStation } from './gas-payment.js';\nimport { getTokenInfo } from './token.js';\n\nconst log = createModuleLogger(projectLogger, 'max-gas-station');\n\nconst PROBE_AMOUNT_PERCENTAGE = 0.25;\n\n/**\n * Provider-agnostic view of a quote, supplied by each strategy so the max gas\n * station flow never reads a strategy's original quote shape directly.\n */\nexport type MaxGasStationQuoteDetails = {\n /** Gas limits estimated for each origin-chain transaction. */\n gasLimits: number[];\n\n /**\n * Origin-chain transactions the gas station simulates to price gas in the\n * source token, or `undefined` when the quote has none.\n */\n gasStationTransactions?: {\n /** First origin-chain call. */\n firstStepData: GasStationStepData;\n\n /** Number of origin-chain calls in the quote. */\n totalItemCount: number;\n };\n\n /** Whether origin gas is paid by someone other than the user. */\n isGasless: boolean;\n};\n\n/**\n * Callbacks bridging the max gas station flow to a specific strategy.\n */\nexport type MaxGasStationRequest<OriginalQuote> = {\n /** Map a strategy quote to the details the flow needs. */\n getQuoteDetails: (\n quote: TransactionPayQuote<OriginalQuote>,\n ) => MaxGasStationQuoteDetails;\n\n messenger: TransactionPayControllerMessenger;\n\n /** Quote for the full max source amount. */\n quote: TransactionPayQuote<OriginalQuote>;\n\n /** Fetch a quote for an adjusted request from the same provider. */\n requote: (\n request: QuoteRequest,\n ) => Promise<TransactionPayQuote<OriginalQuote>>;\n};\n\ntype MaxGasStationContext<OriginalQuote> = Pick<\n MaxGasStationRequest<OriginalQuote>,\n 'getQuoteDetails' | 'messenger' | 'requote'\n> & {\n /** Request of the original max quote, captured before any re-quote. */\n request: QuoteRequest;\n};\n\n/**\n * Re-quote a max-amount flow so origin gas can be paid from the source token.\n *\n * A max quote commits the whole source balance, so when the user cannot cover\n * origin gas in native token the gas station has nothing left to collect. This\n * estimates the gas cost in source-token units via a smaller probe quote, then\n * re-quotes with the source amount reduced by that cost plus a configurable\n * buffer.\n *\n * The adjusted quote is accepted only if it is still priced with the source\n * gas fee token and the reduced amount plus its gas cost still fits the\n * original amount. Otherwise, and on any failure, the original quote is\n * returned.\n *\n * @param request - Max gas station request.\n * @returns The adjusted quote, or the original quote on fallback.\n */\nexport async function getMaxGasStationQuote<OriginalQuote>(\n request: MaxGasStationRequest<OriginalQuote>,\n): Promise<TransactionPayQuote<OriginalQuote>> {\n const { getQuoteDetails, messenger, quote, requote } = request;\n const context: MaxGasStationContext<OriginalQuote> = {\n getQuoteDetails,\n messenger,\n request: quote.request,\n requote,\n };\n const details = getQuoteDetails(quote);\n\n if (details.isGasless) {\n return fallback(quote, 'Quote is gasless');\n }\n\n // A source-token gas fee means the native balance is irrelevant, so skip the\n // balance check entirely rather than comparing against a cost the user will\n // never pay in native token.\n const useGasStation = shouldUseGasStation({\n messenger,\n nativeGasCostRaw: quote.fees.isSourceGasFeeToken\n ? undefined\n : quote.fees.sourceNetwork.max.raw,\n request: context.request,\n });\n\n if (!useGasStation) {\n return fallback(\n quote,\n 'Native balance covers gas or gas station is unavailable',\n );\n }\n\n const sourceAmount = new BigNumber(context.request.sourceTokenAmount);\n const probeGasCost = await getProbeGasCost(context);\n\n if (!probeGasCost) {\n return fallback(quote, 'Unable to estimate gas-station source token cost');\n }\n\n const reservedGasCost = probeGasCost\n .multipliedBy(getGasStationCostBuffer(messenger))\n .integerValue(BigNumber.ROUND_UP);\n\n const adjustedSourceAmount = sourceAmount\n .minus(reservedGasCost)\n .integerValue(BigNumber.ROUND_DOWN);\n\n if (!adjustedSourceAmount.isGreaterThan(0)) {\n return fallback(\n quote,\n 'Insufficient balance for gas station after max adjustment',\n );\n }\n\n const adjustedQuote = await getAdjustedQuote(\n adjustedSourceAmount,\n reservedGasCost,\n context,\n );\n\n if (!adjustedQuote) {\n return fallback(quote, 'Adjusted quote request failed');\n }\n\n const validationError = getAdjustedQuoteValidationError({\n adjustedQuote,\n adjustedSourceAmount,\n sourceAmount,\n });\n\n if (validationError) {\n return fallback(quote, validationError.message, validationError.params);\n }\n\n return adjustedQuote;\n}\n\nasync function getAdjustedQuote<OriginalQuote>(\n adjustedSourceAmount: BigNumber,\n reservedGasCost: BigNumber,\n { request, requote }: MaxGasStationContext<OriginalQuote>,\n): Promise<TransactionPayQuote<OriginalQuote> | undefined> {\n log('Requesting adjusted max quote', {\n adjustedAmount: adjustedSourceAmount.toString(10),\n gasCostInSourceToken: reservedGasCost.toString(10),\n originalAmount: request.sourceTokenAmount,\n });\n\n try {\n return await requote({\n ...request,\n sourceTokenAmount: adjustedSourceAmount.toFixed(0, BigNumber.ROUND_DOWN),\n });\n } catch (error) {\n log('Adjusted quote request failed', { error });\n return undefined;\n }\n}\n\nfunction getAdjustedQuoteValidationError<OriginalQuote>({\n adjustedQuote,\n adjustedSourceAmount,\n sourceAmount,\n}: {\n adjustedQuote: TransactionPayQuote<OriginalQuote>;\n adjustedSourceAmount: BigNumber;\n sourceAmount: BigNumber;\n}): { message: string; params?: Record<string, unknown> } | undefined {\n if (!adjustedQuote.fees.isSourceGasFeeToken) {\n return {\n message: 'Adjusted quote did not return source gas fee token pricing',\n };\n }\n\n const adjustedGasCost = new BigNumber(\n adjustedQuote.fees.sourceNetwork.max.raw,\n );\n\n const isAffordable = adjustedSourceAmount\n .plus(adjustedGasCost)\n .isLessThanOrEqualTo(sourceAmount);\n\n if (!isAffordable) {\n return {\n message: 'Adjusted quote fails affordability validation',\n params: {\n adjustedGasCost: adjustedGasCost.toString(10),\n adjustedSourceAmount: adjustedSourceAmount.toString(10),\n },\n };\n }\n\n return undefined;\n}\n\n/**\n * Estimate origin gas in source-token units using a quote for a fraction of\n * the source amount, leaving balance for the gas station to collect.\n *\n * @param context - Max gas station context.\n * @returns The probe's gas cost in raw source-token units, or `undefined` if\n * unknown.\n */\nasync function getProbeGasCost<OriginalQuote>(\n context: MaxGasStationContext<OriginalQuote>,\n): Promise<BigNumber | undefined> {\n const { getQuoteDetails, messenger, request, requote } = context;\n const sourceToken = getTokenInfo(\n messenger,\n request.sourceTokenAddress,\n request.sourceChainId,\n );\n\n if (!sourceToken) {\n return undefined;\n }\n\n const probeAmount = getProbeAmount(\n request.sourceTokenAmount,\n sourceToken.decimals,\n );\n\n if (!probeAmount || probeAmount === request.sourceTokenAmount) {\n return undefined;\n }\n\n log('Requesting probe quote for gas station estimation', {\n originalSourceAmount: request.sourceTokenAmount,\n probeAmount,\n });\n\n let probeQuote: TransactionPayQuote<OriginalQuote> | undefined;\n\n try {\n probeQuote = await requote({ ...request, sourceTokenAmount: probeAmount });\n } catch (error) {\n log('Probe quote request failed', { error });\n return undefined;\n }\n\n if (!probeQuote) {\n return undefined;\n }\n\n const { gasLimits, gasStationTransactions } = getQuoteDetails(probeQuote);\n\n if (probeQuote.fees.isSourceGasFeeToken) {\n const { raw } = probeQuote.fees.sourceNetwork.max;\n log('Probe gas cost already in source token units', { raw });\n return new BigNumber(raw);\n }\n\n if (!gasStationTransactions) {\n return undefined;\n }\n\n // Native balance and chain eligibility were already checked before the probe\n // flow started, so only the source-token pricing is needed here.\n const gasStationCost = await resolveGasStationCost({\n firstStepData: gasStationTransactions.firstStepData,\n messenger,\n request: {\n from: request.from,\n sourceChainId: request.sourceChainId,\n sourceTokenAddress: request.sourceTokenAddress,\n },\n totalGasEstimate: sum(gasLimits),\n totalItemCount: gasStationTransactions.totalItemCount,\n });\n\n if (!gasStationCost.amount) {\n return undefined;\n }\n\n return new BigNumber(gasStationCost.amount.raw);\n}\n\nfunction getProbeAmount(\n sourceAmountRaw: string,\n sourceDecimals: number,\n): string | undefined {\n const sourceAmount = new BigNumber(sourceAmountRaw);\n\n if (sourceAmount.isLessThanOrEqualTo(0)) {\n return undefined;\n }\n\n const probeRawAmount = sourceAmount\n .multipliedBy(PROBE_AMOUNT_PERCENTAGE)\n .integerValue(BigNumber.ROUND_FLOOR);\n\n // Minimum probe size: ~0.01 token for tokens with >=2 decimals,\n // otherwise one raw unit for low-decimal tokens.\n const minimumProbeRaw = new BigNumber(1).shiftedBy(\n Math.max(sourceDecimals - 2, 0),\n );\n\n const probeRaw = BigNumber.minimum(\n sourceAmount,\n BigNumber.maximum(probeRawAmount, minimumProbeRaw),\n ).integerValue(BigNumber.ROUND_FLOOR);\n\n if (probeRaw.isLessThanOrEqualTo(0)) {\n return undefined;\n }\n\n return probeRaw.toFixed(0);\n}\n\nfunction fallback<OriginalQuote>(\n quote: TransactionPayQuote<OriginalQuote>,\n message: string,\n params?: Record<string, unknown>,\n): TransactionPayQuote<OriginalQuote> {\n log(`Using original max quote: ${message}`, params);\n return quote;\n}\n\nfunction sum(values: number[]): number {\n return values.reduce((total, value) => total + value, 0);\n}\n"]}
@@ -4,7 +4,7 @@ import { accountRequiresMfa } from './account.js';
4
4
  import { prefixError } from './error-prefix.js';
5
5
  import { isAtomicMaxEnabled } from './feature-flags.js';
6
6
  import { getMaxGasStationQuote } from './max-gas-station.js';
7
- import { getEvmQuoteSource, getTokenInfo } from './token.js';
7
+ import { getTokenInfo } from './token.js';
8
8
  import { QuoteError } from './validation.js';
9
9
  const ATOMIC_PROMOTION_FAILURE_PREFIX = 'Atomic promotion failed: ';
10
10
  /**
@@ -155,8 +155,7 @@ function withGasStation(quote, { fullRequest, getQuoteDetails, requote }) {
155
155
  * @returns Raw target amount.
156
156
  */
157
157
  function getFixedRateTargetAmount(messenger, request) {
158
- const { chainId: sourceChainId, tokenAddress: sourceTokenAddress } = getEvmQuoteSource(request);
159
- const sourceToken = getTokenInfo(messenger, sourceTokenAddress, sourceChainId);
158
+ const sourceToken = getTokenInfo(messenger, request.sourceTokenAddress, request.sourceChainId);
160
159
  const targetToken = getTokenInfo(messenger, request.targetTokenAddress, request.targetChainId);
161
160
  if (!sourceToken || !targetToken) {
162
161
  throw new Error('Token decimals not found for atomic max quote');