@metamask/transaction-controller 24.0.0 → 25.0.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 (278) hide show
  1. package/CHANGELOG.md +118 -1
  2. package/dist/TransactionController.js +38 -1816
  3. package/dist/TransactionController.js.map +1 -1
  4. package/dist/TransactionController.mjs +39 -0
  5. package/dist/TransactionController.mjs.map +1 -0
  6. package/dist/chunk-2K7J3EY3.mjs +170 -0
  7. package/dist/chunk-2K7J3EY3.mjs.map +1 -0
  8. package/dist/chunk-5XBULBP2.js +399 -0
  9. package/dist/chunk-5XBULBP2.js.map +1 -0
  10. package/dist/chunk-6MYNWYJK.mjs +158 -0
  11. package/dist/chunk-6MYNWYJK.mjs.map +1 -0
  12. package/dist/chunk-7APMBUKB.js +158 -0
  13. package/dist/chunk-7APMBUKB.js.map +1 -0
  14. package/dist/chunk-7LXE4KHV.js +40 -0
  15. package/dist/chunk-7LXE4KHV.js.map +1 -0
  16. package/dist/chunk-7MZ57ILQ.mjs +62 -0
  17. package/dist/chunk-7MZ57ILQ.mjs.map +1 -0
  18. package/dist/chunk-BJEESIBE.js +2313 -0
  19. package/dist/chunk-BJEESIBE.js.map +1 -0
  20. package/dist/chunk-C67QD5PV.mjs +320 -0
  21. package/dist/chunk-C67QD5PV.mjs.map +1 -0
  22. package/dist/chunk-DE3MZYVY.mjs +2313 -0
  23. package/dist/chunk-DE3MZYVY.mjs.map +1 -0
  24. package/dist/chunk-DEKM6PVG.mjs +46 -0
  25. package/dist/chunk-DEKM6PVG.mjs.map +1 -0
  26. package/dist/chunk-DQP6X25N.mjs +208 -0
  27. package/dist/chunk-DQP6X25N.mjs.map +1 -0
  28. package/dist/chunk-DTDTOMTB.js +238 -0
  29. package/dist/chunk-DTDTOMTB.js.map +1 -0
  30. package/dist/chunk-DTEDYRTL.js +242 -0
  31. package/dist/chunk-DTEDYRTL.js.map +1 -0
  32. package/dist/chunk-EQ3RRHB7.mjs +211 -0
  33. package/dist/chunk-EQ3RRHB7.mjs.map +1 -0
  34. package/dist/chunk-FDJPXQTF.js +46 -0
  35. package/dist/chunk-FDJPXQTF.js.map +1 -0
  36. package/dist/chunk-FRKQ3Z2L.mjs +40 -0
  37. package/dist/chunk-FRKQ3Z2L.mjs.map +1 -0
  38. package/dist/chunk-FS7FRO7B.mjs +90 -0
  39. package/dist/chunk-FS7FRO7B.mjs.map +1 -0
  40. package/dist/chunk-GKTIFXPN.js +170 -0
  41. package/dist/chunk-GKTIFXPN.js.map +1 -0
  42. package/dist/chunk-H4M66BA3.js +62 -0
  43. package/dist/chunk-H4M66BA3.js.map +1 -0
  44. package/dist/chunk-HPNXIKFY.js +76 -0
  45. package/dist/chunk-HPNXIKFY.js.map +1 -0
  46. package/dist/chunk-HS277C77.js +75 -0
  47. package/dist/chunk-HS277C77.js.map +1 -0
  48. package/dist/chunk-I5YZ7QUK.js +121 -0
  49. package/dist/chunk-I5YZ7QUK.js.map +1 -0
  50. package/dist/chunk-IC233ZQS.js +211 -0
  51. package/dist/chunk-IC233ZQS.js.map +1 -0
  52. package/dist/chunk-IUBAETUH.js +137 -0
  53. package/dist/chunk-IUBAETUH.js.map +1 -0
  54. package/dist/chunk-J56A7UCK.mjs +123 -0
  55. package/dist/chunk-J56A7UCK.mjs.map +1 -0
  56. package/dist/chunk-JR6HDRNV.mjs +242 -0
  57. package/dist/chunk-JR6HDRNV.mjs.map +1 -0
  58. package/dist/chunk-JRBREX22.mjs +75 -0
  59. package/dist/chunk-JRBREX22.mjs.map +1 -0
  60. package/dist/chunk-JRQHIBG5.mjs +399 -0
  61. package/dist/chunk-JRQHIBG5.mjs.map +1 -0
  62. package/dist/chunk-LM4NUNMT.mjs +76 -0
  63. package/dist/chunk-LM4NUNMT.mjs.map +1 -0
  64. package/dist/chunk-M7455RU7.js +320 -0
  65. package/dist/chunk-M7455RU7.js.map +1 -0
  66. package/dist/chunk-MHM5LRRF.mjs +122 -0
  67. package/dist/chunk-MHM5LRRF.mjs.map +1 -0
  68. package/dist/chunk-NHRBO3LU.mjs +50 -0
  69. package/dist/chunk-NHRBO3LU.mjs.map +1 -0
  70. package/dist/chunk-NM6OYEPP.mjs +182 -0
  71. package/dist/chunk-NM6OYEPP.mjs.map +1 -0
  72. package/dist/chunk-NUOBUW7C.js +85 -0
  73. package/dist/chunk-NUOBUW7C.js.map +1 -0
  74. package/dist/chunk-QP75SWIQ.js +53 -0
  75. package/dist/chunk-QP75SWIQ.js.map +1 -0
  76. package/dist/chunk-RI6MVJJN.js +122 -0
  77. package/dist/chunk-RI6MVJJN.js.map +1 -0
  78. package/dist/chunk-S6VGOPUY.js +14 -0
  79. package/dist/chunk-S6VGOPUY.js.map +1 -0
  80. package/dist/chunk-UGFBA4GV.js +123 -0
  81. package/dist/chunk-UGFBA4GV.js.map +1 -0
  82. package/dist/chunk-UKYY2RVS.mjs +137 -0
  83. package/dist/chunk-UKYY2RVS.mjs.map +1 -0
  84. package/dist/chunk-UM4ORJ5B.mjs +121 -0
  85. package/dist/chunk-UM4ORJ5B.mjs.map +1 -0
  86. package/dist/chunk-UQQWZT6C.mjs +14 -0
  87. package/dist/chunk-UQQWZT6C.mjs.map +1 -0
  88. package/dist/chunk-VH47Q6TS.js +182 -0
  89. package/dist/chunk-VH47Q6TS.js.map +1 -0
  90. package/dist/chunk-XGRAHX6T.mjs +53 -0
  91. package/dist/chunk-XGRAHX6T.mjs.map +1 -0
  92. package/dist/chunk-XUI43LEZ.mjs +30 -0
  93. package/dist/chunk-XUI43LEZ.mjs.map +1 -0
  94. package/dist/chunk-Y734U4V6.mjs +85 -0
  95. package/dist/chunk-Y734U4V6.mjs.map +1 -0
  96. package/dist/chunk-Y7ENNK7L.mjs +238 -0
  97. package/dist/chunk-Y7ENNK7L.mjs.map +1 -0
  98. package/dist/chunk-Z4BLTVTB.js +30 -0
  99. package/dist/chunk-Z4BLTVTB.js.map +1 -0
  100. package/dist/chunk-ZCQRDZ36.js +208 -0
  101. package/dist/chunk-ZCQRDZ36.js.map +1 -0
  102. package/dist/chunk-ZJLZSFOZ.js +90 -0
  103. package/dist/chunk-ZJLZSFOZ.js.map +1 -0
  104. package/dist/chunk-ZNZEJDOE.js +50 -0
  105. package/dist/chunk-ZNZEJDOE.js.map +1 -0
  106. package/dist/constants.js +15 -110
  107. package/dist/constants.js.map +1 -1
  108. package/dist/constants.mjs +16 -0
  109. package/dist/constants.mjs.map +1 -0
  110. package/dist/gas-flows/DefaultGasFeeFlow.js +14 -77
  111. package/dist/gas-flows/DefaultGasFeeFlow.js.map +1 -1
  112. package/dist/gas-flows/DefaultGasFeeFlow.mjs +15 -0
  113. package/dist/gas-flows/DefaultGasFeeFlow.mjs.map +1 -0
  114. package/dist/gas-flows/LineaGasFeeFlow.js +15 -110
  115. package/dist/gas-flows/LineaGasFeeFlow.js.map +1 -1
  116. package/dist/gas-flows/LineaGasFeeFlow.mjs +16 -0
  117. package/dist/gas-flows/LineaGasFeeFlow.mjs.map +1 -0
  118. package/dist/helpers/EtherscanRemoteTransactionSource.js +11 -145
  119. package/dist/helpers/EtherscanRemoteTransactionSource.js.map +1 -1
  120. package/dist/helpers/EtherscanRemoteTransactionSource.mjs +12 -0
  121. package/dist/helpers/EtherscanRemoteTransactionSource.mjs.map +1 -0
  122. package/dist/helpers/GasFeePoller.js +10 -143
  123. package/dist/helpers/GasFeePoller.js.map +1 -1
  124. package/dist/helpers/GasFeePoller.mjs +11 -0
  125. package/dist/helpers/GasFeePoller.mjs.map +1 -0
  126. package/dist/helpers/IncomingTransactionHelper.js +8 -205
  127. package/dist/helpers/IncomingTransactionHelper.js.map +1 -1
  128. package/dist/helpers/IncomingTransactionHelper.mjs +9 -0
  129. package/dist/helpers/IncomingTransactionHelper.mjs.map +1 -0
  130. package/dist/helpers/MultichainTrackingHelper.js +12 -291
  131. package/dist/helpers/MultichainTrackingHelper.js.map +1 -1
  132. package/dist/helpers/MultichainTrackingHelper.mjs +13 -0
  133. package/dist/helpers/MultichainTrackingHelper.mjs.map +1 -0
  134. package/dist/helpers/PendingTransactionTracker.js +9 -360
  135. package/dist/helpers/PendingTransactionTracker.js.map +1 -1
  136. package/dist/helpers/PendingTransactionTracker.mjs +10 -0
  137. package/dist/helpers/PendingTransactionTracker.mjs.map +1 -0
  138. package/dist/index.js +56 -26
  139. package/dist/index.js.map +1 -1
  140. package/dist/index.mjs +57 -0
  141. package/dist/index.mjs.map +1 -0
  142. package/dist/logger.js +11 -8
  143. package/dist/logger.js.map +1 -1
  144. package/dist/logger.mjs +12 -0
  145. package/dist/logger.mjs.map +1 -0
  146. package/dist/tsconfig.build.tsbuildinfo +1 -0
  147. package/dist/{TransactionController.d.ts → types/TransactionController.d.ts} +235 -46
  148. package/dist/types/TransactionController.d.ts.map +1 -0
  149. package/dist/{constants.d.ts → types/constants.d.ts} +5 -0
  150. package/dist/types/constants.d.ts.map +1 -0
  151. package/dist/types/gas-flows/DefaultGasFeeFlow.d.ts.map +1 -0
  152. package/dist/types/gas-flows/LineaGasFeeFlow.d.ts.map +1 -0
  153. package/dist/types/helpers/EtherscanRemoteTransactionSource.d.ts.map +1 -0
  154. package/dist/types/helpers/GasFeePoller.d.ts.map +1 -0
  155. package/dist/types/helpers/IncomingTransactionHelper.d.ts.map +1 -0
  156. package/dist/types/helpers/MultichainTrackingHelper.d.ts.map +1 -0
  157. package/dist/types/helpers/PendingTransactionTracker.d.ts.map +1 -0
  158. package/dist/types/index.d.ts +9 -0
  159. package/dist/types/index.d.ts.map +1 -0
  160. package/dist/types/logger.d.ts.map +1 -0
  161. package/dist/{types.d.ts → types/types.d.ts} +63 -65
  162. package/dist/types/types.d.ts.map +1 -0
  163. package/dist/types/utils/etherscan.d.ts.map +1 -0
  164. package/dist/types/utils/external-transactions.d.ts.map +1 -0
  165. package/dist/types/utils/gas-fees.d.ts.map +1 -0
  166. package/dist/types/utils/gas-flow.d.ts.map +1 -0
  167. package/dist/types/utils/gas.d.ts.map +1 -0
  168. package/dist/types/utils/history.d.ts +20 -0
  169. package/dist/types/utils/history.d.ts.map +1 -0
  170. package/dist/types/utils/nonce.d.ts.map +1 -0
  171. package/dist/types/utils/simulation-api.d.ts +99 -0
  172. package/dist/types/utils/simulation-api.d.ts.map +1 -0
  173. package/dist/types/utils/simulation.d.ts +21 -0
  174. package/dist/types/utils/simulation.d.ts.map +1 -0
  175. package/dist/{utils → types/utils}/swaps.d.ts +8 -5
  176. package/dist/types/utils/swaps.d.ts.map +1 -0
  177. package/dist/types/utils/transaction-type.d.ts.map +1 -0
  178. package/dist/types/utils/utils.d.ts.map +1 -0
  179. package/dist/types/utils/validation.d.ts.map +1 -0
  180. package/dist/types.js +19 -170
  181. package/dist/types.js.map +1 -1
  182. package/dist/types.mjs +20 -0
  183. package/dist/types.mjs.map +1 -0
  184. package/dist/utils/etherscan.js +13 -118
  185. package/dist/utils/etherscan.js.map +1 -1
  186. package/dist/utils/etherscan.mjs +14 -0
  187. package/dist/utils/etherscan.mjs.map +1 -0
  188. package/dist/utils/external-transactions.js +8 -35
  189. package/dist/utils/external-transactions.js.map +1 -1
  190. package/dist/utils/external-transactions.mjs +9 -0
  191. package/dist/utils/external-transactions.mjs.map +1 -0
  192. package/dist/utils/gas-fees.js +15 -200
  193. package/dist/utils/gas-fees.js.map +1 -1
  194. package/dist/utils/gas-fees.mjs +16 -0
  195. package/dist/utils/gas-fees.mjs.map +1 -0
  196. package/dist/utils/gas-flow.js +10 -52
  197. package/dist/utils/gas-flow.js.map +1 -1
  198. package/dist/utils/gas-flow.mjs +11 -0
  199. package/dist/utils/gas-flow.mjs.map +1 -0
  200. package/dist/utils/gas.js +19 -133
  201. package/dist/utils/gas.js.map +1 -1
  202. package/dist/utils/gas.mjs +20 -0
  203. package/dist/utils/gas.mjs.map +1 -0
  204. package/dist/utils/history.js +9 -83
  205. package/dist/utils/history.js.map +1 -1
  206. package/dist/utils/history.mjs +10 -0
  207. package/dist/utils/history.mjs.map +1 -0
  208. package/dist/utils/nonce.js +10 -76
  209. package/dist/utils/nonce.js.map +1 -1
  210. package/dist/utils/nonce.mjs +11 -0
  211. package/dist/utils/nonce.mjs.map +1 -0
  212. package/dist/utils/simulation-api.js +10 -0
  213. package/dist/utils/simulation-api.js.map +1 -0
  214. package/dist/utils/simulation-api.mjs +10 -0
  215. package/dist/utils/simulation-api.mjs.map +1 -0
  216. package/dist/utils/simulation.js +12 -0
  217. package/dist/utils/simulation.js.map +1 -0
  218. package/dist/utils/simulation.mjs +12 -0
  219. package/dist/utils/simulation.mjs.map +1 -0
  220. package/dist/utils/swaps.js +23 -256
  221. package/dist/utils/swaps.js.map +1 -1
  222. package/dist/utils/swaps.mjs +24 -0
  223. package/dist/utils/swaps.mjs.map +1 -0
  224. package/dist/utils/transaction-type.js +10 -120
  225. package/dist/utils/transaction-type.js.map +1 -1
  226. package/dist/utils/transaction-type.mjs +11 -0
  227. package/dist/utils/transaction-type.mjs.map +1 -0
  228. package/dist/utils/utils.js +32 -160
  229. package/dist/utils/utils.js.map +1 -1
  230. package/dist/utils/utils.mjs +33 -0
  231. package/dist/utils/utils.mjs.map +1 -0
  232. package/dist/utils/validation.js +11 -258
  233. package/dist/utils/validation.js.map +1 -1
  234. package/dist/utils/validation.mjs +12 -0
  235. package/dist/utils/validation.mjs.map +1 -0
  236. package/package.json +21 -9
  237. package/dist/TransactionController.d.ts.map +0 -1
  238. package/dist/constants.d.ts.map +0 -1
  239. package/dist/gas-flows/DefaultGasFeeFlow.d.ts.map +0 -1
  240. package/dist/gas-flows/LineaGasFeeFlow.d.ts.map +0 -1
  241. package/dist/helpers/EtherscanRemoteTransactionSource.d.ts.map +0 -1
  242. package/dist/helpers/GasFeePoller.d.ts.map +0 -1
  243. package/dist/helpers/IncomingTransactionHelper.d.ts.map +0 -1
  244. package/dist/helpers/MultichainTrackingHelper.d.ts.map +0 -1
  245. package/dist/helpers/PendingTransactionTracker.d.ts.map +0 -1
  246. package/dist/index.d.ts +0 -7
  247. package/dist/index.d.ts.map +0 -1
  248. package/dist/logger.d.ts.map +0 -1
  249. package/dist/types.d.ts.map +0 -1
  250. package/dist/utils/etherscan.d.ts.map +0 -1
  251. package/dist/utils/external-transactions.d.ts.map +0 -1
  252. package/dist/utils/gas-fees.d.ts.map +0 -1
  253. package/dist/utils/gas-flow.d.ts.map +0 -1
  254. package/dist/utils/gas.d.ts.map +0 -1
  255. package/dist/utils/history.d.ts +0 -15
  256. package/dist/utils/history.d.ts.map +0 -1
  257. package/dist/utils/nonce.d.ts.map +0 -1
  258. package/dist/utils/swaps.d.ts.map +0 -1
  259. package/dist/utils/transaction-type.d.ts.map +0 -1
  260. package/dist/utils/utils.d.ts.map +0 -1
  261. package/dist/utils/validation.d.ts.map +0 -1
  262. /package/dist/{gas-flows → types/gas-flows}/DefaultGasFeeFlow.d.ts +0 -0
  263. /package/dist/{gas-flows → types/gas-flows}/LineaGasFeeFlow.d.ts +0 -0
  264. /package/dist/{helpers → types/helpers}/EtherscanRemoteTransactionSource.d.ts +0 -0
  265. /package/dist/{helpers → types/helpers}/GasFeePoller.d.ts +0 -0
  266. /package/dist/{helpers → types/helpers}/IncomingTransactionHelper.d.ts +0 -0
  267. /package/dist/{helpers → types/helpers}/MultichainTrackingHelper.d.ts +0 -0
  268. /package/dist/{helpers → types/helpers}/PendingTransactionTracker.d.ts +0 -0
  269. /package/dist/{logger.d.ts → types/logger.d.ts} +0 -0
  270. /package/dist/{utils → types/utils}/etherscan.d.ts +0 -0
  271. /package/dist/{utils → types/utils}/external-transactions.d.ts +0 -0
  272. /package/dist/{utils → types/utils}/gas-fees.d.ts +0 -0
  273. /package/dist/{utils → types/utils}/gas-flow.d.ts +0 -0
  274. /package/dist/{utils → types/utils}/gas.d.ts +0 -0
  275. /package/dist/{utils → types/utils}/nonce.d.ts +0 -0
  276. /package/dist/{utils → types/utils}/transaction-type.d.ts +0 -0
  277. /package/dist/{utils → types/utils}/utils.d.ts +0 -0
  278. /package/dist/{utils → types/utils}/validation.d.ts +0 -0
