@metamask/transaction-pay-controller 16.4.0 → 16.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/CHANGELOG.md +29 -1
  2. package/dist/strategy/across/across-quotes.cjs +42 -8
  3. package/dist/strategy/across/across-quotes.cjs.map +1 -1
  4. package/dist/strategy/across/across-quotes.d.cts.map +1 -1
  5. package/dist/strategy/across/across-quotes.d.mts.map +1 -1
  6. package/dist/strategy/across/across-quotes.mjs +42 -8
  7. package/dist/strategy/across/across-quotes.mjs.map +1 -1
  8. package/dist/strategy/relay/constants.cjs +3 -1
  9. package/dist/strategy/relay/constants.cjs.map +1 -1
  10. package/dist/strategy/relay/constants.d.cts +2 -0
  11. package/dist/strategy/relay/constants.d.cts.map +1 -1
  12. package/dist/strategy/relay/constants.d.mts +2 -0
  13. package/dist/strategy/relay/constants.d.mts.map +1 -1
  14. package/dist/strategy/relay/constants.mjs +2 -0
  15. package/dist/strategy/relay/constants.mjs.map +1 -1
  16. package/dist/strategy/relay/gas-station.cjs +1 -2
  17. package/dist/strategy/relay/gas-station.cjs.map +1 -1
  18. package/dist/strategy/relay/gas-station.d.cts.map +1 -1
  19. package/dist/strategy/relay/gas-station.d.mts.map +1 -1
  20. package/dist/strategy/relay/gas-station.mjs +2 -3
  21. package/dist/strategy/relay/gas-station.mjs.map +1 -1
  22. package/dist/strategy/relay/relay-api.cjs +55 -0
  23. package/dist/strategy/relay/relay-api.cjs.map +1 -0
  24. package/dist/strategy/relay/relay-api.d.cts +26 -0
  25. package/dist/strategy/relay/relay-api.d.cts.map +1 -0
  26. package/dist/strategy/relay/relay-api.d.mts +26 -0
  27. package/dist/strategy/relay/relay-api.d.mts.map +1 -0
  28. package/dist/strategy/relay/relay-api.mjs +49 -0
  29. package/dist/strategy/relay/relay-api.mjs.map +1 -0
  30. package/dist/strategy/relay/relay-quotes.cjs +114 -31
  31. package/dist/strategy/relay/relay-quotes.cjs.map +1 -1
  32. package/dist/strategy/relay/relay-quotes.d.cts.map +1 -1
  33. package/dist/strategy/relay/relay-quotes.d.mts.map +1 -1
  34. package/dist/strategy/relay/relay-quotes.mjs +116 -33
  35. package/dist/strategy/relay/relay-quotes.mjs.map +1 -1
  36. package/dist/strategy/relay/relay-submit.cjs +120 -8
  37. package/dist/strategy/relay/relay-submit.cjs.map +1 -1
  38. package/dist/strategy/relay/relay-submit.d.cts.map +1 -1
  39. package/dist/strategy/relay/relay-submit.d.mts.map +1 -1
  40. package/dist/strategy/relay/relay-submit.mjs +123 -11
  41. package/dist/strategy/relay/relay-submit.mjs.map +1 -1
  42. package/dist/strategy/relay/types.cjs.map +1 -1
  43. package/dist/strategy/relay/types.d.cts +27 -0
  44. package/dist/strategy/relay/types.d.cts.map +1 -1
  45. package/dist/strategy/relay/types.d.mts +27 -0
  46. package/dist/strategy/relay/types.d.mts.map +1 -1
  47. package/dist/strategy/relay/types.mjs.map +1 -1
  48. package/dist/tests/messenger-mock.d.cts +8 -1
  49. package/dist/tests/messenger-mock.d.cts.map +1 -1
  50. package/dist/tests/messenger-mock.d.mts +8 -1
  51. package/dist/tests/messenger-mock.d.mts.map +1 -1
  52. package/dist/utils/feature-flags.cjs +37 -7
  53. package/dist/utils/feature-flags.cjs.map +1 -1
  54. package/dist/utils/feature-flags.d.cts +24 -3
  55. package/dist/utils/feature-flags.d.cts.map +1 -1
  56. package/dist/utils/feature-flags.d.mts +24 -3
  57. package/dist/utils/feature-flags.d.mts.map +1 -1
  58. package/dist/utils/feature-flags.mjs +34 -6
  59. package/dist/utils/feature-flags.mjs.map +1 -1
  60. package/dist/utils/transaction.cjs +16 -1
  61. package/dist/utils/transaction.cjs.map +1 -1
  62. package/dist/utils/transaction.d.cts +10 -0
  63. package/dist/utils/transaction.d.cts.map +1 -1
  64. package/dist/utils/transaction.d.mts +10 -0
  65. package/dist/utils/transaction.d.mts.map +1 -1
  66. package/dist/utils/transaction.mjs +15 -1
  67. package/dist/utils/transaction.mjs.map +1 -1
  68. package/package.json +5 -5
@@ -1 +1 @@
1
- {"version":3,"file":"constants.mjs","sourceRoot":"","sources":["../../../src/strategy/relay/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,yCAAyC;AAEnE,MAAM,CAAC,MAAM,cAAc,GAAG,wBAAwB,CAAC;AACvD,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,cAAc,oBAAoB,CAAC;AACtE,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAC,CAAC,WAAW;AACvD,MAAM,CAAC,MAAM,wBAAwB,GAAG,YAAY,CAAC;AAErD,MAAM,CAAC,MAAM,mBAAmB,GAAoC;IAClE,CAAC,eAAe,CAAC,cAAc,CAAC,EAAE,eAAe,CAAC,mBAAmB;IACrE,CAAC,eAAe,CAAC,YAAY,CAAC,EAAE,eAAe,CAAC,iBAAiB;CAClE,CAAC","sourcesContent":["import { TransactionType } from '@metamask/transaction-controller';\n\nexport const RELAY_URL_BASE = 'https://api.relay.link';\nexport const RELAY_STATUS_URL = `${RELAY_URL_BASE}/intents/status/v3`;\nexport const RELAY_POLLING_INTERVAL = 1000; // 1 Second\nexport const TOKEN_TRANSFER_FOUR_BYTE = '0xa9059cbb';\n\nexport const RELAY_DEPOSIT_TYPES: Record<string, TransactionType> = {\n [TransactionType.predictDeposit]: TransactionType.predictRelayDeposit,\n [TransactionType.perpsDeposit]: TransactionType.perpsRelayDeposit,\n};\n"]}
1
+ {"version":3,"file":"constants.mjs","sourceRoot":"","sources":["../../../src/strategy/relay/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,yCAAyC;AAEnE,MAAM,CAAC,MAAM,cAAc,GAAG,wBAAwB,CAAC;AACvD,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,cAAc,UAAU,CAAC;AAC7D,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,cAAc,QAAQ,CAAC;AACzD,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,cAAc,oBAAoB,CAAC;AACtE,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAC,CAAC,WAAW;AACvD,MAAM,CAAC,MAAM,wBAAwB,GAAG,YAAY,CAAC;AAErD,MAAM,CAAC,MAAM,mBAAmB,GAAoC;IAClE,CAAC,eAAe,CAAC,cAAc,CAAC,EAAE,eAAe,CAAC,mBAAmB;IACrE,CAAC,eAAe,CAAC,YAAY,CAAC,EAAE,eAAe,CAAC,iBAAiB;CAClE,CAAC","sourcesContent":["import { TransactionType } from '@metamask/transaction-controller';\n\nexport const RELAY_URL_BASE = 'https://api.relay.link';\nexport const RELAY_EXECUTE_URL = `${RELAY_URL_BASE}/execute`;\nexport const RELAY_QUOTE_URL = `${RELAY_URL_BASE}/quote`;\nexport const RELAY_STATUS_URL = `${RELAY_URL_BASE}/intents/status/v3`;\nexport const RELAY_POLLING_INTERVAL = 1000; // 1 Second\nexport const TOKEN_TRANSFER_FOUR_BYTE = '0xa9059cbb';\n\nexport const RELAY_DEPOSIT_TYPES: Record<string, TransactionType> = {\n [TransactionType.predictDeposit]: TransactionType.predictRelayDeposit,\n [TransactionType.perpsDeposit]: TransactionType.perpsRelayDeposit,\n};\n"]}
@@ -10,8 +10,7 @@ const gas_1 = require("../../utils/gas.cjs");
10
10
  const log = (0, utils_1.createModuleLogger)(logger_1.projectLogger, 'relay-gas-station');
11
11
  function getGasStationEligibility(messenger, sourceChainId) {
12
12
  const { relayDisabledGasStationChains } = (0, feature_flags_1.getFeatureFlags)(messenger);
13
- const supportedChains = (0, feature_flags_1.getEIP7702SupportedChains)(messenger);
14
- const chainSupportsGasStation = supportedChains.some((supportedChainId) => supportedChainId.toLowerCase() === sourceChainId.toLowerCase());
13
+ const chainSupportsGasStation = (0, feature_flags_1.isEIP7702Chain)(messenger, sourceChainId);
15
14
  const isDisabledChain = relayDisabledGasStationChains.includes(sourceChainId);
16
15
  return {
17
16
  chainSupportsGasStation,
@@ -1 +1 @@
1
- {"version":3,"file":"gas-station.cjs","sourceRoot":"","sources":["../../../src/strategy/relay/gas-station.ts"],"names":[],"mappings":"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","sourcesContent":["import { toHex } from '@metamask/controller-utils';\nimport type { GasFeeToken } 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';\nimport type {\n Amount,\n QuoteRequest,\n TransactionPayControllerMessenger,\n} from '../../types';\nimport {\n getEIP7702SupportedChains,\n getFeatureFlags,\n} from '../../utils/feature-flags';\nimport { calculateGasFeeTokenCost } from '../../utils/gas';\n\nconst log = createModuleLogger(projectLogger, 'relay-gas-station');\n\ntype GasStationCostParams = {\n firstStepData: {\n data: Hex;\n to: Hex;\n value?: string;\n };\n messenger: TransactionPayControllerMessenger;\n request: Pick<QuoteRequest, 'from' | 'sourceChainId' | 'sourceTokenAddress'>;\n totalGasEstimate: number;\n totalItemCount: number;\n};\n\nexport type GasStationEligibility = {\n chainSupportsGasStation: boolean;\n isDisabledChain: boolean;\n isEligible: boolean;\n};\n\nexport function getGasStationEligibility(\n messenger: TransactionPayControllerMessenger,\n sourceChainId: QuoteRequest['sourceChainId'],\n): GasStationEligibility {\n const { relayDisabledGasStationChains } = getFeatureFlags(messenger);\n const supportedChains = getEIP7702SupportedChains(messenger);\n const chainSupportsGasStation = supportedChains.some(\n (supportedChainId) =>\n supportedChainId.toLowerCase() === sourceChainId.toLowerCase(),\n );\n\n const isDisabledChain = relayDisabledGasStationChains.includes(sourceChainId);\n\n return {\n chainSupportsGasStation,\n isDisabledChain,\n isEligible: !isDisabledChain && chainSupportsGasStation,\n };\n}\n\nexport async function getGasStationCostInSourceTokenRaw({\n firstStepData,\n messenger,\n request,\n totalGasEstimate,\n totalItemCount,\n}: GasStationCostParams): Promise<Amount | undefined> {\n const { data, to, value } = firstStepData;\n const { from, sourceChainId, sourceTokenAddress } = request;\n\n let gasFeeTokens: GasFeeToken[];\n\n try {\n gasFeeTokens = await messenger.call(\n 'TransactionController:getGasFeeTokens',\n {\n chainId: sourceChainId,\n data,\n from,\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 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"]}
1
+ {"version":3,"file":"gas-station.cjs","sourceRoot":"","sources":["../../../src/strategy/relay/gas-station.ts"],"names":[],"mappings":"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","sourcesContent":["import { toHex } from '@metamask/controller-utils';\nimport type { GasFeeToken } 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';\nimport type {\n Amount,\n QuoteRequest,\n TransactionPayControllerMessenger,\n} from '../../types';\nimport { getFeatureFlags, isEIP7702Chain } from '../../utils/feature-flags';\nimport { calculateGasFeeTokenCost } from '../../utils/gas';\n\nconst log = createModuleLogger(projectLogger, 'relay-gas-station');\n\ntype GasStationCostParams = {\n firstStepData: {\n data: Hex;\n to: Hex;\n value?: string;\n };\n messenger: TransactionPayControllerMessenger;\n request: Pick<QuoteRequest, 'from' | 'sourceChainId' | 'sourceTokenAddress'>;\n totalGasEstimate: number;\n totalItemCount: number;\n};\n\nexport type GasStationEligibility = {\n chainSupportsGasStation: boolean;\n isDisabledChain: boolean;\n isEligible: boolean;\n};\n\nexport function getGasStationEligibility(\n messenger: TransactionPayControllerMessenger,\n sourceChainId: QuoteRequest['sourceChainId'],\n): GasStationEligibility {\n const { relayDisabledGasStationChains } = getFeatureFlags(messenger);\n const chainSupportsGasStation = isEIP7702Chain(messenger, sourceChainId);\n\n const isDisabledChain = relayDisabledGasStationChains.includes(sourceChainId);\n\n return {\n chainSupportsGasStation,\n isDisabledChain,\n isEligible: !isDisabledChain && chainSupportsGasStation,\n };\n}\n\nexport async function getGasStationCostInSourceTokenRaw({\n firstStepData,\n messenger,\n request,\n totalGasEstimate,\n totalItemCount,\n}: GasStationCostParams): Promise<Amount | undefined> {\n const { data, to, value } = firstStepData;\n const { from, sourceChainId, sourceTokenAddress } = request;\n\n let gasFeeTokens: GasFeeToken[];\n\n try {\n gasFeeTokens = await messenger.call(\n 'TransactionController:getGasFeeTokens',\n {\n chainId: sourceChainId,\n data,\n from,\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 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"]}
@@ -1 +1 @@
1
- {"version":3,"file":"gas-station.d.cts","sourceRoot":"","sources":["../../../src/strategy/relay/gas-station.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAK3C,OAAO,KAAK,EACV,MAAM,EACN,YAAY,EACZ,iCAAiC,EAClC,wBAAoB;AASrB,KAAK,oBAAoB,GAAG;IAC1B,aAAa,EAAE;QACb,IAAI,EAAE,GAAG,CAAC;QACV,EAAE,EAAE,GAAG,CAAC;QACR,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,SAAS,EAAE,iCAAiC,CAAC;IAC7C,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,eAAe,GAAG,oBAAoB,CAAC,CAAC;IAC7E,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,uBAAuB,EAAE,OAAO,CAAC;IACjC,eAAe,EAAE,OAAO,CAAC;IACzB,UAAU,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,iCAAiC,EAC5C,aAAa,EAAE,YAAY,CAAC,eAAe,CAAC,GAC3C,qBAAqB,CAevB;AAED,wBAAsB,iCAAiC,CAAC,EACtD,aAAa,EACb,SAAS,EACT,OAAO,EACP,gBAAgB,EAChB,cAAc,GACf,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAuEpD"}
1
+ {"version":3,"file":"gas-station.d.cts","sourceRoot":"","sources":["../../../src/strategy/relay/gas-station.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAK3C,OAAO,KAAK,EACV,MAAM,EACN,YAAY,EACZ,iCAAiC,EAClC,wBAAoB;AAMrB,KAAK,oBAAoB,GAAG;IAC1B,aAAa,EAAE;QACb,IAAI,EAAE,GAAG,CAAC;QACV,EAAE,EAAE,GAAG,CAAC;QACR,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,SAAS,EAAE,iCAAiC,CAAC;IAC7C,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,eAAe,GAAG,oBAAoB,CAAC,CAAC;IAC7E,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,uBAAuB,EAAE,OAAO,CAAC;IACjC,eAAe,EAAE,OAAO,CAAC;IACzB,UAAU,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,iCAAiC,EAC5C,aAAa,EAAE,YAAY,CAAC,eAAe,CAAC,GAC3C,qBAAqB,CAWvB;AAED,wBAAsB,iCAAiC,CAAC,EACtD,aAAa,EACb,SAAS,EACT,OAAO,EACP,gBAAgB,EAChB,cAAc,GACf,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAuEpD"}
@@ -1 +1 @@
1
- {"version":3,"file":"gas-station.d.mts","sourceRoot":"","sources":["../../../src/strategy/relay/gas-station.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAK3C,OAAO,KAAK,EACV,MAAM,EACN,YAAY,EACZ,iCAAiC,EAClC,wBAAoB;AASrB,KAAK,oBAAoB,GAAG;IAC1B,aAAa,EAAE;QACb,IAAI,EAAE,GAAG,CAAC;QACV,EAAE,EAAE,GAAG,CAAC;QACR,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,SAAS,EAAE,iCAAiC,CAAC;IAC7C,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,eAAe,GAAG,oBAAoB,CAAC,CAAC;IAC7E,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,uBAAuB,EAAE,OAAO,CAAC;IACjC,eAAe,EAAE,OAAO,CAAC;IACzB,UAAU,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,iCAAiC,EAC5C,aAAa,EAAE,YAAY,CAAC,eAAe,CAAC,GAC3C,qBAAqB,CAevB;AAED,wBAAsB,iCAAiC,CAAC,EACtD,aAAa,EACb,SAAS,EACT,OAAO,EACP,gBAAgB,EAChB,cAAc,GACf,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAuEpD"}
1
+ {"version":3,"file":"gas-station.d.mts","sourceRoot":"","sources":["../../../src/strategy/relay/gas-station.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAE,wBAAwB;AAK3C,OAAO,KAAK,EACV,MAAM,EACN,YAAY,EACZ,iCAAiC,EAClC,wBAAoB;AAMrB,KAAK,oBAAoB,GAAG;IAC1B,aAAa,EAAE;QACb,IAAI,EAAE,GAAG,CAAC;QACV,EAAE,EAAE,GAAG,CAAC;QACR,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,SAAS,EAAE,iCAAiC,CAAC;IAC7C,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,eAAe,GAAG,oBAAoB,CAAC,CAAC;IAC7E,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,uBAAuB,EAAE,OAAO,CAAC;IACjC,eAAe,EAAE,OAAO,CAAC;IACzB,UAAU,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,iCAAiC,EAC5C,aAAa,EAAE,YAAY,CAAC,eAAe,CAAC,GAC3C,qBAAqB,CAWvB;AAED,wBAAsB,iCAAiC,CAAC,EACtD,aAAa,EACb,SAAS,EACT,OAAO,EACP,gBAAgB,EAChB,cAAc,GACf,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAuEpD"}
@@ -2,13 +2,12 @@ import { toHex } from "@metamask/controller-utils";
2
2
  import { createModuleLogger } from "@metamask/utils";
3
3
  import { BigNumber } from "bignumber.js";
4
4
  import { projectLogger } from "../../logger.mjs";
5
- import { getEIP7702SupportedChains, getFeatureFlags } from "../../utils/feature-flags.mjs";
5
+ import { getFeatureFlags, isEIP7702Chain } from "../../utils/feature-flags.mjs";
6
6
  import { calculateGasFeeTokenCost } from "../../utils/gas.mjs";
7
7
  const log = createModuleLogger(projectLogger, 'relay-gas-station');
8
8
  export function getGasStationEligibility(messenger, sourceChainId) {
9
9
  const { relayDisabledGasStationChains } = getFeatureFlags(messenger);
10
- const supportedChains = getEIP7702SupportedChains(messenger);
11
- const chainSupportsGasStation = supportedChains.some((supportedChainId) => supportedChainId.toLowerCase() === sourceChainId.toLowerCase());
10
+ const chainSupportsGasStation = isEIP7702Chain(messenger, sourceChainId);
12
11
  const isDisabledChain = relayDisabledGasStationChains.includes(sourceChainId);
13
12
  return {
14
13
  chainSupportsGasStation,
@@ -1 +1 @@
1
- {"version":3,"file":"gas-station.mjs","sourceRoot":"","sources":["../../../src/strategy/relay/gas-station.ts"],"names":[],"mappings":"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","sourcesContent":["import { toHex } from '@metamask/controller-utils';\nimport type { GasFeeToken } 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';\nimport type {\n Amount,\n QuoteRequest,\n TransactionPayControllerMessenger,\n} from '../../types';\nimport {\n getEIP7702SupportedChains,\n getFeatureFlags,\n} from '../../utils/feature-flags';\nimport { calculateGasFeeTokenCost } from '../../utils/gas';\n\nconst log = createModuleLogger(projectLogger, 'relay-gas-station');\n\ntype GasStationCostParams = {\n firstStepData: {\n data: Hex;\n to: Hex;\n value?: string;\n };\n messenger: TransactionPayControllerMessenger;\n request: Pick<QuoteRequest, 'from' | 'sourceChainId' | 'sourceTokenAddress'>;\n totalGasEstimate: number;\n totalItemCount: number;\n};\n\nexport type GasStationEligibility = {\n chainSupportsGasStation: boolean;\n isDisabledChain: boolean;\n isEligible: boolean;\n};\n\nexport function getGasStationEligibility(\n messenger: TransactionPayControllerMessenger,\n sourceChainId: QuoteRequest['sourceChainId'],\n): GasStationEligibility {\n const { relayDisabledGasStationChains } = getFeatureFlags(messenger);\n const supportedChains = getEIP7702SupportedChains(messenger);\n const chainSupportsGasStation = supportedChains.some(\n (supportedChainId) =>\n supportedChainId.toLowerCase() === sourceChainId.toLowerCase(),\n );\n\n const isDisabledChain = relayDisabledGasStationChains.includes(sourceChainId);\n\n return {\n chainSupportsGasStation,\n isDisabledChain,\n isEligible: !isDisabledChain && chainSupportsGasStation,\n };\n}\n\nexport async function getGasStationCostInSourceTokenRaw({\n firstStepData,\n messenger,\n request,\n totalGasEstimate,\n totalItemCount,\n}: GasStationCostParams): Promise<Amount | undefined> {\n const { data, to, value } = firstStepData;\n const { from, sourceChainId, sourceTokenAddress } = request;\n\n let gasFeeTokens: GasFeeToken[];\n\n try {\n gasFeeTokens = await messenger.call(\n 'TransactionController:getGasFeeTokens',\n {\n chainId: sourceChainId,\n data,\n from,\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 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"]}
1
+ {"version":3,"file":"gas-station.mjs","sourceRoot":"","sources":["../../../src/strategy/relay/gas-station.ts"],"names":[],"mappings":"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","sourcesContent":["import { toHex } from '@metamask/controller-utils';\nimport type { GasFeeToken } 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';\nimport type {\n Amount,\n QuoteRequest,\n TransactionPayControllerMessenger,\n} from '../../types';\nimport { getFeatureFlags, isEIP7702Chain } from '../../utils/feature-flags';\nimport { calculateGasFeeTokenCost } from '../../utils/gas';\n\nconst log = createModuleLogger(projectLogger, 'relay-gas-station');\n\ntype GasStationCostParams = {\n firstStepData: {\n data: Hex;\n to: Hex;\n value?: string;\n };\n messenger: TransactionPayControllerMessenger;\n request: Pick<QuoteRequest, 'from' | 'sourceChainId' | 'sourceTokenAddress'>;\n totalGasEstimate: number;\n totalItemCount: number;\n};\n\nexport type GasStationEligibility = {\n chainSupportsGasStation: boolean;\n isDisabledChain: boolean;\n isEligible: boolean;\n};\n\nexport function getGasStationEligibility(\n messenger: TransactionPayControllerMessenger,\n sourceChainId: QuoteRequest['sourceChainId'],\n): GasStationEligibility {\n const { relayDisabledGasStationChains } = getFeatureFlags(messenger);\n const chainSupportsGasStation = isEIP7702Chain(messenger, sourceChainId);\n\n const isDisabledChain = relayDisabledGasStationChains.includes(sourceChainId);\n\n return {\n chainSupportsGasStation,\n isDisabledChain,\n isEligible: !isDisabledChain && chainSupportsGasStation,\n };\n}\n\nexport async function getGasStationCostInSourceTokenRaw({\n firstStepData,\n messenger,\n request,\n totalGasEstimate,\n totalItemCount,\n}: GasStationCostParams): Promise<Amount | undefined> {\n const { data, to, value } = firstStepData;\n const { from, sourceChainId, sourceTokenAddress } = request;\n\n let gasFeeTokens: GasFeeToken[];\n\n try {\n gasFeeTokens = await messenger.call(\n 'TransactionController:getGasFeeTokens',\n {\n chainId: sourceChainId,\n data,\n from,\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 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"]}
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getRelayStatus = exports.submitRelayExecute = exports.fetchRelayQuote = void 0;
4
+ const controller_utils_1 = require("@metamask/controller-utils");
5
+ const constants_1 = require("./constants.cjs");
6
+ const feature_flags_1 = require("../../utils/feature-flags.cjs");
7
+ /**
8
+ * Fetch a quote from the Relay API.
9
+ *
10
+ * @param messenger - Controller messenger.
11
+ * @param body - Quote request parameters.
12
+ * @returns The Relay quote with the request attached.
13
+ */
14
+ async function fetchRelayQuote(messenger, body) {
15
+ const { relayQuoteUrl } = (0, feature_flags_1.getFeatureFlags)(messenger);
16
+ const response = await (0, controller_utils_1.successfulFetch)(relayQuoteUrl, {
17
+ method: 'POST',
18
+ headers: { 'Content-Type': 'application/json' },
19
+ body: JSON.stringify(body),
20
+ });
21
+ const quote = (await response.json());
22
+ quote.request = body;
23
+ return quote;
24
+ }
25
+ exports.fetchRelayQuote = fetchRelayQuote;
26
+ /**
27
+ * Submit a gasless transaction via the Relay /execute endpoint.
28
+ *
29
+ * @param messenger - Controller messenger.
30
+ * @param body - Execute request parameters.
31
+ * @returns The execute response containing the request ID.
32
+ */
33
+ async function submitRelayExecute(messenger, body) {
34
+ const { relayExecuteUrl } = (0, feature_flags_1.getFeatureFlags)(messenger);
35
+ const response = await (0, controller_utils_1.successfulFetch)(relayExecuteUrl, {
36
+ method: 'POST',
37
+ headers: { 'Content-Type': 'application/json' },
38
+ body: JSON.stringify(body),
39
+ });
40
+ return (await response.json());
41
+ }
42
+ exports.submitRelayExecute = submitRelayExecute;
43
+ /**
44
+ * Poll the Relay status endpoint for a given request ID.
45
+ *
46
+ * @param requestId - The Relay request ID to check.
47
+ * @returns The current status of the request.
48
+ */
49
+ async function getRelayStatus(requestId) {
50
+ const url = `${constants_1.RELAY_STATUS_URL}?requestId=${requestId}`;
51
+ const response = await (0, controller_utils_1.successfulFetch)(url, { method: 'GET' });
52
+ return (await response.json());
53
+ }
54
+ exports.getRelayStatus = getRelayStatus;
55
+ //# sourceMappingURL=relay-api.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay-api.cjs","sourceRoot":"","sources":["../../../src/strategy/relay/relay-api.ts"],"names":[],"mappings":";;;AAAA,iEAA6D;AAE7D,+CAA+C;AAS/C,iEAA4D;AAE5D;;;;;;GAMG;AACI,KAAK,UAAU,eAAe,CACnC,SAA4C,EAC5C,IAAuB;IAEvB,MAAM,EAAE,aAAa,EAAE,GAAG,IAAA,+BAAe,EAAC,SAAS,CAAC,CAAC;IAErD,MAAM,QAAQ,GAAG,MAAM,IAAA,kCAAe,EAAC,aAAa,EAAE;QACpD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;KAC3B,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAe,CAAC;IACpD,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;IAErB,OAAO,KAAK,CAAC;AACf,CAAC;AAhBD,0CAgBC;AAED;;;;;;GAMG;AACI,KAAK,UAAU,kBAAkB,CACtC,SAA4C,EAC5C,IAAyB;IAEzB,MAAM,EAAE,eAAe,EAAE,GAAG,IAAA,+BAAe,EAAC,SAAS,CAAC,CAAC;IAEvD,MAAM,QAAQ,GAAG,MAAM,IAAA,kCAAe,EAAC,eAAe,EAAE;QACtD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;KAC3B,CAAC,CAAC;IAEH,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAyB,CAAC;AACzD,CAAC;AAbD,gDAaC;AAED;;;;;GAKG;AACI,KAAK,UAAU,cAAc,CAClC,SAAiB;IAEjB,MAAM,GAAG,GAAG,GAAG,4BAAgB,cAAc,SAAS,EAAE,CAAC;IAEzD,MAAM,QAAQ,GAAG,MAAM,IAAA,kCAAe,EAAC,GAAG,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IAE/D,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAwB,CAAC;AACxD,CAAC;AARD,wCAQC","sourcesContent":["import { successfulFetch } from '@metamask/controller-utils';\n\nimport { RELAY_STATUS_URL } from './constants';\nimport type {\n RelayExecuteRequest,\n RelayExecuteResponse,\n RelayQuote,\n RelayQuoteRequest,\n RelayStatusResponse,\n} from './types';\nimport type { TransactionPayControllerMessenger } from '../../types';\nimport { getFeatureFlags } from '../../utils/feature-flags';\n\n/**\n * Fetch a quote from the Relay API.\n *\n * @param messenger - Controller messenger.\n * @param body - Quote request parameters.\n * @returns The Relay quote with the request attached.\n */\nexport async function fetchRelayQuote(\n messenger: TransactionPayControllerMessenger,\n body: RelayQuoteRequest,\n): Promise<RelayQuote> {\n const { relayQuoteUrl } = getFeatureFlags(messenger);\n\n const response = await successfulFetch(relayQuoteUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n\n const quote = (await response.json()) as RelayQuote;\n quote.request = body;\n\n return quote;\n}\n\n/**\n * Submit a gasless transaction via the Relay /execute endpoint.\n *\n * @param messenger - Controller messenger.\n * @param body - Execute request parameters.\n * @returns The execute response containing the request ID.\n */\nexport async function submitRelayExecute(\n messenger: TransactionPayControllerMessenger,\n body: RelayExecuteRequest,\n): Promise<RelayExecuteResponse> {\n const { relayExecuteUrl } = getFeatureFlags(messenger);\n\n const response = await successfulFetch(relayExecuteUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n\n return (await response.json()) as RelayExecuteResponse;\n}\n\n/**\n * Poll the Relay status endpoint for a given request ID.\n *\n * @param requestId - The Relay request ID to check.\n * @returns The current status of the request.\n */\nexport async function getRelayStatus(\n requestId: string,\n): Promise<RelayStatusResponse> {\n const url = `${RELAY_STATUS_URL}?requestId=${requestId}`;\n\n const response = await successfulFetch(url, { method: 'GET' });\n\n return (await response.json()) as RelayStatusResponse;\n}\n"]}
@@ -0,0 +1,26 @@
1
+ import type { RelayExecuteRequest, RelayExecuteResponse, RelayQuote, RelayQuoteRequest, RelayStatusResponse } from "./types.cjs";
2
+ import type { TransactionPayControllerMessenger } from "../../types.cjs";
3
+ /**
4
+ * Fetch a quote from the Relay API.
5
+ *
6
+ * @param messenger - Controller messenger.
7
+ * @param body - Quote request parameters.
8
+ * @returns The Relay quote with the request attached.
9
+ */
10
+ export declare function fetchRelayQuote(messenger: TransactionPayControllerMessenger, body: RelayQuoteRequest): Promise<RelayQuote>;
11
+ /**
12
+ * Submit a gasless transaction via the Relay /execute endpoint.
13
+ *
14
+ * @param messenger - Controller messenger.
15
+ * @param body - Execute request parameters.
16
+ * @returns The execute response containing the request ID.
17
+ */
18
+ export declare function submitRelayExecute(messenger: TransactionPayControllerMessenger, body: RelayExecuteRequest): Promise<RelayExecuteResponse>;
19
+ /**
20
+ * Poll the Relay status endpoint for a given request ID.
21
+ *
22
+ * @param requestId - The Relay request ID to check.
23
+ * @returns The current status of the request.
24
+ */
25
+ export declare function getRelayStatus(requestId: string): Promise<RelayStatusResponse>;
26
+ //# sourceMappingURL=relay-api.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay-api.d.cts","sourceRoot":"","sources":["../../../src/strategy/relay/relay-api.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,EACpB,UAAU,EACV,iBAAiB,EACjB,mBAAmB,EACpB,oBAAgB;AACjB,OAAO,KAAK,EAAE,iCAAiC,EAAE,wBAAoB;AAGrE;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,iCAAiC,EAC5C,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,UAAU,CAAC,CAarB;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,iCAAiC,EAC5C,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,oBAAoB,CAAC,CAU/B;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,mBAAmB,CAAC,CAM9B"}
@@ -0,0 +1,26 @@
1
+ import type { RelayExecuteRequest, RelayExecuteResponse, RelayQuote, RelayQuoteRequest, RelayStatusResponse } from "./types.mjs";
2
+ import type { TransactionPayControllerMessenger } from "../../types.mjs";
3
+ /**
4
+ * Fetch a quote from the Relay API.
5
+ *
6
+ * @param messenger - Controller messenger.
7
+ * @param body - Quote request parameters.
8
+ * @returns The Relay quote with the request attached.
9
+ */
10
+ export declare function fetchRelayQuote(messenger: TransactionPayControllerMessenger, body: RelayQuoteRequest): Promise<RelayQuote>;
11
+ /**
12
+ * Submit a gasless transaction via the Relay /execute endpoint.
13
+ *
14
+ * @param messenger - Controller messenger.
15
+ * @param body - Execute request parameters.
16
+ * @returns The execute response containing the request ID.
17
+ */
18
+ export declare function submitRelayExecute(messenger: TransactionPayControllerMessenger, body: RelayExecuteRequest): Promise<RelayExecuteResponse>;
19
+ /**
20
+ * Poll the Relay status endpoint for a given request ID.
21
+ *
22
+ * @param requestId - The Relay request ID to check.
23
+ * @returns The current status of the request.
24
+ */
25
+ export declare function getRelayStatus(requestId: string): Promise<RelayStatusResponse>;
26
+ //# sourceMappingURL=relay-api.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay-api.d.mts","sourceRoot":"","sources":["../../../src/strategy/relay/relay-api.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,EACpB,UAAU,EACV,iBAAiB,EACjB,mBAAmB,EACpB,oBAAgB;AACjB,OAAO,KAAK,EAAE,iCAAiC,EAAE,wBAAoB;AAGrE;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,iCAAiC,EAC5C,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,UAAU,CAAC,CAarB;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,iCAAiC,EAC5C,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,oBAAoB,CAAC,CAU/B;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,mBAAmB,CAAC,CAM9B"}
@@ -0,0 +1,49 @@
1
+ import { successfulFetch } from "@metamask/controller-utils";
2
+ import { RELAY_STATUS_URL } from "./constants.mjs";
3
+ import { getFeatureFlags } from "../../utils/feature-flags.mjs";
4
+ /**
5
+ * Fetch a quote from the Relay API.
6
+ *
7
+ * @param messenger - Controller messenger.
8
+ * @param body - Quote request parameters.
9
+ * @returns The Relay quote with the request attached.
10
+ */
11
+ export async function fetchRelayQuote(messenger, body) {
12
+ const { relayQuoteUrl } = getFeatureFlags(messenger);
13
+ const response = await successfulFetch(relayQuoteUrl, {
14
+ method: 'POST',
15
+ headers: { 'Content-Type': 'application/json' },
16
+ body: JSON.stringify(body),
17
+ });
18
+ const quote = (await response.json());
19
+ quote.request = body;
20
+ return quote;
21
+ }
22
+ /**
23
+ * Submit a gasless transaction via the Relay /execute endpoint.
24
+ *
25
+ * @param messenger - Controller messenger.
26
+ * @param body - Execute request parameters.
27
+ * @returns The execute response containing the request ID.
28
+ */
29
+ export async function submitRelayExecute(messenger, body) {
30
+ const { relayExecuteUrl } = getFeatureFlags(messenger);
31
+ const response = await successfulFetch(relayExecuteUrl, {
32
+ method: 'POST',
33
+ headers: { 'Content-Type': 'application/json' },
34
+ body: JSON.stringify(body),
35
+ });
36
+ return (await response.json());
37
+ }
38
+ /**
39
+ * Poll the Relay status endpoint for a given request ID.
40
+ *
41
+ * @param requestId - The Relay request ID to check.
42
+ * @returns The current status of the request.
43
+ */
44
+ export async function getRelayStatus(requestId) {
45
+ const url = `${RELAY_STATUS_URL}?requestId=${requestId}`;
46
+ const response = await successfulFetch(url, { method: 'GET' });
47
+ return (await response.json());
48
+ }
49
+ //# sourceMappingURL=relay-api.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay-api.mjs","sourceRoot":"","sources":["../../../src/strategy/relay/relay-api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,mCAAmC;AAE7D,OAAO,EAAE,gBAAgB,EAAE,wBAAoB;AAS/C,OAAO,EAAE,eAAe,EAAE,sCAAkC;AAE5D;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,SAA4C,EAC5C,IAAuB;IAEvB,MAAM,EAAE,aAAa,EAAE,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC;IAErD,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,aAAa,EAAE;QACpD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;KAC3B,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAe,CAAC;IACpD,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;IAErB,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,SAA4C,EAC5C,IAAyB;IAEzB,MAAM,EAAE,eAAe,EAAE,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC;IAEvD,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,eAAe,EAAE;QACtD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;KAC3B,CAAC,CAAC;IAEH,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAyB,CAAC;AACzD,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,SAAiB;IAEjB,MAAM,GAAG,GAAG,GAAG,gBAAgB,cAAc,SAAS,EAAE,CAAC;IAEzD,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IAE/D,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAwB,CAAC;AACxD,CAAC","sourcesContent":["import { successfulFetch } from '@metamask/controller-utils';\n\nimport { RELAY_STATUS_URL } from './constants';\nimport type {\n RelayExecuteRequest,\n RelayExecuteResponse,\n RelayQuote,\n RelayQuoteRequest,\n RelayStatusResponse,\n} from './types';\nimport type { TransactionPayControllerMessenger } from '../../types';\nimport { getFeatureFlags } from '../../utils/feature-flags';\n\n/**\n * Fetch a quote from the Relay API.\n *\n * @param messenger - Controller messenger.\n * @param body - Quote request parameters.\n * @returns The Relay quote with the request attached.\n */\nexport async function fetchRelayQuote(\n messenger: TransactionPayControllerMessenger,\n body: RelayQuoteRequest,\n): Promise<RelayQuote> {\n const { relayQuoteUrl } = getFeatureFlags(messenger);\n\n const response = await successfulFetch(relayQuoteUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n\n const quote = (await response.json()) as RelayQuote;\n quote.request = body;\n\n return quote;\n}\n\n/**\n * Submit a gasless transaction via the Relay /execute endpoint.\n *\n * @param messenger - Controller messenger.\n * @param body - Execute request parameters.\n * @returns The execute response containing the request ID.\n */\nexport async function submitRelayExecute(\n messenger: TransactionPayControllerMessenger,\n body: RelayExecuteRequest,\n): Promise<RelayExecuteResponse> {\n const { relayExecuteUrl } = getFeatureFlags(messenger);\n\n const response = await successfulFetch(relayExecuteUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n });\n\n return (await response.json()) as RelayExecuteResponse;\n}\n\n/**\n * Poll the Relay status endpoint for a given request ID.\n *\n * @param requestId - The Relay request ID to check.\n * @returns The current status of the request.\n */\nexport async function getRelayStatus(\n requestId: string,\n): Promise<RelayStatusResponse> {\n const url = `${RELAY_STATUS_URL}?requestId=${requestId}`;\n\n const response = await successfulFetch(url, { method: 'GET' });\n\n return (await response.json()) as RelayStatusResponse;\n}\n"]}
@@ -8,6 +8,7 @@ const utils_1 = require("@metamask/utils");
8
8
  const bignumber_js_1 = require("bignumber.js");
9
9
  const constants_1 = require("./constants.cjs");
10
10
  const gas_station_1 = require("./gas-station.cjs");
11
+ const relay_api_1 = require("./relay-api.cjs");
11
12
  const relay_max_gas_station_1 = require("./relay-max-gas-station.cjs");
12
13
  const __1 = require("../../index.cjs");
13
14
  const constants_2 = require("../../constants.cjs");
@@ -16,6 +17,7 @@ const amounts_1 = require("../../utils/amounts.cjs");
16
17
  const feature_flags_1 = require("../../utils/feature-flags.cjs");
17
18
  const gas_1 = require("../../utils/gas.cjs");
18
19
  const token_1 = require("../../utils/token.cjs");
20
+ const transaction_1 = require("../../utils/transaction.cjs");
19
21
  const log = (0, utils_1.createModuleLogger)(logger_1.projectLogger, 'relay-strategy');
20
22
  /**
21
23
  * Fetches Relay quotes.
@@ -45,10 +47,56 @@ exports.getRelayQuotes = getRelayQuotes;
45
47
  async function getQuoteWithMaxAmountHandling(request, fullRequest) {
46
48
  const { isMaxAmount } = request;
47
49
  if (!isMaxAmount) {
48
- return getSingleQuote(request, fullRequest);
50
+ return getQuoteWithPostQuoteGasHandling(request, fullRequest);
49
51
  }
50
52
  return (0, relay_max_gas_station_1.getRelayMaxGasStationQuote)(request, fullRequest, getSingleQuote);
51
53
  }
54
+ /**
55
+ * For post-quote flows, fetch an initial quote to compute gas cost in source
56
+ * token, then re-quote with the source amount reduced by the gas cost.
57
+ * This ensures Relay reserves enough for the gas fee token payment.
58
+ *
59
+ * For non-post-quote flows, just returns a single quote.
60
+ *
61
+ * @param request - Quote request.
62
+ * @param fullRequest - Full request context.
63
+ * @returns The final quote (phase 2 for post-quote, or phase 1 for normal).
64
+ */
65
+ async function getQuoteWithPostQuoteGasHandling(request, fullRequest) {
66
+ const phase1Quote = await getSingleQuote(request, fullRequest);
67
+ if (!request.isPostQuote || !phase1Quote.fees.isSourceGasFeeToken) {
68
+ return phase1Quote;
69
+ }
70
+ const gasCostRaw = phase1Quote.fees.sourceNetwork.max.raw;
71
+ const adjustedSourceAmount = new bignumber_js_1.BigNumber(request.sourceTokenAmount)
72
+ .minus(gasCostRaw)
73
+ .integerValue(bignumber_js_1.BigNumber.ROUND_DOWN);
74
+ log('Subtracting gas from source for post-quote two-call', {
75
+ originalSourceAmount: request.sourceTokenAmount,
76
+ gasCostRaw,
77
+ adjustedSourceAmount: adjustedSourceAmount.toString(10),
78
+ });
79
+ if (!adjustedSourceAmount.isGreaterThan(0)) {
80
+ log('Insufficient balance after gas subtraction for post-quote, using phase 1');
81
+ return phase1Quote;
82
+ }
83
+ try {
84
+ const phase2Quote = await getSingleQuote({
85
+ ...request,
86
+ sourceTokenAmount: adjustedSourceAmount.toFixed(0, bignumber_js_1.BigNumber.ROUND_DOWN),
87
+ }, fullRequest);
88
+ if (phase1Quote.fees.isSourceGasFeeToken &&
89
+ !phase2Quote.fees.isSourceGasFeeToken) {
90
+ log('Phase 2 lost gas fee token eligibility, falling back to phase 1');
91
+ return phase1Quote;
92
+ }
93
+ return phase2Quote;
94
+ }
95
+ catch (error) {
96
+ log('Phase 2 quote failed, falling back to phase 1', { error });
97
+ return phase1Quote;
98
+ }
99
+ }
52
100
  /**
53
101
  * Fetches a single Relay quote.
54
102
  *
@@ -67,12 +115,17 @@ async function getSingleQuote(request, fullRequest) {
67
115
  // For regular flows with a target amount, use EXPECTED_OUTPUT.
68
116
  // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
69
117
  const useExactInput = isMaxAmount || request.isPostQuote;
118
+ const useExecute = (0, feature_flags_1.isRelayExecuteEnabled)(messenger) &&
119
+ (0, feature_flags_1.isEIP7702Chain)(messenger, sourceChainId);
70
120
  const body = {
71
121
  amount: useExactInput ? sourceTokenAmount : targetAmountMinimum,
72
122
  destinationChainId: Number(targetChainId),
73
123
  destinationCurrency: targetTokenAddress,
74
124
  originChainId: Number(sourceChainId),
75
125
  originCurrency: sourceTokenAddress,
126
+ ...(useExecute
127
+ ? { originGasOverhead: (0, feature_flags_1.getRelayOriginGasOverhead)(messenger) }
128
+ : {}),
76
129
  recipient: from,
77
130
  slippageTolerance,
78
131
  tradeType: useExactInput ? 'EXACT_INPUT' : 'EXPECTED_OUTPUT',
@@ -89,15 +142,8 @@ async function getSingleQuote(request, fullRequest) {
89
142
  // Safe proxy) rather than defaulting to the EOA.
90
143
  body.refundTo = request.refundTo;
91
144
  }
92
- const url = (0, feature_flags_1.getFeatureFlags)(messenger).relayQuoteUrl;
93
- log('Request body', { body, url });
94
- const response = await (0, controller_utils_1.successfulFetch)(url, {
95
- method: 'POST',
96
- headers: { 'Content-Type': 'application/json' },
97
- body: JSON.stringify(body),
98
- });
99
- const quote = (await response.json());
100
- quote.request = body;
145
+ log('Request body', body);
146
+ const quote = await (0, relay_api_1.fetchRelayQuote)(messenger, body);
101
147
  log('Fetched relay quote', quote);
102
148
  return await normalizeQuote(quote, request, fullRequest);
103
149
  }
@@ -317,7 +363,12 @@ async function calculateSourceNetworkCost(quote, messenger, request, transaction
317
363
  .flatMap((step) => step.items)
318
364
  .map((item) => item.data);
319
365
  const { chainId, data, maxFeePerGas, maxPriorityFeePerGas, to, value } = relayParams[0];
320
- const { totalGasEstimate, totalGasLimit, gasLimits } = await calculateSourceNetworkGasLimit(relayParams, messenger, request.isPostQuote ? transaction : undefined);
366
+ const isPredictWithdraw = request.isPostQuote && (0, transaction_1.isPredictWithdrawTransaction)(transaction);
367
+ const fromOverride = isPredictWithdraw ? request.refundTo : undefined;
368
+ const relayOnlyGas = await calculateSourceNetworkGasLimit(relayParams, messenger, fromOverride);
369
+ const { totalGasEstimate, totalGasLimit, gasLimits } = request.isPostQuote
370
+ ? combinePostQuoteGas(relayOnlyGas, transaction)
371
+ : relayOnlyGas;
321
372
  log('Gas limit', {
322
373
  totalGasEstimate,
323
374
  totalGasLimit,
@@ -360,6 +411,40 @@ async function calculateSourceNetworkCost(quote, messenger, request, transaction
360
411
  nativeBalance,
361
412
  max: max.raw,
362
413
  });
414
+ if (isPredictWithdraw && request.refundTo) {
415
+ log('Using proxy address for predict withdraw gas station simulation', {
416
+ proxyAddress: request.refundTo,
417
+ sourceTokenAddress,
418
+ totalGasEstimate,
419
+ });
420
+ const gasFeeTokenCost = await (0, gas_station_1.getGasStationCostInSourceTokenRaw)({
421
+ firstStepData: {
422
+ data,
423
+ to,
424
+ value,
425
+ },
426
+ messenger,
427
+ request: {
428
+ from: request.refundTo,
429
+ sourceChainId,
430
+ sourceTokenAddress,
431
+ },
432
+ totalGasEstimate,
433
+ totalItemCount: relayParams.length + 1,
434
+ });
435
+ if (gasFeeTokenCost) {
436
+ log('Using predict withdraw gas fee token for source network', {
437
+ gasFeeTokenCost,
438
+ });
439
+ return {
440
+ isGasFeeToken: true,
441
+ estimate: gasFeeTokenCost,
442
+ max: gasFeeTokenCost,
443
+ gasLimits,
444
+ };
445
+ }
446
+ return result;
447
+ }
363
448
  const gasFeeTokenCost = await (0, gas_station_1.getGasStationCostInSourceTokenRaw)({
364
449
  firstStepData: {
365
450
  data,
@@ -391,24 +476,17 @@ async function calculateSourceNetworkCost(quote, messenger, request, transaction
391
476
  /**
392
477
  * Calculate the total gas limit for the source network.
393
478
  *
394
- * For post-quote flows (e.g. predict withdrawals), the original transaction's
395
- * gas is combined with the relay gas so that source network cost accounts for
396
- * both the user's transaction and the relay transactions.
397
- *
398
479
  * @param params - Array of relay transaction parameters.
399
480
  * @param messenger - Controller messenger.
400
- * @param postQuoteTransaction - Original transaction for post-quote flows.
401
- * When provided, its gas is included in the returned totals.
481
+ * @param fromOverride - Optional address to use as `from` in gas estimation
482
+ * instead of the address in the relay params. Used in predict withdraw flows
483
+ * to estimate with the proxy/Safe address that holds the source token balance.
402
484
  * @returns Total gas estimates and per-transaction gas limits.
403
485
  */
404
- async function calculateSourceNetworkGasLimit(params, messenger, postQuoteTransaction) {
405
- const relayGas = params.length === 1
406
- ? await calculateSourceNetworkGasLimitSingle(params[0], messenger)
407
- : await calculateSourceNetworkGasLimitBatch(params, messenger);
408
- if (!postQuoteTransaction?.txParams.to) {
409
- return relayGas;
410
- }
411
- return combinePostQuoteGas(relayGas, postQuoteTransaction);
486
+ async function calculateSourceNetworkGasLimit(params, messenger, fromOverride) {
487
+ return params.length === 1
488
+ ? calculateSourceNetworkGasLimitSingle(params[0], messenger, fromOverride)
489
+ : calculateSourceNetworkGasLimitBatch(params, messenger, fromOverride);
412
490
  }
413
491
  /**
414
492
  * Combine the original transaction's gas with relay gas for post-quote flows.
@@ -486,11 +564,11 @@ function getTransferRecipient(data) {
486
564
  .decodeFunctionData('transfer', data)
487
565
  .to.toLowerCase();
488
566
  }
489
- async function calculateSourceNetworkGasLimitSingle(params, messenger) {
567
+ async function calculateSourceNetworkGasLimitSingle(params, messenger, fromOverride) {
490
568
  const paramGasLimit = params.gas
491
569
  ? new bignumber_js_1.BigNumber(params.gas).toNumber()
492
570
  : undefined;
493
- if (paramGasLimit) {
571
+ if (paramGasLimit && !fromOverride) {
494
572
  log('Using single gas limit from params', { paramGasLimit });
495
573
  return {
496
574
  totalGasEstimate: paramGasLimit,
@@ -499,7 +577,8 @@ async function calculateSourceNetworkGasLimitSingle(params, messenger) {
499
577
  };
500
578
  }
501
579
  try {
502
- const { chainId: chainIdNumber, data, from, to, value: valueString, } = params;
580
+ const { chainId: chainIdNumber, data, from: paramsFrom, to, value: valueString, } = params;
581
+ const from = fromOverride ?? paramsFrom;
503
582
  const chainId = (0, controller_utils_1.toHex)(chainIdNumber);
504
583
  const value = (0, controller_utils_1.toHex)(valueString ?? '0');
505
584
  const gasBuffer = (0, feature_flags_1.getGasBuffer)(messenger, chainId);
@@ -537,16 +616,20 @@ async function calculateSourceNetworkGasLimitSingle(params, messenger) {
537
616
  *
538
617
  * @param params - Array of transaction parameters.
539
618
  * @param messenger - Controller messenger.
619
+ * @param fromOverride - Optional address to use as `from` in gas estimation.
540
620
  * @returns - Gas limits.
541
621
  */
542
- async function calculateSourceNetworkGasLimitBatch(params, messenger) {
622
+ async function calculateSourceNetworkGasLimitBatch(params, messenger, fromOverride) {
543
623
  try {
544
- const { chainId: chainIdNumber, from } = params[0];
624
+ const { chainId: chainIdNumber, from: paramsFrom } = params[0];
625
+ const from = fromOverride ?? paramsFrom;
545
626
  const chainId = (0, controller_utils_1.toHex)(chainIdNumber);
546
627
  const gasBuffer = (0, feature_flags_1.getGasBuffer)(messenger, chainId);
547
628
  const transactions = params.map((singleParams) => ({
548
629
  ...singleParams,
549
- gas: singleParams.gas ? (0, controller_utils_1.toHex)(singleParams.gas) : undefined,
630
+ gas: !fromOverride && singleParams.gas
631
+ ? (0, controller_utils_1.toHex)(singleParams.gas)
632
+ : undefined,
550
633
  maxFeePerGas: undefined,
551
634
  maxPriorityFeePerGas: undefined,
552
635
  value: (0, controller_utils_1.toHex)(singleParams.value ?? '0'),