@@ -0,0 +1,242 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
+
3
+ var _chunkFDJPXQTFjs = require('./chunk-FDJPXQTF.js');
4
+
5
+
6
+ var _chunkS6VGOPUYjs = require('./chunk-S6VGOPUY.js');
7
+
8
+ // src/utils/simulation.ts
9
+ var _abi = require('@ethersproject/abi');
10
+ var _controllerutils = require('@metamask/controller-utils');
11
+ var _metamaskethabis = require('@metamask/metamask-eth-abis');
12
+ var _utils = require('@metamask/utils');
13
+ var log = _utils.createModuleLogger.call(void 0, _chunkS6VGOPUYjs.projectLogger, "simulation");
14
+ async function getSimulationData(request) {
15
+ const { chainId, from, to, value, data } = request;
16
+ log("Getting simulation data", request);
17
+ try {
18
+ const response = await _chunkFDJPXQTFjs.simulateTransactions.call(void 0, chainId, {
19
+ transactions: [{ from, to, value, data }],
20
+ withCallTrace: true,
21
+ withLogs: true
22
+ });
23
+ const nativeBalanceChange = getNativeBalanceChange(request.from, response);
24
+ const events = getEvents(response);
25
+ log("Parsed events", events);
26
+ const tokenBalanceChanges = await getTokenBalanceChanges(request, events);
27
+ return {
28
+ nativeBalanceChange,
29
+ tokenBalanceChanges
30
+ };
31
+ } catch (error) {
32
+ log("Failed to get simulation data", error, request);
33
+ return void 0;
34
+ }
35
+ }
36
+ function getNativeBalanceChange(userAddress, response) {
37
+ const transactionResponse = response.transactions[0];
38
+ if (!transactionResponse) {
39
+ return void 0;
40
+ }
41
+ const { stateDiff } = transactionResponse;
42
+ const previousBalance = stateDiff.pre[userAddress]?.balance;
43
+ const newBalance = stateDiff.post[userAddress]?.balance;
44
+ if (!previousBalance || !newBalance) {
45
+ return void 0;
46
+ }
47
+ return getSimulationBalanceChange(previousBalance, newBalance);
48
+ }
49
+ function getEvents(response) {
50
+ const logs = extractLogs(response.transactions[0]?.callTrace ?? {});
51
+ log("Extracted logs", logs);
52
+ const erc20Interface = new (0, _abi.Interface)(_metamaskethabis.abiERC20);
53
+ const erc721Interface = new (0, _abi.Interface)(_metamaskethabis.abiERC721);
54
+ const erc1155Interface = new (0, _abi.Interface)(_metamaskethabis.abiERC1155);
55
+ return logs.map((currentLog) => {
56
+ const event = parseLog(
57
+ currentLog,
58
+ erc20Interface,
59
+ erc721Interface,
60
+ erc1155Interface
61
+ );
62
+ if (!event) {
63
+ log("Failed to parse log", currentLog);
64
+ return void 0;
65
+ }
66
+ const inputs = event.abi.find((e) => e.name === event.name)?.inputs;
67
+ if (!inputs) {
68
+ log("Failed to find inputs for event", event);
69
+ return void 0;
70
+ }
71
+ const args = parseEventArgs(event.args, inputs);
72
+ return {
73
+ contractAddress: currentLog.address,
74
+ tokenStandard: event.standard,
75
+ name: event.name,
76
+ args,
77
+ abi: event.abi
78
+ };
79
+ }).filter((e) => e !== void 0);
80
+ }
81
+ function parseEventArgs(args, abiInputs) {
82
+ return args.reduce((result, arg, index) => {
83
+ const name = abiInputs[index].name.replace("_", "");
84
+ const value = parseEventArgValue(arg);
85
+ result[name] = value;
86
+ return result;
87
+ }, {});
88
+ }
89
+ function parseEventArgValue(value) {
90
+ if (Array.isArray(value)) {
91
+ return value.map(parseEventArgValue);
92
+ }
93
+ return (value.toHexString?.() ?? value).toLowerCase();
94
+ }
95
+ async function getTokenBalanceChanges(request, events) {
96
+ const balanceTransactionsByToken = getTokenBalanceTransactions(
97
+ request,
98
+ events
99
+ );
100
+ const balanceTransactions = [...balanceTransactionsByToken.values()];
101
+ log("Generated balance transactions", balanceTransactions);
102
+ if (!balanceTransactions.length) {
103
+ return [];
104
+ }
105
+ const response = await _chunkFDJPXQTFjs.simulateTransactions.call(void 0, request.chainId, {
106
+ transactions: [...balanceTransactions, request, ...balanceTransactions]
107
+ });
108
+ log("Balance simulation response", response);
109
+ if (response.transactions.length !== balanceTransactions.length * 2 + 1) {
110
+ throw new Error("Invalid response from simulation API");
111
+ }
112
+ return [...balanceTransactionsByToken.keys()].map((token, index) => {
113
+ const previousBalance = normalizeReturnValue(
114
+ response.transactions[index].return
115
+ );
116
+ const newBalance = normalizeReturnValue(
117
+ response.transactions[index + balanceTransactions.length + 1].return
118
+ );
119
+ const balanceChange = getSimulationBalanceChange(
120
+ previousBalance,
121
+ newBalance
122
+ );
123
+ if (!balanceChange) {
124
+ return void 0;
125
+ }
126
+ return {
127
+ ...token,
128
+ ...balanceChange
129
+ };
130
+ }).filter((change) => change !== void 0);
131
+ }
132
+ function getTokenBalanceTransactions(request, events) {
133
+ const tokenKeys = /* @__PURE__ */ new Set();
134
+ return events.reduce((result, event) => {
135
+ if (!["Transfer", "TransferSingle", "TransferBatch"].includes(event.name) || ![event.args.from, event.args.to].includes(request.from)) {
136
+ log("Ignoring event", event);
137
+ return result;
138
+ }
139
+ let tokenIds = [void 0];
140
+ if (event.tokenStandard === "erc721" /* erc721 */) {
141
+ tokenIds = [event.args.tokenId];
142
+ }
143
+ if (event.tokenStandard === "erc1155" /* erc1155 */ && event.name === "TransferSingle") {
144
+ tokenIds = [event.args.id];
145
+ }
146
+ if (event.tokenStandard === "erc1155" /* erc1155 */ && event.name === "TransferBatch") {
147
+ tokenIds = event.args.ids;
148
+ }
149
+ log("Extracted token ids", tokenIds);
150
+ for (const tokenId of tokenIds) {
151
+ const simulationToken = {
152
+ address: event.contractAddress,
153
+ standard: event.tokenStandard,
154
+ id: tokenId
155
+ };
156
+ const tokenKey = JSON.stringify(simulationToken);
157
+ if (tokenKeys.has(tokenKey)) {
158
+ log(
159
+ "Ignoring additional event with same contract and token ID",
160
+ simulationToken
161
+ );
162
+ continue;
163
+ }
164
+ tokenKeys.add(tokenKey);
165
+ const parameters = [request.from];
166
+ if (event.tokenStandard === "erc1155" /* erc1155 */) {
167
+ parameters.push(tokenId);
168
+ }
169
+ result.set(simulationToken, {
170
+ from: request.from,
171
+ to: event.contractAddress,
172
+ data: new (0, _abi.Interface)(event.abi).encodeFunctionData(
173
+ "balanceOf",
174
+ parameters
175
+ )
176
+ });
177
+ }
178
+ return result;
179
+ }, /* @__PURE__ */ new Map());
180
+ }
181
+ function parseLog(eventLog, erc20, erc721, erc1155) {
182
+ const abisByStandard = [
183
+ {
184
+ abi: _metamaskethabis.abiERC20,
185
+ contractInterface: erc20,
186
+ standard: "erc20" /* erc20 */
187
+ },
188
+ {
189
+ abi: _metamaskethabis.abiERC721,
190
+ contractInterface: erc721,
191
+ standard: "erc721" /* erc721 */
192
+ },
193
+ {
194
+ abi: _metamaskethabis.abiERC1155,
195
+ contractInterface: erc1155,
196
+ standard: "erc1155" /* erc1155 */
197
+ }
198
+ ];
199
+ for (const { abi, contractInterface, standard } of abisByStandard) {
200
+ try {
201
+ return {
202
+ ...contractInterface.parseLog(eventLog),
203
+ abi,
204
+ standard
205
+ };
206
+ } catch (e) {
207
+ continue;
208
+ }
209
+ }
210
+ return void 0;
211
+ }
212
+ function extractLogs(call) {
213
+ const logs = call.logs ?? [];
214
+ const nestedCalls = call.calls ?? [];
215
+ return [
216
+ ...logs,
217
+ ...nestedCalls.map((nestedCall) => extractLogs(nestedCall)).flat()
218
+ ];
219
+ }
220
+ function getSimulationBalanceChange(previousBalance, newBalance) {
221
+ const differenceBN = _controllerutils.hexToBN.call(void 0, newBalance).sub(_controllerutils.hexToBN.call(void 0, previousBalance));
222
+ const isDecrease = differenceBN.isNeg();
223
+ const difference = _controllerutils.toHex.call(void 0, differenceBN.abs());
224
+ if (differenceBN.isZero()) {
225
+ log("Balance change is zero");
226
+ return void 0;
227
+ }
228
+ return {
229
+ previousBalance,
230
+ newBalance,
231
+ difference,
232
+ isDecrease
233
+ };
234
+ }
235
+ function normalizeReturnValue(value) {
236
+ return _controllerutils.toHex.call(void 0, _controllerutils.hexToBN.call(void 0, value));
237
+ }
238
+
239
+
240
+
241
+ exports.getSimulationData = getSimulationData;
242
+ //# sourceMappingURL=chunk-DTEDYRTL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/simulation.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Fragment, LogDescription, Result } from '@ethersproject/abi';\nimport { Interface } from '@ethersproject/abi';\nimport { hexToBN, toHex } from '@metamask/controller-utils';\nimport { abiERC20, abiERC721, abiERC1155 } from '@metamask/metamask-eth-abis';\nimport { createModuleLogger, type Hex } from '@metamask/utils';\n\nimport { projectLogger } from '../logger';\nimport type {\n SimulationBalanceChange,\n SimulationData,\n SimulationTokenBalanceChange,\n SimulationToken,\n} from '../types';\nimport { SimulationTokenStandard } from '../types';\nimport type {\n SimulationResponseLog,\n SimulationRequestTransaction,\n SimulationResponse,\n SimulationResponseCallTrace,\n} from './simulation-api';\nimport { simulateTransactions } from './simulation-api';\n\nconst log = createModuleLogger(projectLogger, 'simulation');\n\ntype ABI = Fragment[];\n\nexport type GetSimulationDataRequest = {\n chainId: Hex;\n from: Hex;\n to?: Hex;\n value?: Hex;\n data?: Hex;\n};\n\ntype ParsedEvent = {\n contractAddress: Hex;\n tokenStandard: SimulationTokenStandard;\n name: string;\n args: Record<string, Hex | Hex[]>;\n abi: ABI;\n};\n\n/**\n * Generate simulation data for a transaction.\n * @param request - The transaction to simulate.\n * @param request.chainId - The chain ID of the transaction.\n * @param request.from - The sender of the transaction.\n * @param request.to - The recipient of the transaction.\n * @param request.value - The value of the transaction.\n * @param request.data - The data of the transaction.\n * @returns The simulation data.\n */\nexport async function getSimulationData(\n request: GetSimulationDataRequest,\n): Promise<SimulationData | undefined> {\n const { chainId, from, to, value, data } = request;\n\n log('Getting simulation data', request);\n\n try {\n const response = await simulateTransactions(chainId, {\n transactions: [{ from, to, value, data }],\n withCallTrace: true,\n withLogs: true,\n });\n\n const nativeBalanceChange = getNativeBalanceChange(request.from, response);\n const events = getEvents(response);\n\n log('Parsed events', events);\n\n const tokenBalanceChanges = await getTokenBalanceChanges(request, events);\n\n return {\n nativeBalanceChange,\n tokenBalanceChanges,\n };\n } catch (error) {\n log('Failed to get simulation data', error, request);\n return undefined;\n }\n}\n\n/**\n * Extract the native balance change from a simulation response.\n * @param userAddress - The user's account address.\n * @param response - The simulation response.\n * @returns The native balance change or undefined if unchanged.\n */\nfunction getNativeBalanceChange(\n userAddress: Hex,\n response: SimulationResponse,\n): SimulationBalanceChange | undefined {\n const transactionResponse = response.transactions[0];\n\n /* istanbul ignore next */\n if (!transactionResponse) {\n return undefined;\n }\n\n const { stateDiff } = transactionResponse;\n const previousBalance = stateDiff.pre[userAddress]?.balance;\n const newBalance = stateDiff.post[userAddress]?.balance;\n\n if (!previousBalance || !newBalance) {\n return undefined;\n }\n\n return getSimulationBalanceChange(previousBalance, newBalance);\n}\n\n/**\n * Extract events from a simulation response.\n * @param response - The simulation response.\n * @returns The parsed events.\n */\nfunction getEvents(response: SimulationResponse): ParsedEvent[] {\n /* istanbul ignore next */\n const logs = extractLogs(response.transactions[0]?.callTrace ?? {});\n\n log('Extracted logs', logs);\n\n const erc20Interface = new Interface(abiERC20);\n const erc721Interface = new Interface(abiERC721);\n const erc1155Interface = new Interface(abiERC1155);\n\n return logs\n .map((currentLog) => {\n const event = parseLog(\n currentLog,\n erc20Interface,\n erc721Interface,\n erc1155Interface,\n );\n\n if (!event) {\n log('Failed to parse log', currentLog);\n return undefined;\n }\n\n /* istanbul ignore next */\n const inputs = event.abi.find((e) => e.name === event.name)?.inputs;\n\n /* istanbul ignore if */\n if (!inputs) {\n log('Failed to find inputs for event', event);\n return undefined;\n }\n\n const args = parseEventArgs(event.args, inputs);\n\n return {\n contractAddress: currentLog.address,\n tokenStandard: event.standard,\n name: event.name,\n args,\n abi: event.abi,\n };\n })\n .filter((e) => e !== undefined) as ParsedEvent[];\n}\n\n/**\n * Parse event arguments using ABI input definitions.\n * @param args - The raw event arguments.\n * @param abiInputs - The ABI input definitions.\n * @returns The parsed event arguments.\n */\nfunction parseEventArgs(\n args: Result,\n abiInputs: { name: string }[],\n): Record<string, Hex | Hex[]> {\n return args.reduce((result, arg, index) => {\n const name = abiInputs[index].name.replace('_', '');\n const value = parseEventArgValue(arg);\n\n result[name] = value;\n\n return result;\n }, {});\n}\n\n/**\n * Parse an event argument value.\n * @param value - The event argument value.\n * @returns The parsed event argument value.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction parseEventArgValue(value: any): Hex | Hex[] {\n if (Array.isArray(value)) {\n return value.map(parseEventArgValue) as Hex[];\n }\n\n return (value.toHexString?.() ?? value).toLowerCase();\n}\n\n/**\n * Generate token balance changes from parsed events.\n * @param request - The transaction that was simulated.\n * @param events - The parsed events.\n * @returns An array of token balance changes.\n */\nasync function getTokenBalanceChanges(\n request: GetSimulationDataRequest,\n events: ParsedEvent[],\n): Promise<SimulationTokenBalanceChange[]> {\n const balanceTransactionsByToken = getTokenBalanceTransactions(\n request,\n events,\n );\n\n const balanceTransactions = [...balanceTransactionsByToken.values()];\n\n log('Generated balance transactions', balanceTransactions);\n\n if (!balanceTransactions.length) {\n return [];\n }\n\n const response = await simulateTransactions(request.chainId as Hex, {\n transactions: [...balanceTransactions, request, ...balanceTransactions],\n });\n\n log('Balance simulation response', response);\n\n if (response.transactions.length !== balanceTransactions.length * 2 + 1) {\n throw new Error('Invalid response from simulation API');\n }\n\n return [...balanceTransactionsByToken.keys()]\n .map((token, index) => {\n const previousBalance = normalizeReturnValue(\n response.transactions[index].return,\n );\n\n const newBalance = normalizeReturnValue(\n response.transactions[index + balanceTransactions.length + 1].return,\n );\n\n const balanceChange = getSimulationBalanceChange(\n previousBalance,\n newBalance,\n );\n\n if (!balanceChange) {\n return undefined;\n }\n\n return {\n ...token,\n ...balanceChange,\n };\n })\n .filter((change) => change !== undefined) as SimulationTokenBalanceChange[];\n}\n\n/**\n * Generate transactions to check token balances.\n * @param request - The transaction that was simulated.\n * @param events - The parsed events.\n * @returns A map of token balance transactions keyed by token.\n */\nfunction getTokenBalanceTransactions(\n request: GetSimulationDataRequest,\n events: ParsedEvent[],\n): Map<SimulationToken, SimulationRequestTransaction> {\n const tokenKeys = new Set();\n\n return events.reduce((result, event) => {\n if (\n !['Transfer', 'TransferSingle', 'TransferBatch'].includes(event.name) ||\n ![event.args.from, event.args.to].includes(request.from)\n ) {\n log('Ignoring event', event);\n return result;\n }\n\n // ERC-20 does not have a token ID so default to undefined.\n let tokenIds: (Hex | undefined)[] = [undefined];\n\n if (event.tokenStandard === SimulationTokenStandard.erc721) {\n tokenIds = [event.args.tokenId as Hex];\n }\n\n if (\n event.tokenStandard === SimulationTokenStandard.erc1155 &&\n event.name === 'TransferSingle'\n ) {\n tokenIds = [event.args.id as Hex];\n }\n\n if (\n event.tokenStandard === SimulationTokenStandard.erc1155 &&\n event.name === 'TransferBatch'\n ) {\n tokenIds = event.args.ids as Hex[];\n }\n\n log('Extracted token ids', tokenIds);\n\n for (const tokenId of tokenIds) {\n const simulationToken: SimulationToken = {\n address: event.contractAddress,\n standard: event.tokenStandard,\n id: tokenId,\n };\n\n const tokenKey = JSON.stringify(simulationToken);\n\n if (tokenKeys.has(tokenKey)) {\n log(\n 'Ignoring additional event with same contract and token ID',\n simulationToken,\n );\n continue;\n }\n\n tokenKeys.add(tokenKey);\n\n const parameters = [request.from];\n\n if (event.tokenStandard === SimulationTokenStandard.erc1155) {\n parameters.push(tokenId as Hex);\n }\n\n result.set(simulationToken, {\n from: request.from,\n to: event.contractAddress,\n data: new Interface(event.abi).encodeFunctionData(\n 'balanceOf',\n parameters,\n ) as Hex,\n });\n }\n\n return result;\n }, new Map<SimulationToken, SimulationRequestTransaction>());\n}\n\n/**\n * Parse a raw event log using known ABIs.\n * @param eventLog - The raw event log.\n * @param erc20 - The ERC-20 ABI interface.\n * @param erc721 - The ERC-721 ABI interface.\n * @param erc1155 - The ERC-1155 ABI interface.\n * @returns The parsed event log or undefined if it could not be parsed.\n */\nfunction parseLog(\n eventLog: SimulationResponseLog,\n erc20: Interface,\n erc721: Interface,\n erc1155: Interface,\n):\n | (LogDescription & { abi: ABI; standard: SimulationTokenStandard })\n | undefined {\n const abisByStandard = [\n {\n abi: abiERC20,\n contractInterface: erc20,\n standard: SimulationTokenStandard.erc20,\n },\n {\n abi: abiERC721,\n contractInterface: erc721,\n standard: SimulationTokenStandard.erc721,\n },\n {\n abi: abiERC1155,\n contractInterface: erc1155,\n standard: SimulationTokenStandard.erc1155,\n },\n ];\n\n for (const { abi, contractInterface, standard } of abisByStandard) {\n try {\n return {\n ...contractInterface.parseLog(eventLog),\n abi,\n standard,\n };\n } catch (e) {\n continue;\n }\n }\n\n return undefined;\n}\n\n/**\n * Extract all logs from a call trace tree.\n * @param call - The root call trace.\n * @returns An array of logs.\n */\nfunction extractLogs(\n call: SimulationResponseCallTrace,\n): SimulationResponseLog[] {\n /* istanbul ignore next */\n const logs = call.logs ?? [];\n\n /* istanbul ignore next */\n const nestedCalls = call.calls ?? [];\n\n return [\n ...logs,\n ...nestedCalls.map((nestedCall) => extractLogs(nestedCall)).flat(),\n ];\n}\n\n/**\n * Generate balance change data from previous and new balances.\n * @param previousBalance - The previous balance.\n * @param newBalance - The new balance.\n * @returns The balance change data or undefined if unchanged.\n */\nfunction getSimulationBalanceChange(\n previousBalance: Hex,\n newBalance: Hex,\n): SimulationBalanceChange | undefined {\n const differenceBN = hexToBN(newBalance).sub(hexToBN(previousBalance));\n const isDecrease = differenceBN.isNeg();\n const difference = toHex(differenceBN.abs());\n\n if (differenceBN.isZero()) {\n log('Balance change is zero');\n return undefined;\n }\n\n return {\n previousBalance,\n newBalance,\n difference,\n isDecrease,\n };\n}\n\n/**\n * Normalize a return value.\n * @param value - The return value to normalize.\n * @returns The normalized return value.\n */\nfunction normalizeReturnValue(value: Hex): Hex {\n return toHex(hexToBN(value));\n}\n"]}
@@ -0,0 +1,211 @@
1
+ import {
2
+ validateIfTransactionUnapproved
3
+ } from "./chunk-J56A7UCK.mjs";
4
+ import {
5
+ CHAIN_IDS
6
+ } from "./chunk-MHM5LRRF.mjs";
7
+ import {
8
+ createModuleLogger,
9
+ projectLogger
10
+ } from "./chunk-UQQWZT6C.mjs";
11
+
12
+ // src/utils/swaps.ts
13
+ import { query } from "@metamask/controller-utils";
14
+ import { merge, pickBy } from "lodash";
15
+ var log = createModuleLogger(projectLogger, "swaps");
16
+ var UPDATE_POST_TX_BALANCE_TIMEOUT = 5e3;
17
+ var UPDATE_POST_TX_BALANCE_ATTEMPTS = 6;
18
+ var SWAPS_TESTNET_CHAIN_ID = "0x539";
19
+ var DEFAULT_TOKEN_ADDRESS = "0x0000000000000000000000000000000000000000";
20
+ var ETH_SWAPS_TOKEN_OBJECT = {
21
+ name: "Ether",
22
+ address: DEFAULT_TOKEN_ADDRESS,
23
+ decimals: 18
24
+ };
25
+ var BNB_SWAPS_TOKEN_OBJECT = {
26
+ name: "Binance Coin",
27
+ address: DEFAULT_TOKEN_ADDRESS,
28
+ decimals: 18
29
+ };
30
+ var MATIC_SWAPS_TOKEN_OBJECT = {
31
+ name: "Matic",
32
+ address: DEFAULT_TOKEN_ADDRESS,
33
+ decimals: 18
34
+ };
35
+ var AVAX_SWAPS_TOKEN_OBJECT = {
36
+ name: "Avalanche",
37
+ address: DEFAULT_TOKEN_ADDRESS,
38
+ decimals: 18
39
+ };
40
+ var TEST_ETH_SWAPS_TOKEN_OBJECT = {
41
+ name: "Test Ether",
42
+ address: DEFAULT_TOKEN_ADDRESS,
43
+ decimals: 18
44
+ };
45
+ var GOERLI_SWAPS_TOKEN_OBJECT = {
46
+ name: "Ether",
47
+ address: DEFAULT_TOKEN_ADDRESS,
48
+ decimals: 18
49
+ };
50
+ var ARBITRUM_SWAPS_TOKEN_OBJECT = {
51
+ ...ETH_SWAPS_TOKEN_OBJECT
52
+ };
53
+ var OPTIMISM_SWAPS_TOKEN_OBJECT = {
54
+ ...ETH_SWAPS_TOKEN_OBJECT
55
+ };
56
+ var ZKSYNC_ERA_SWAPS_TOKEN_OBJECT = {
57
+ ...ETH_SWAPS_TOKEN_OBJECT
58
+ };
59
+ var SWAPS_CHAINID_DEFAULT_TOKEN_MAP = {
60
+ [CHAIN_IDS.MAINNET]: ETH_SWAPS_TOKEN_OBJECT,
61
+ [SWAPS_TESTNET_CHAIN_ID]: TEST_ETH_SWAPS_TOKEN_OBJECT,
62
+ [CHAIN_IDS.BSC]: BNB_SWAPS_TOKEN_OBJECT,
63
+ [CHAIN_IDS.POLYGON]: MATIC_SWAPS_TOKEN_OBJECT,
64
+ [CHAIN_IDS.GOERLI]: GOERLI_SWAPS_TOKEN_OBJECT,
65
+ [CHAIN_IDS.AVALANCHE]: AVAX_SWAPS_TOKEN_OBJECT,
66
+ [CHAIN_IDS.OPTIMISM]: OPTIMISM_SWAPS_TOKEN_OBJECT,
67
+ [CHAIN_IDS.ARBITRUM]: ARBITRUM_SWAPS_TOKEN_OBJECT,
68
+ [CHAIN_IDS.ZKSYNC_ERA]: ZKSYNC_ERA_SWAPS_TOKEN_OBJECT
69
+ };
70
+ var SWAP_TRANSACTION_TYPES = [
71
+ "swap" /* swap */,
72
+ "swapApproval" /* swapApproval */
73
+ ];
74
+ function updateSwapsTransaction(transactionMeta, transactionType, swaps, {
75
+ isSwapsDisabled,
76
+ cancelTransaction,
77
+ messenger
78
+ }) {
79
+ if (isSwapsDisabled || !SWAP_TRANSACTION_TYPES.includes(transactionType)) {
80
+ return transactionMeta;
81
+ }
82
+ if (transactionType === "swap" /* swap */ && swaps?.hasApproveTx === false && transactionMeta.simulationFails) {
83
+ cancelTransaction(transactionMeta.id);
84
+ throw new Error("Simulation failed");
85
+ }
86
+ const swapsMeta = swaps?.meta;
87
+ if (!swapsMeta) {
88
+ return transactionMeta;
89
+ }
90
+ let updatedTransactionMeta = transactionMeta;
91
+ if (transactionType === "swapApproval" /* swapApproval */) {
92
+ updatedTransactionMeta = updateSwapApprovalTransaction(
93
+ transactionMeta,
94
+ swapsMeta
95
+ );
96
+ messenger.publish("TransactionController:transactionNewSwapApproval", {
97
+ transactionMeta: updatedTransactionMeta
98
+ });
99
+ }
100
+ if (transactionType === "swap" /* swap */) {
101
+ updatedTransactionMeta = updateSwapTransaction(transactionMeta, swapsMeta);
102
+ messenger.publish("TransactionController:transactionNewSwap", {
103
+ transactionMeta: updatedTransactionMeta
104
+ });
105
+ }
106
+ return updatedTransactionMeta;
107
+ }
108
+ async function updatePostTransactionBalance(transactionMeta, {
109
+ ethQuery,
110
+ getTransaction,
111
+ updateTransaction
112
+ }) {
113
+ log("Updating post transaction balance", transactionMeta.id);
114
+ const transactionId = transactionMeta.id;
115
+ let latestTransactionMeta;
116
+ let approvalTransactionMeta;
117
+ for (let i = 0; i < UPDATE_POST_TX_BALANCE_ATTEMPTS; i++) {
118
+ log("Querying balance", { attempt: i });
119
+ const postTransactionBalance = await query(ethQuery, "getBalance", [
120
+ transactionMeta.txParams.from
121
+ ]);
122
+ latestTransactionMeta = {
123
+ ...getTransaction(transactionId) ?? {}
124
+ };
125
+ approvalTransactionMeta = latestTransactionMeta.approvalTxId ? getTransaction(latestTransactionMeta.approvalTxId) : void 0;
126
+ latestTransactionMeta.postTxBalance = postTransactionBalance.toString(16);
127
+ const isDefaultTokenAddress = isSwapsDefaultTokenAddress(
128
+ transactionMeta.destinationTokenAddress,
129
+ transactionMeta.chainId
130
+ );
131
+ if (!isDefaultTokenAddress || transactionMeta.preTxBalance !== latestTransactionMeta.postTxBalance) {
132
+ log("Finishing post balance update", {
133
+ isDefaultTokenAddress,
134
+ preTxBalance: transactionMeta.preTxBalance,
135
+ postTxBalance: latestTransactionMeta.postTxBalance
136
+ });
137
+ break;
138
+ }
139
+ log("Waiting for balance to update", {
140
+ delay: UPDATE_POST_TX_BALANCE_TIMEOUT
141
+ });
142
+ await sleep(UPDATE_POST_TX_BALANCE_TIMEOUT);
143
+ }
144
+ updateTransaction(
145
+ latestTransactionMeta,
146
+ "TransactionController#updatePostTransactionBalance - Add post transaction balance"
147
+ );
148
+ log("Completed post balance update", latestTransactionMeta?.postTxBalance);
149
+ return {
150
+ updatedTransactionMeta: latestTransactionMeta,
151
+ approvalTransactionMeta
152
+ };
153
+ }
154
+ function updateSwapTransaction(transactionMeta, {
155
+ sourceTokenSymbol,
156
+ destinationTokenSymbol,
157
+ type,
158
+ destinationTokenDecimals,
159
+ destinationTokenAddress,
160
+ swapMetaData,
161
+ swapTokenValue,
162
+ estimatedBaseFee,
163
+ approvalTxId
164
+ }) {
165
+ validateIfTransactionUnapproved(transactionMeta, "updateSwapTransaction");
166
+ let swapTransaction = {
167
+ sourceTokenSymbol,
168
+ destinationTokenSymbol,
169
+ type,
170
+ destinationTokenDecimals,
171
+ destinationTokenAddress,
172
+ swapMetaData,
173
+ swapTokenValue,
174
+ estimatedBaseFee,
175
+ approvalTxId
176
+ };
177
+ swapTransaction = pickBy(swapTransaction);
178
+ return merge({}, transactionMeta, swapTransaction);
179
+ }
180
+ function updateSwapApprovalTransaction(transactionMeta, { type, sourceTokenSymbol }) {
181
+ validateIfTransactionUnapproved(
182
+ transactionMeta,
183
+ "updateSwapApprovalTransaction"
184
+ );
185
+ let swapApprovalTransaction = { type, sourceTokenSymbol };
186
+ swapApprovalTransaction = pickBy({
187
+ type,
188
+ sourceTokenSymbol
189
+ });
190
+ return merge({}, transactionMeta, swapApprovalTransaction);
191
+ }
192
+ function isSwapsDefaultTokenAddress(address, chainId) {
193
+ if (!address || !chainId) {
194
+ return false;
195
+ }
196
+ return address === SWAPS_CHAINID_DEFAULT_TOKEN_MAP[chainId]?.address;
197
+ }
198
+ function sleep(ms) {
199
+ return new Promise((resolve) => setTimeout(resolve, ms));
200
+ }
201
+
202
+ export {
203
+ UPDATE_POST_TX_BALANCE_TIMEOUT,
204
+ UPDATE_POST_TX_BALANCE_ATTEMPTS,
205
+ DEFAULT_TOKEN_ADDRESS,
206
+ SWAPS_CHAINID_DEFAULT_TOKEN_MAP,
207
+ SWAP_TRANSACTION_TYPES,
208
+ updateSwapsTransaction,
209
+ updatePostTransactionBalance
210
+ };
211
+ //# sourceMappingURL=chunk-EQ3RRHB7.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/swaps.ts"],"sourcesContent":["import { query } from '@metamask/controller-utils';\nimport type EthQuery from '@metamask/eth-query';\nimport { merge, pickBy } from 'lodash';\n\nimport { CHAIN_IDS } from '../constants';\nimport { createModuleLogger, projectLogger } from '../logger';\nimport type { TransactionControllerMessenger } from '../TransactionController';\nimport type { TransactionMeta } from '../types';\nimport { TransactionType } from '../types';\nimport { validateIfTransactionUnapproved } from './utils';\n\nconst log = createModuleLogger(projectLogger, 'swaps');\n\n/**\n * Interval in milliseconds between checks of post transaction balance\n */\nexport const UPDATE_POST_TX_BALANCE_TIMEOUT = 5000;\n\n/**\n * Retry attempts for checking post transaction balance\n */\nexport const UPDATE_POST_TX_BALANCE_ATTEMPTS = 6;\n\nconst SWAPS_TESTNET_CHAIN_ID = '0x539';\n\n/**\n * An address that the metaswap-api recognizes as the default token for the current network, in place of the token address that ERC-20 tokens have\n */\nexport const DEFAULT_TOKEN_ADDRESS =\n '0x0000000000000000000000000000000000000000';\n\n// Convert to a `type` in a future major version.\n// eslint-disable-next-line @typescript-eslint/consistent-type-definitions\ninterface SwapsTokenObject {\n /**\n * The name for the network\n */\n name: string;\n /**\n * An address that the metaswap-api recognizes as the default token\n */\n address: string;\n /**\n * Number of digits after decimal point\n */\n decimals: number;\n}\n\nconst ETH_SWAPS_TOKEN_OBJECT: SwapsTokenObject = {\n name: 'Ether',\n address: DEFAULT_TOKEN_ADDRESS,\n decimals: 18,\n};\n\nconst BNB_SWAPS_TOKEN_OBJECT: SwapsTokenObject = {\n name: 'Binance Coin',\n address: DEFAULT_TOKEN_ADDRESS,\n decimals: 18,\n} as const;\n\nconst MATIC_SWAPS_TOKEN_OBJECT: SwapsTokenObject = {\n name: 'Matic',\n address: DEFAULT_TOKEN_ADDRESS,\n decimals: 18,\n} as const;\n\nconst AVAX_SWAPS_TOKEN_OBJECT: SwapsTokenObject = {\n name: 'Avalanche',\n address: DEFAULT_TOKEN_ADDRESS,\n decimals: 18,\n} as const;\n\nconst TEST_ETH_SWAPS_TOKEN_OBJECT: SwapsTokenObject = {\n name: 'Test Ether',\n address: DEFAULT_TOKEN_ADDRESS,\n decimals: 18,\n} as const;\n\nconst GOERLI_SWAPS_TOKEN_OBJECT: SwapsTokenObject = {\n name: 'Ether',\n address: DEFAULT_TOKEN_ADDRESS,\n decimals: 18,\n} as const;\n\nconst ARBITRUM_SWAPS_TOKEN_OBJECT: SwapsTokenObject = {\n ...ETH_SWAPS_TOKEN_OBJECT,\n} as const;\n\nconst OPTIMISM_SWAPS_TOKEN_OBJECT: SwapsTokenObject = {\n ...ETH_SWAPS_TOKEN_OBJECT,\n} as const;\n\nconst ZKSYNC_ERA_SWAPS_TOKEN_OBJECT: SwapsTokenObject = {\n ...ETH_SWAPS_TOKEN_OBJECT,\n} as const;\n\nexport const SWAPS_CHAINID_DEFAULT_TOKEN_MAP = {\n [CHAIN_IDS.MAINNET]: ETH_SWAPS_TOKEN_OBJECT,\n [SWAPS_TESTNET_CHAIN_ID]: TEST_ETH_SWAPS_TOKEN_OBJECT,\n [CHAIN_IDS.BSC]: BNB_SWAPS_TOKEN_OBJECT,\n [CHAIN_IDS.POLYGON]: MATIC_SWAPS_TOKEN_OBJECT,\n [CHAIN_IDS.GOERLI]: GOERLI_SWAPS_TOKEN_OBJECT,\n [CHAIN_IDS.AVALANCHE]: AVAX_SWAPS_TOKEN_OBJECT,\n [CHAIN_IDS.OPTIMISM]: OPTIMISM_SWAPS_TOKEN_OBJECT,\n [CHAIN_IDS.ARBITRUM]: ARBITRUM_SWAPS_TOKEN_OBJECT,\n [CHAIN_IDS.ZKSYNC_ERA]: ZKSYNC_ERA_SWAPS_TOKEN_OBJECT,\n} as const;\n\nexport const SWAP_TRANSACTION_TYPES = [\n TransactionType.swap,\n TransactionType.swapApproval,\n];\n\n/**\n * Updates the transaction meta object with the swap information\n *\n * @param transactionMeta - The transaction meta object to update\n * @param transactionType - The type of the transaction\n * @param swaps - The swaps object\n * @param swaps.hasApproveTx - Whether the swap has an approval transaction\n * @param swaps.meta - The swap meta object\n * @param updateSwapsTransactionRequest - Dependency bag\n * @param updateSwapsTransactionRequest.isSwapsDisabled - Whether swaps are disabled\n * @param updateSwapsTransactionRequest.cancelTransaction - Function to cancel a transaction\n * @param updateSwapsTransactionRequest.messenger - TransactionController messenger\n * @returns A copy of the transaction meta object with updates, or the same\n * transaction meta object if no updates were made.\n */\nexport function updateSwapsTransaction(\n transactionMeta: TransactionMeta,\n transactionType: TransactionType,\n swaps: {\n hasApproveTx?: boolean;\n meta?: Partial<TransactionMeta>;\n },\n {\n isSwapsDisabled,\n cancelTransaction,\n messenger,\n }: {\n isSwapsDisabled: boolean;\n cancelTransaction: (transactionId: string) => void;\n messenger: TransactionControllerMessenger;\n },\n): TransactionMeta {\n if (isSwapsDisabled || !SWAP_TRANSACTION_TYPES.includes(transactionType)) {\n return transactionMeta;\n }\n\n // The simulationFails property is added if the estimateGas call fails. In cases\n // when no swaps approval tx is required, this indicates that the swap will likely\n // fail. There was an earlier estimateGas call made by the swaps controller,\n // but it is possible that external conditions have change since then, and\n // a previously succeeding estimate gas call could now fail. By checking for\n // the `simulationFails` property here, we can reduce the number of swap\n // transactions that get published to the blockchain only to fail and thereby\n // waste the user's funds on gas.\n if (\n transactionType === TransactionType.swap &&\n swaps?.hasApproveTx === false &&\n transactionMeta.simulationFails\n ) {\n cancelTransaction(transactionMeta.id);\n throw new Error('Simulation failed');\n }\n\n const swapsMeta = swaps?.meta as Partial<TransactionMeta>;\n\n if (!swapsMeta) {\n return transactionMeta;\n }\n\n let updatedTransactionMeta = transactionMeta;\n\n if (transactionType === TransactionType.swapApproval) {\n updatedTransactionMeta = updateSwapApprovalTransaction(\n transactionMeta,\n swapsMeta,\n );\n messenger.publish('TransactionController:transactionNewSwapApproval', {\n transactionMeta: updatedTransactionMeta,\n });\n }\n\n if (transactionType === TransactionType.swap) {\n updatedTransactionMeta = updateSwapTransaction(transactionMeta, swapsMeta);\n messenger.publish('TransactionController:transactionNewSwap', {\n transactionMeta: updatedTransactionMeta,\n });\n }\n\n return updatedTransactionMeta;\n}\n\n/**\n * Attempts to update the post transaction balance of the provided transaction\n *\n * @param transactionMeta - Transaction meta object to update\n * @param updatePostTransactionBalanceRequest - Dependency bag\n * @param updatePostTransactionBalanceRequest.ethQuery - EthQuery object\n * @param updatePostTransactionBalanceRequest.getTransaction - Reading function for the latest transaction state\n * @param updatePostTransactionBalanceRequest.updateTransaction - Updating transaction function\n */\nexport async function updatePostTransactionBalance(\n transactionMeta: TransactionMeta,\n {\n ethQuery,\n getTransaction,\n updateTransaction,\n }: {\n ethQuery: EthQuery;\n getTransaction: (transactionId: string) => TransactionMeta | undefined;\n updateTransaction: (transactionMeta: TransactionMeta, note: string) => void;\n },\n): Promise<{\n updatedTransactionMeta: TransactionMeta;\n approvalTransactionMeta?: TransactionMeta;\n}> {\n log('Updating post transaction balance', transactionMeta.id);\n\n const transactionId = transactionMeta.id;\n let latestTransactionMeta: TransactionMeta | undefined;\n let approvalTransactionMeta;\n\n for (let i = 0; i < UPDATE_POST_TX_BALANCE_ATTEMPTS; i++) {\n log('Querying balance', { attempt: i });\n\n const postTransactionBalance = await query(ethQuery, 'getBalance', [\n transactionMeta.txParams.from,\n ]);\n\n latestTransactionMeta = {\n ...(getTransaction(transactionId) ?? ({} as TransactionMeta)),\n };\n\n approvalTransactionMeta = latestTransactionMeta.approvalTxId\n ? getTransaction(latestTransactionMeta.approvalTxId)\n : undefined;\n\n latestTransactionMeta.postTxBalance = postTransactionBalance.toString(16);\n\n const isDefaultTokenAddress = isSwapsDefaultTokenAddress(\n transactionMeta.destinationTokenAddress as string,\n transactionMeta.chainId,\n );\n\n if (\n !isDefaultTokenAddress ||\n transactionMeta.preTxBalance !== latestTransactionMeta.postTxBalance\n ) {\n log('Finishing post balance update', {\n isDefaultTokenAddress,\n preTxBalance: transactionMeta.preTxBalance,\n postTxBalance: latestTransactionMeta.postTxBalance,\n });\n\n break;\n }\n\n log('Waiting for balance to update', {\n delay: UPDATE_POST_TX_BALANCE_TIMEOUT,\n });\n\n await sleep(UPDATE_POST_TX_BALANCE_TIMEOUT);\n }\n\n updateTransaction(\n latestTransactionMeta as TransactionMeta,\n 'TransactionController#updatePostTransactionBalance - Add post transaction balance',\n );\n\n log('Completed post balance update', latestTransactionMeta?.postTxBalance);\n\n return {\n updatedTransactionMeta: latestTransactionMeta as TransactionMeta,\n approvalTransactionMeta,\n };\n}\n\n/**\n * Updates the transaction meta object with the swap information\n *\n * @param transactionMeta - Transaction meta object to update\n * @param propsToUpdate - Properties to update\n * @param propsToUpdate.sourceTokenSymbol - Symbol of the token to be swapped\n * @param propsToUpdate.destinationTokenSymbol - Symbol of the token to be received\n * @param propsToUpdate.type - Type of the transaction\n * @param propsToUpdate.destinationTokenDecimals - Decimals of the token to be received\n * @param propsToUpdate.destinationTokenAddress - Address of the token to be received\n * @param propsToUpdate.swapMetaData - Metadata of the swap\n * @param propsToUpdate.swapTokenValue - Value of the token to be swapped\n * @param propsToUpdate.estimatedBaseFee - Estimated base fee of the transaction\n * @param propsToUpdate.approvalTxId - Transaction id of the approval transaction\n * @returns The updated transaction meta object.\n */\nfunction updateSwapTransaction(\n transactionMeta: TransactionMeta,\n {\n sourceTokenSymbol,\n destinationTokenSymbol,\n type,\n destinationTokenDecimals,\n destinationTokenAddress,\n swapMetaData,\n swapTokenValue,\n estimatedBaseFee,\n approvalTxId,\n }: Partial<TransactionMeta>,\n): TransactionMeta {\n validateIfTransactionUnapproved(transactionMeta, 'updateSwapTransaction');\n\n let swapTransaction = {\n sourceTokenSymbol,\n destinationTokenSymbol,\n type,\n destinationTokenDecimals,\n destinationTokenAddress,\n swapMetaData,\n swapTokenValue,\n estimatedBaseFee,\n approvalTxId,\n };\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n swapTransaction = pickBy(swapTransaction) as any;\n\n return merge({}, transactionMeta, swapTransaction);\n}\n\n/**\n * Updates the transaction meta object with the swap approval information\n *\n * @param transactionMeta - Transaction meta object to update\n * @param propsToUpdate - Properties to update\n * @param propsToUpdate.type - Type of the transaction\n * @param propsToUpdate.sourceTokenSymbol - Symbol of the token to be swapped\n * @returns The updated transaction meta object.\n */\nfunction updateSwapApprovalTransaction(\n transactionMeta: TransactionMeta,\n { type, sourceTokenSymbol }: Partial<TransactionMeta>,\n): TransactionMeta {\n validateIfTransactionUnapproved(\n transactionMeta,\n 'updateSwapApprovalTransaction',\n );\n\n // TODO: Replace `any` with type\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let swapApprovalTransaction = { type, sourceTokenSymbol } as any;\n swapApprovalTransaction = pickBy({\n type,\n sourceTokenSymbol,\n }) as Partial<TransactionMeta>;\n\n return merge({}, transactionMeta, swapApprovalTransaction);\n}\n\n/**\n * Checks whether the provided address is strictly equal to the address for\n * the default swaps token of the provided chain.\n *\n * @param address - The string to compare to the default token address\n * @param chainId - The hex encoded chain ID of the default swaps token to check\n * @returns Whether the address is the provided chain's default token address\n */\nfunction isSwapsDefaultTokenAddress(address: string, chainId: string) {\n if (!address || !chainId) {\n return false;\n }\n\n return (\n address ===\n SWAPS_CHAINID_DEFAULT_TOKEN_MAP[\n chainId as keyof typeof SWAPS_CHAINID_DEFAULT_TOKEN_MAP\n ]?.address\n );\n}\n\n/**\n * Sleeps for the provided number of milliseconds\n *\n * @param ms - Number of milliseconds to sleep\n * @returns Promise that resolves after the provided number of milliseconds\n */\nfunction sleep(ms: number) {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,46 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
+
3
+ var _chunkRI6MVJJNjs = require('./chunk-RI6MVJJN.js');
4
+
5
+
6
+ var _chunkS6VGOPUYjs = require('./chunk-S6VGOPUY.js');
7
+
8
+ // src/utils/simulation-api.ts
9
+ var _providers = require('@ethersproject/providers');
10
+ var _utils = require('@metamask/utils');
11
+ var log = _utils.createModuleLogger.call(void 0, _chunkS6VGOPUYjs.projectLogger, "simulation-api");
12
+ var RPC_METHOD = "infura_simulateTransactions";
13
+ var BASE_URL = "https://tx-sentinel-{0}.api.cx.metamask.io/";
14
+ var SUBDOMAIN_BY_CHAIN_ID = {
15
+ [_chunkRI6MVJJNjs.CHAIN_IDS.MAINNET]: "ethereum-mainnet",
16
+ [_chunkRI6MVJJNjs.CHAIN_IDS.GOERLI]: "ethereum-goerli",
17
+ [_chunkRI6MVJJNjs.CHAIN_IDS.SEPOLIA]: "ethereum-sepolia",
18
+ [_chunkRI6MVJJNjs.CHAIN_IDS.LINEA_MAINNET]: "linea-mainnet",
19
+ [_chunkRI6MVJJNjs.CHAIN_IDS.LINEA_GOERLI]: "linea-goerli",
20
+ [_chunkRI6MVJJNjs.CHAIN_IDS.ARBITRUM]: "arbitrum-mainnet",
21
+ [_chunkRI6MVJJNjs.CHAIN_IDS.AVALANCHE]: "avalanche-mainnet",
22
+ [_chunkRI6MVJJNjs.CHAIN_IDS.OPTIMISM]: "optimism-mainnet",
23
+ [_chunkRI6MVJJNjs.CHAIN_IDS.POLYGON]: "polygon-mainnet",
24
+ [_chunkRI6MVJJNjs.CHAIN_IDS.BSC]: "bsc-mainnet"
25
+ };
26
+ async function simulateTransactions(chainId, request) {
27
+ const url = getUrl(chainId);
28
+ log("Sending request", url, request);
29
+ const jsonRpc = new (0, _providers.JsonRpcProvider)(url);
30
+ const response = await jsonRpc.send(RPC_METHOD, [request]);
31
+ log("Received response", response);
32
+ return response;
33
+ }
34
+ function getUrl(chainId) {
35
+ const subdomain = SUBDOMAIN_BY_CHAIN_ID[chainId];
36
+ if (!subdomain) {
37
+ log("Chain is not supported", chainId);
38
+ throw new Error(`Chain is not supported: ${chainId}`);
39
+ }
40
+ return BASE_URL.replace("{0}", subdomain);
41
+ }
42
+
43
+
44
+
45
+ exports.simulateTransactions = simulateTransactions;
46
+ //# sourceMappingURL=chunk-FDJPXQTF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/simulation-api.ts"],"names":[],"mappings":";;;;;;;;AAAA,SAAS,uBAAuB;AAChC,SAAS,0BAAoC;AAK7C,IAAM,MAAM,mBAAmB,eAAe,gBAAgB;AAE9D,IAAM,aAAa;AACnB,IAAM,WAAW;AAEjB,IAAM,wBAA6C;AAAA,EACjD,CAAC,UAAU,OAAO,GAAG;AAAA,EACrB,CAAC,UAAU,MAAM,GAAG;AAAA,EACpB,CAAC,UAAU,OAAO,GAAG;AAAA,EACrB,CAAC,UAAU,aAAa,GAAG;AAAA,EAC3B,CAAC,UAAU,YAAY,GAAG;AAAA,EAC1B,CAAC,UAAU,QAAQ,GAAG;AAAA,EACtB,CAAC,UAAU,SAAS,GAAG;AAAA,EACvB,CAAC,UAAU,QAAQ,GAAG;AAAA,EACtB,CAAC,UAAU,OAAO,GAAG;AAAA,EACrB,CAAC,UAAU,GAAG,GAAG;AACnB;AAuHA,eAAsB,qBACpB,SACA,SAC6B;AAC7B,QAAM,MAAM,OAAO,OAAO;AAE1B,MAAI,mBAAmB,KAAK,OAAO;AAEnC,QAAM,UAAU,IAAI,gBAAgB,GAAG;AACvC,QAAM,WAAW,MAAM,QAAQ,KAAK,YAAY,CAAC,OAAO,CAAC;AAEzD,MAAI,qBAAqB,QAAQ;AAEjC,SAAO;AACT;AAOA,SAAS,OAAO,SAAsB;AACpC,QAAM,YAAY,sBAAsB,OAAO;AAE/C,MAAI,CAAC,WAAW;AACd,QAAI,0BAA0B,OAAO;AACrC,UAAM,IAAI,MAAM,2BAA2B,OAAO,EAAE;AAAA,EACtD;AAEA,SAAO,SAAS,QAAQ,OAAO,SAAS;AAC1C","sourcesContent":["import { JsonRpcProvider } from '@ethersproject/providers';\nimport { createModuleLogger, type Hex } from '@metamask/utils';\n\nimport { CHAIN_IDS } from '../constants';\nimport { projectLogger } from '../logger';\n\nconst log = createModuleLogger(projectLogger, 'simulation-api');\n\nconst RPC_METHOD = 'infura_simulateTransactions';\nconst BASE_URL = 'https://tx-sentinel-{0}.api.cx.metamask.io/';\n\nconst SUBDOMAIN_BY_CHAIN_ID: Record<Hex, string> = {\n [CHAIN_IDS.MAINNET]: 'ethereum-mainnet',\n [CHAIN_IDS.GOERLI]: 'ethereum-goerli',\n [CHAIN_IDS.SEPOLIA]: 'ethereum-sepolia',\n [CHAIN_IDS.LINEA_MAINNET]: 'linea-mainnet',\n [CHAIN_IDS.LINEA_GOERLI]: 'linea-goerli',\n [CHAIN_IDS.ARBITRUM]: 'arbitrum-mainnet',\n [CHAIN_IDS.AVALANCHE]: 'avalanche-mainnet',\n [CHAIN_IDS.OPTIMISM]: 'optimism-mainnet',\n [CHAIN_IDS.POLYGON]: 'polygon-mainnet',\n [CHAIN_IDS.BSC]: 'bsc-mainnet',\n};\n\n/** Single transaction to simulate in a simulation API request. */\nexport type SimulationRequestTransaction = {\n /** Sender of the transaction. */\n from: Hex;\n\n /** Recipient of the transaction. */\n to?: Hex;\n\n /** Value to send with the transaction. */\n value?: Hex;\n\n /** Data to send with the transaction. */\n data?: Hex;\n};\n\n/** Request to the simulation API to simulate transactions. */\nexport type SimulationRequest = {\n /**\n * Transactions to be sequentially simulated.\n * State changes impact subsequent transactions in the list.\n */\n transactions: SimulationRequestTransaction[];\n\n /**\n * Overrides to the state of the blockchain, keyed by smart contract address.\n */\n overrides?: {\n [address: Hex]: {\n /** Overrides to the storage slots for a smart contract account. */\n stateDiff: {\n [slot: Hex]: Hex;\n };\n };\n };\n\n /**\n * Whether to include call traces in the response.\n * Defaults to false.\n */\n withCallTrace?: boolean;\n\n /**\n * Whether to include event logs in the response.\n * Defaults to false.\n */\n withLogs?: boolean;\n};\n\n/** Raw event log emitted by a simulated transaction. */\nexport type SimulationResponseLog = {\n /** Address of the account that created the event. */\n address: Hex;\n\n /** Raw data in the event that is not indexed. */\n data: Hex;\n\n /** Raw indexed data from the event. */\n topics: Hex[];\n};\n\n/** Call trace of a single simulated transaction. */\nexport type SimulationResponseCallTrace = {\n /** Nested calls. */\n calls: SimulationResponseCallTrace[];\n\n /** Raw event logs created by the call. */\n logs: SimulationResponseLog[];\n};\n\n/**\n * Changes to the blockchain state.\n * Keyed by account address.\n */\nexport type SimulationResponseStateDiff = {\n [address: Hex]: {\n /** Native balance of the account. */\n balance?: Hex;\n\n /** Nonce of the account. */\n nonce?: Hex;\n\n /** Storage values per slot. */\n storage?: {\n [slot: Hex]: Hex;\n };\n };\n};\n\n/** Response from the simulation API for a single transaction. */\nexport type SimulationResponseTransaction = {\n /** Return value of the transaction, such as the balance if calling balanceOf. */\n return: Hex;\n\n /** Hierarchy of call data including nested calls and logs. */\n callTrace: SimulationResponseCallTrace;\n\n /** Changes to the blockchain state. */\n stateDiff: {\n /** Initial blockchain state before the transaction. */\n pre: SimulationResponseStateDiff;\n\n /** Updated blockchain state after the transaction. */\n post: SimulationResponseStateDiff;\n };\n};\n\n/** Response from the simulation API. */\nexport type SimulationResponse = {\n /** Simulation data for each transaction in the request. */\n transactions: SimulationResponseTransaction[];\n};\n\n/**\n * Simulate transactions using the transaction simulation API.\n * @param chainId - The chain ID to simulate transactions on.\n * @param request - The request to simulate transactions.\n */\nexport async function simulateTransactions(\n chainId: Hex,\n request: SimulationRequest,\n): Promise<SimulationResponse> {\n const url = getUrl(chainId);\n\n log('Sending request', url, request);\n\n const jsonRpc = new JsonRpcProvider(url);\n const response = await jsonRpc.send(RPC_METHOD, [request]);\n\n log('Received response', response);\n\n return response;\n}\n\n/**\n * Get the URL for the transaction simulation API.\n * @param chainId - The chain ID to get the URL for.\n * @returns The URL for the transaction simulation API.\n */\nfunction getUrl(chainId: Hex): string {\n const subdomain = SUBDOMAIN_BY_CHAIN_ID[chainId];\n\n if (!subdomain) {\n log('Chain is not supported', chainId);\n throw new Error(`Chain is not supported: ${chainId}`);\n }\n\n return BASE_URL.replace('{0}', subdomain);\n}\n"]}
@@ -0,0 +1,40 @@
1
+ // src/utils/external-transactions.ts
2
+ import { rpcErrors } from "@metamask/rpc-errors";
3
+ function validateConfirmedExternalTransaction(transactionMeta, confirmedTxs, pendingTxs) {
4
+ if (!transactionMeta || !transactionMeta.txParams) {
5
+ throw rpcErrors.invalidParams(
6
+ '"transactionMeta" or "transactionMeta.txParams" is missing'
7
+ );
8
+ }
9
+ if (transactionMeta.status !== "confirmed" /* confirmed */) {
10
+ throw rpcErrors.invalidParams(
11
+ 'External transaction status should be "confirmed"'
12
+ );
13
+ }
14
+ const externalTxNonce = transactionMeta.txParams.nonce;
15
+ if (pendingTxs && pendingTxs.length > 0) {
16
+ const foundPendingTxByNonce = pendingTxs.find(
17
+ (tx) => tx.txParams?.nonce === externalTxNonce
18
+ );
19
+ if (foundPendingTxByNonce) {
20
+ throw rpcErrors.invalidParams(
21
+ "External transaction nonce should not be in pending txs"
22
+ );
23
+ }
24
+ }
25
+ if (confirmedTxs && confirmedTxs.length > 0) {
26
+ const foundConfirmedTxByNonce = confirmedTxs.find(
27
+ (tx) => tx.txParams?.nonce === externalTxNonce
28
+ );
29
+ if (foundConfirmedTxByNonce) {
30
+ throw rpcErrors.invalidParams(
31
+ "External transaction nonce should not be in confirmed txs"
32
+ );
33
+ }
34
+ }
35
+ }
36
+
37
+ export {
38
+ validateConfirmedExternalTransaction
39
+ };
40
+ //# sourceMappingURL=chunk-FRKQ3Z2L.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/external-transactions.ts"],"sourcesContent":["// These utility functions are exclusively used by `confirmExternalTransaction` method in controller\nimport { rpcErrors } from '@metamask/rpc-errors';\n\nimport { TransactionStatus } from '../types';\nimport type { TransactionMeta } from '../types';\n\n/**\n * Validates the external provided transaction meta.\n *\n * @param transactionMeta - The transaction meta to validate.\n * @param confirmedTxs - The confirmed transactions in controller state.\n * @param pendingTxs - The submitted transactions in controller state.\n */\nexport function validateConfirmedExternalTransaction(\n transactionMeta?: TransactionMeta,\n confirmedTxs?: TransactionMeta[],\n pendingTxs?: TransactionMeta[],\n) {\n if (!transactionMeta || !transactionMeta.txParams) {\n throw rpcErrors.invalidParams(\n '\"transactionMeta\" or \"transactionMeta.txParams\" is missing',\n );\n }\n\n if (transactionMeta.status !== TransactionStatus.confirmed) {\n throw rpcErrors.invalidParams(\n 'External transaction status should be \"confirmed\"',\n );\n }\n\n const externalTxNonce = transactionMeta.txParams.nonce;\n if (pendingTxs && pendingTxs.length > 0) {\n const foundPendingTxByNonce = pendingTxs.find(\n (tx) => tx.txParams?.nonce === externalTxNonce,\n );\n if (foundPendingTxByNonce) {\n throw rpcErrors.invalidParams(\n 'External transaction nonce should not be in pending txs',\n );\n }\n }\n\n if (confirmedTxs && confirmedTxs.length > 0) {\n const foundConfirmedTxByNonce = confirmedTxs.find(\n (tx) => tx.txParams?.nonce === externalTxNonce,\n );\n if (foundConfirmedTxByNonce) {\n throw rpcErrors.invalidParams(\n 'External transaction nonce should not be in confirmed txs',\n );\n }\n }\n}\n"],"mappings":";AACA,SAAS,iBAAiB;AAYnB,SAAS,qCACd,iBACA,cACA,YACA;AACA,MAAI,CAAC,mBAAmB,CAAC,gBAAgB,UAAU;AACjD,UAAM,UAAU;AAAA,MACd;AAAA,IACF;AAAA,EACF;AAEA,MAAI,gBAAgB,wCAAwC;AAC1D,UAAM,UAAU;AAAA,MACd;AAAA,IACF;AAAA,EACF;AAEA,QAAM,kBAAkB,gBAAgB,SAAS;AACjD,MAAI,cAAc,WAAW,SAAS,GAAG;AACvC,UAAM,wBAAwB,WAAW;AAAA,MACvC,CAAC,OAAO,GAAG,UAAU,UAAU;AAAA,IACjC;AACA,QAAI,uBAAuB;AACzB,YAAM,UAAU;AAAA,QACd;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,MAAI,gBAAgB,aAAa,SAAS,GAAG;AAC3C,UAAM,0BAA0B,aAAa;AAAA,MAC3C,CAAC,OAAO,GAAG,UAAU,UAAU;AAAA,IACjC;AACA,QAAI,yBAAyB;AAC3B,YAAM,UAAU;AAAA,QACd;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":[]}