@paraswap/dex-lib 4.1.7 → 4.1.8

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 (294) hide show
  1. package/.idea/codeStyles/Project.xml +44 -4
  2. package/.idea/codeStyles/codeStyleConfig.xml +1 -1
  3. package/.idea/inspectionProfiles/Project_Default.xml +6 -0
  4. package/.idea/paraswap-dex-lib.iml +7 -4
  5. package/build/dex/angle-staked-stable/angle-staked-stable.js +2 -1
  6. package/build/dex/angle-staked-stable/angle-staked-stable.js.map +1 -1
  7. package/build/dex/angle-transmuter/angle-transmuter.js.map +1 -1
  8. package/build/dex/balancer-v1/config.js +2 -1
  9. package/build/dex/balancer-v1/config.js.map +1 -1
  10. package/build/dex/jarvis.d.ts +2 -1
  11. package/build/dex/jarvis.js +58 -0
  12. package/build/dex/jarvis.js.map +1 -1
  13. package/build/dex/weth/weth.js +2 -1
  14. package/build/dex/weth/weth.js.map +1 -1
  15. package/build/dex/zerox/index.d.ts +3 -25
  16. package/build/dex/zerox/index.js +50 -42
  17. package/build/dex/zerox/index.js.map +1 -1
  18. package/build/dex/zerox/types.d.ts +29 -0
  19. package/build/dex/zerox/types.js +8 -1
  20. package/build/dex/zerox/types.js.map +1 -1
  21. package/package.json +1 -1
  22. package/scripts/get-uniswap-v3-dexes.ts +64 -0
  23. package/src/dex/aave-gsm/aave-gsm-gas-estimation.test.ts +32 -0
  24. package/src/dex/aave-v2/aave-v2-gas-estimation.test.ts +28 -0
  25. package/src/dex/aave-v3/aave-v3-gas-estimation.test.ts +66 -0
  26. package/src/dex/aave-v3-stata/aave-v3-stata-gas-estimation.test.ts +41 -0
  27. package/src/dex/aave-v3-stata-v2/aave-v3-stata-v2-gas-estimation.test.ts +41 -0
  28. package/src/dex/angle-staked-stable/angle-staked-stable-gas-estimation.test.ts +29 -0
  29. package/src/dex/angle-staked-stable/angle-staked-stable.ts +2 -1
  30. package/src/dex/angle-transmuter/angle-transmuter-gas-estimation.test.ts +41 -0
  31. package/src/dex/angle-transmuter/angle-transmuter.ts +0 -1
  32. package/src/dex/balancer-v1/balancer-v1-gas-estimation.test.ts +29 -0
  33. package/src/dex/balancer-v1/config.ts +2 -1
  34. package/src/dex/balancer-v2/balancer-v2-gas-estimation.test.ts +98 -0
  35. package/src/dex/balancer-v3/balancer-v3-gas-estimation.test.ts +30 -0
  36. package/src/dex/bancor/bancor-gas-estimation.test.ts +30 -0
  37. package/src/dex/bebop/bebop-gas-estimation.test.ts +30 -0
  38. package/src/dex/concentrator-arusd/concentrator-arusd-gas-estimation.test.ts +29 -0
  39. package/src/dex/curve-v1/curve-v1-gas-estimation.test.ts +29 -0
  40. package/src/dex/curve-v2/curve-v2-gas-estimation.test.ts +29 -0
  41. package/src/dex/fluid-dex/fluid-dex-gas-estimation.test.ts +29 -0
  42. package/src/dex/fx-protocol-rusd/fx-protocol-rusd-gas-estimation.test.ts +29 -0
  43. package/src/dex/hashflow/hashflow-gas-estimation.test.ts +30 -0
  44. package/src/dex/idle-dao/idle-dao--gas-estimation.test.ts +30 -0
  45. package/src/dex/lido/lido-gas-estimation.test.ts +29 -0
  46. package/src/dex/lite-psm/lite-psm-gas-estimation.test.ts +34 -0
  47. package/src/dex/maker-psm/maker-psm-gas-estimation.test.ts +34 -0
  48. package/src/dex/maverick-v1/maverick-v1-gas-estimation.test.ts +29 -0
  49. package/src/dex/maverick-v2/maverick-v2-gas-estimation.test.ts +29 -0
  50. package/src/dex/pancakeswap-v3/pancakeswap-v3-gas-estimation.test.ts +29 -0
  51. package/src/dex/polygon-migrator/polygon-migrator-gas-estimation.test.ts +41 -0
  52. package/src/dex/sky-converter/sky-converter-gas-estimation.test.ts +78 -0
  53. package/src/dex/solidly/solidly-gas-estimation.test.ts +29 -0
  54. package/src/dex/solidly-v3/solidly-v3-gas-estimation.test.ts +29 -0
  55. package/src/dex/spark/spark-gas-estimation.test.ts +29 -0
  56. package/src/dex/stader/stader-gas-estimation.test.ts +29 -0
  57. package/src/dex/stkgho/stkgho-gas-estimation.test.ts +29 -0
  58. package/src/dex/swaap-v2/swaap-v2-estimation.test.ts +29 -0
  59. package/src/dex/swell/swell-estimation.test.ts +29 -0
  60. package/src/dex/trader-joe-v2.1/trader-joe-v2.1-gas-estimation.test.ts +29 -0
  61. package/src/dex/uniswap-v2/uniswap-v2-gas-estimation.test.ts +98 -0
  62. package/src/dex/uniswap-v3/uniswap-v3-gas-estimation.test.ts +98 -0
  63. package/src/dex/usual/usual-gas-estimation.test.ts +54 -0
  64. package/src/dex/usual-pp/usual-pp-gas-estimation.test.ts +29 -0
  65. package/src/dex/weth/weth-gas-estimation.test.ts +42 -0
  66. package/src/dex/weth/weth.ts +3 -1
  67. package/src/dex/wombat/wombat-gas-estimation.test.ts +29 -0
  68. package/src/dex/wsteth/wsteth-gas-estimation.test.ts +41 -0
  69. package/src/dex/wusdm/wusdm-gas-estimation.test.ts +41 -0
  70. package/tests/constants-e2e.ts +8 -0
  71. package/tests/tenderly-simulation-new.test.ts +151 -0
  72. package/tests/tenderly-simulation-new.ts +698 -0
  73. package/tests/token-storage-slots.json +532 -0
  74. package/tests/utils-e2e.ts +137 -1
  75. package/.idea/aws.xml +0 -17
  76. package/.idea/misc.xml +0 -6
  77. package/.idea/prettier.xml +0 -7
  78. package/build/abi/AaveV1_lending_pool.json +0 -27
  79. package/build/abi/integral/factory.json +0 -333
  80. package/build/abi/integral/oracle.json +0 -501
  81. package/build/abi/integral/pool.json +0 -1041
  82. package/build/abi/integral/relayer.json +0 -1536
  83. package/build/abi/platypus/asset.json +0 -490
  84. package/build/abi/platypus/avax-pool.json +0 -820
  85. package/build/abi/platypus/oracle.json +0 -119
  86. package/build/abi/platypus/pool.json +0 -677
  87. package/build/abi/smardex/all/smardex-router.json +0 -648
  88. package/build/abi/smardex/layer-1/smardex-factory.json +0 -242
  89. package/build/abi/smardex/layer-1/smardex-pool.json +0 -506
  90. package/build/abi/smardex/layer-2/smardex-factory.json +0 -185
  91. package/build/abi/smardex/layer-2/smardex-pool.json +0 -578
  92. package/build/abi/swaap-v1/pool.json +0 -1346
  93. package/build/dex/aave-v1/aave-v1.d.ts +0 -33
  94. package/build/dex/aave-v1/aave-v1.js +0 -158
  95. package/build/dex/aave-v1/aave-v1.js.map +0 -1
  96. package/build/dex/aave-v1/config.d.ts +0 -11
  97. package/build/dex/aave-v1/config.js +0 -20
  98. package/build/dex/aave-v1/config.js.map +0 -1
  99. package/build/dex/aave-v1/tokens-mainnet.json +0 -134
  100. package/build/dex/aave-v1/tokens.d.ts +0 -3
  101. package/build/dex/aave-v1/tokens.js +0 -50
  102. package/build/dex/aave-v1/tokens.js.map +0 -1
  103. package/build/dex/aave-v1/types.d.ts +0 -12
  104. package/build/dex/aave-v1/types.js +0 -3
  105. package/build/dex/aave-v1/types.js.map +0 -1
  106. package/build/dex/bProtocol.d.ts +0 -18
  107. package/build/dex/bProtocol.js +0 -39
  108. package/build/dex/bProtocol.js.map +0 -1
  109. package/build/dex/bancor.d.ts +0 -26
  110. package/build/dex/bancor.js +0 -58
  111. package/build/dex/bancor.js.map +0 -1
  112. package/build/dex/compound.d.ts +0 -18
  113. package/build/dex/compound.js +0 -46
  114. package/build/dex/compound.js.map +0 -1
  115. package/build/dex/curve-v2.d.ts +0 -58
  116. package/build/dex/curve-v2.js +0 -180
  117. package/build/dex/curve-v2.js.map +0 -1
  118. package/build/dex/dodo-v1.d.ts +0 -33
  119. package/build/dex/dodo-v1.js +0 -63
  120. package/build/dex/dodo-v1.js.map +0 -1
  121. package/build/dex/fluid-dex/fluid-dex-generate-pool.d.ts +0 -49
  122. package/build/dex/fluid-dex/fluid-dex-generate-pool.js +0 -104
  123. package/build/dex/fluid-dex/fluid-dex-generate-pool.js.map +0 -1
  124. package/build/dex/integral/config.d.ts +0 -4
  125. package/build/dex/integral/config.js +0 -20
  126. package/build/dex/integral/config.js.map +0 -1
  127. package/build/dex/integral/context.d.ts +0 -40
  128. package/build/dex/integral/context.js +0 -158
  129. package/build/dex/integral/context.js.map +0 -1
  130. package/build/dex/integral/integral-factory.d.ts +0 -24
  131. package/build/dex/integral/integral-factory.js +0 -95
  132. package/build/dex/integral/integral-factory.js.map +0 -1
  133. package/build/dex/integral/integral-pool.d.ts +0 -50
  134. package/build/dex/integral/integral-pool.js +0 -149
  135. package/build/dex/integral/integral-pool.js.map +0 -1
  136. package/build/dex/integral/integral-pricing.d.ts +0 -18
  137. package/build/dex/integral/integral-pricing.js +0 -114
  138. package/build/dex/integral/integral-pricing.js.map +0 -1
  139. package/build/dex/integral/integral-relayer.d.ts +0 -42
  140. package/build/dex/integral/integral-relayer.js +0 -192
  141. package/build/dex/integral/integral-relayer.js.map +0 -1
  142. package/build/dex/integral/integral-token.d.ts +0 -27
  143. package/build/dex/integral/integral-token.js +0 -59
  144. package/build/dex/integral/integral-token.js.map +0 -1
  145. package/build/dex/integral/integral.d.ts +0 -41
  146. package/build/dex/integral/integral.js +0 -376
  147. package/build/dex/integral/integral.js.map +0 -1
  148. package/build/dex/integral/types.d.ts +0 -85
  149. package/build/dex/integral/types.js +0 -9
  150. package/build/dex/integral/types.js.map +0 -1
  151. package/build/dex/integral/utils-e2e.d.ts +0 -9
  152. package/build/dex/integral/utils-e2e.js +0 -131
  153. package/build/dex/integral/utils-e2e.js.map +0 -1
  154. package/build/dex/integral/utils.d.ts +0 -17
  155. package/build/dex/integral/utils.js +0 -94
  156. package/build/dex/integral/utils.js.map +0 -1
  157. package/build/dex/lido.d.ts +0 -19
  158. package/build/dex/lido.js +0 -42
  159. package/build/dex/lido.js.map +0 -1
  160. package/build/dex/m-token/config.d.ts +0 -3
  161. package/build/dex/m-token/config.js +0 -19
  162. package/build/dex/m-token/config.js.map +0 -1
  163. package/build/dex/m-token/m-token.d.ts +0 -29
  164. package/build/dex/m-token/m-token.js +0 -145
  165. package/build/dex/m-token/m-token.js.map +0 -1
  166. package/build/dex/m-token/m-wrapped-m.d.ts +0 -17
  167. package/build/dex/m-token/m-wrapped-m.js +0 -50
  168. package/build/dex/m-token/m-wrapped-m.js.map +0 -1
  169. package/build/dex/m-token/types.d.ts +0 -12
  170. package/build/dex/m-token/types.js +0 -3
  171. package/build/dex/m-token/types.js.map +0 -1
  172. package/build/dex/onebit.d.ts +0 -25
  173. package/build/dex/onebit.js +0 -42
  174. package/build/dex/onebit.js.map +0 -1
  175. package/build/dex/platypus/asset.d.ts +0 -14
  176. package/build/dex/platypus/asset.js +0 -97
  177. package/build/dex/platypus/asset.js.map +0 -1
  178. package/build/dex/platypus/config.d.ts +0 -11
  179. package/build/dex/platypus/config.js +0 -69
  180. package/build/dex/platypus/config.js.map +0 -1
  181. package/build/dex/platypus/platypus.d.ts +0 -62
  182. package/build/dex/platypus/platypus.js +0 -442
  183. package/build/dex/platypus/platypus.js.map +0 -1
  184. package/build/dex/platypus/pool-avax.d.ts +0 -8
  185. package/build/dex/platypus/pool-avax.js +0 -54
  186. package/build/dex/platypus/pool-avax.js.map +0 -1
  187. package/build/dex/platypus/pool-base.d.ts +0 -19
  188. package/build/dex/platypus/pool-base.js +0 -86
  189. package/build/dex/platypus/pool-base.js.map +0 -1
  190. package/build/dex/platypus/pool-pure.d.ts +0 -8
  191. package/build/dex/platypus/pool-pure.js +0 -37
  192. package/build/dex/platypus/pool-pure.js.map +0 -1
  193. package/build/dex/platypus/pool-subscriber.d.ts +0 -14
  194. package/build/dex/platypus/pool-subscriber.js +0 -167
  195. package/build/dex/platypus/pool-subscriber.js.map +0 -1
  196. package/build/dex/platypus/pool.d.ts +0 -8
  197. package/build/dex/platypus/pool.js +0 -58
  198. package/build/dex/platypus/pool.js.map +0 -1
  199. package/build/dex/platypus/types.d.ts +0 -92
  200. package/build/dex/platypus/types.js +0 -10
  201. package/build/dex/platypus/types.js.map +0 -1
  202. package/build/dex/quickswap/spiritswap-v3.d.ts +0 -6
  203. package/build/dex/quickswap/spiritswap-v3.js +0 -19
  204. package/build/dex/quickswap/spiritswap-v3.js.map +0 -1
  205. package/build/dex/smardex/config.d.ts +0 -4
  206. package/build/dex/smardex/config.js +0 -64
  207. package/build/dex/smardex/config.js.map +0 -1
  208. package/build/dex/smardex/constants.d.ts +0 -18
  209. package/build/dex/smardex/constants.js +0 -33
  210. package/build/dex/smardex/constants.js.map +0 -1
  211. package/build/dex/smardex/sdk/constants.d.ts +0 -6
  212. package/build/dex/smardex/sdk/constants.js +0 -13
  213. package/build/dex/smardex/sdk/constants.js.map +0 -1
  214. package/build/dex/smardex/sdk/core.d.ts +0 -113
  215. package/build/dex/smardex/sdk/core.js +0 -499
  216. package/build/dex/smardex/sdk/core.js.map +0 -1
  217. package/build/dex/smardex/sdk/errors.d.ts +0 -15
  218. package/build/dex/smardex/sdk/errors.js +0 -22
  219. package/build/dex/smardex/sdk/errors.js.map +0 -1
  220. package/build/dex/smardex/sdk/types.d.ts +0 -62
  221. package/build/dex/smardex/sdk/types.js +0 -3
  222. package/build/dex/smardex/sdk/types.js.map +0 -1
  223. package/build/dex/smardex/sdk/utils.d.ts +0 -44
  224. package/build/dex/smardex/sdk/utils.js +0 -133
  225. package/build/dex/smardex/sdk/utils.js.map +0 -1
  226. package/build/dex/smardex/smardex-event-pool.d.ts +0 -28
  227. package/build/dex/smardex/smardex-event-pool.js +0 -119
  228. package/build/dex/smardex/smardex-event-pool.js.map +0 -1
  229. package/build/dex/smardex/smardex.d.ts +0 -65
  230. package/build/dex/smardex/smardex.js +0 -519
  231. package/build/dex/smardex/smardex.js.map +0 -1
  232. package/build/dex/smardex/types.d.ts +0 -55
  233. package/build/dex/smardex/types.js +0 -3
  234. package/build/dex/smardex/types.js.map +0 -1
  235. package/build/dex/smoothy.d.ts +0 -26
  236. package/build/dex/smoothy.js +0 -48
  237. package/build/dex/smoothy.js.map +0 -1
  238. package/build/dex/solidly/forks-override/cone.d.ts +0 -22
  239. package/build/dex/solidly/forks-override/cone.js +0 -53
  240. package/build/dex/solidly/forks-override/cone.js.map +0 -1
  241. package/build/dex/solidly/forks-override/solisnek.d.ts +0 -8
  242. package/build/dex/solidly/forks-override/solisnek.js +0 -15
  243. package/build/dex/solidly/forks-override/solisnek.js.map +0 -1
  244. package/build/dex/stable-pool.d.ts +0 -28
  245. package/build/dex/stable-pool.js +0 -62
  246. package/build/dex/stable-pool.js.map +0 -1
  247. package/build/dex/swaap-v1/config.d.ts +0 -17
  248. package/build/dex/swaap-v1/config.js +0 -56
  249. package/build/dex/swaap-v1/config.js.map +0 -1
  250. package/build/dex/swaap-v1/constants.d.ts +0 -6
  251. package/build/dex/swaap-v1/constants.js +0 -17
  252. package/build/dex/swaap-v1/constants.js.map +0 -1
  253. package/build/dex/swaap-v1/libraries/ChainlinkUtils.d.ts +0 -25
  254. package/build/dex/swaap-v1/libraries/ChainlinkUtils.js +0 -62
  255. package/build/dex/swaap-v1/libraries/ChainlinkUtils.js.map +0 -1
  256. package/build/dex/swaap-v1/libraries/Const.d.ts +0 -18
  257. package/build/dex/swaap-v1/libraries/Const.js +0 -26
  258. package/build/dex/swaap-v1/libraries/Const.js.map +0 -1
  259. package/build/dex/swaap-v1/libraries/GeometricBrownianMotion.d.ts +0 -77
  260. package/build/dex/swaap-v1/libraries/GeometricBrownianMotion.js +0 -316
  261. package/build/dex/swaap-v1/libraries/GeometricBrownianMotion.js.map +0 -1
  262. package/build/dex/swaap-v1/libraries/LogExpMath.d.ts +0 -75
  263. package/build/dex/swaap-v1/libraries/LogExpMath.js +0 -418
  264. package/build/dex/swaap-v1/libraries/LogExpMath.js.map +0 -1
  265. package/build/dex/swaap-v1/libraries/MathMMM.d.ts +0 -185
  266. package/build/dex/swaap-v1/libraries/MathMMM.js +0 -453
  267. package/build/dex/swaap-v1/libraries/MathMMM.js.map +0 -1
  268. package/build/dex/swaap-v1/libraries/Num.d.ts +0 -31
  269. package/build/dex/swaap-v1/libraries/Num.js +0 -167
  270. package/build/dex/swaap-v1/libraries/Num.js.map +0 -1
  271. package/build/dex/swaap-v1/libraries/PoolQuotations.d.ts +0 -23
  272. package/build/dex/swaap-v1/libraries/PoolQuotations.js +0 -131
  273. package/build/dex/swaap-v1/libraries/PoolQuotations.js.map +0 -1
  274. package/build/dex/swaap-v1/libraries/Struct.d.ts +0 -56
  275. package/build/dex/swaap-v1/libraries/Struct.js +0 -3
  276. package/build/dex/swaap-v1/libraries/Struct.js.map +0 -1
  277. package/build/dex/swaap-v1/swaap-v1-pool.d.ts +0 -45
  278. package/build/dex/swaap-v1/swaap-v1-pool.js +0 -656
  279. package/build/dex/swaap-v1/swaap-v1-pool.js.map +0 -1
  280. package/build/dex/swaap-v1/swaap-v1.d.ts +0 -61
  281. package/build/dex/swaap-v1/swaap-v1.js +0 -369
  282. package/build/dex/swaap-v1/swaap-v1.js.map +0 -1
  283. package/build/dex/swaap-v1/types.d.ts +0 -106
  284. package/build/dex/swaap-v1/types.js +0 -9
  285. package/build/dex/swaap-v1/types.js.map +0 -1
  286. package/build/dex/trader-joe-v2.1/optimizer.d.ts +0 -2
  287. package/build/dex/trader-joe-v2.1/optimizer.js +0 -47
  288. package/build/dex/trader-joe-v2.1/optimizer.js.map +0 -1
  289. package/build/dex/trader-joe-v2.1/trader-joe-v2.1-new.d.ts +0 -7
  290. package/build/dex/trader-joe-v2.1/trader-joe-v2.1-new.js +0 -20
  291. package/build/dex/trader-joe-v2.1/trader-joe-v2.1-new.js.map +0 -1
  292. package/build/dex/trader-joe-v2.1.d.ts +0 -43
  293. package/build/dex/trader-joe-v2.1.js +0 -79
  294. package/build/dex/trader-joe-v2.1.js.map +0 -1
@@ -0,0 +1,698 @@
1
+ /* eslint-disable no-console */
2
+ import 'dotenv/config';
3
+ import axios from 'axios';
4
+ import { ethers } from 'ethers';
5
+ import * as fs from 'fs/promises';
6
+ import * as path from 'path';
7
+ import { ETHER_ADDRESS } from '../src/constants';
8
+
9
+ const TENDERLY_TOKEN = process.env.TENDERLY_TOKEN!;
10
+ const TENDERLY_ACCOUNT_ID = process.env.TENDERLY_ACCOUNT_ID!;
11
+ const TENDERLY_PROJECT = process.env.TENDERLY_PROJECT!;
12
+
13
+ interface StateObject {
14
+ // Overrides of storage slots.
15
+ // In this mapping, the key is variable storage slot,
16
+ // and the value contains the override.
17
+ storage?: Record<string, string>; // storage slot -> value
18
+ balance?: string;
19
+ nonce?: number;
20
+ // Bytecode that will override the code associated to the given account.
21
+ code?: string;
22
+ }
23
+ export type StateOverride = Record<string, StateObject>; // contract -> storage override
24
+
25
+ interface TokenStorageSlots {
26
+ balanceSlot: string;
27
+ allowanceSlot: string;
28
+ isVyper?: boolean;
29
+ }
30
+
31
+ interface SimulateTransactionRequest {
32
+ from: string | null;
33
+ to: string | null;
34
+ value?: string;
35
+ data: string;
36
+ chainId: number;
37
+ timestamp?: number;
38
+ blockNumber?: number;
39
+ stateOverride?: StateOverride;
40
+ }
41
+
42
+ // not fully complete
43
+ interface SimulatedTransactionCall {
44
+ hash: string;
45
+ contract_name: string;
46
+ function_name: string;
47
+ function_pc: number;
48
+ function_op: string;
49
+ function_file_index: number;
50
+ function_code_start: number;
51
+ function_line_number: number;
52
+ function_code_length: number;
53
+ absolute_position: number;
54
+ caller_pc: number;
55
+ caller_op: string;
56
+ call_type: string;
57
+ address: string;
58
+ from: string;
59
+ from_balance: string;
60
+ to: string;
61
+ to_balance: string;
62
+ value: string | null;
63
+ caller: {
64
+ address: string;
65
+ balance: string;
66
+ };
67
+ block_timestamp: string;
68
+ gas: number;
69
+ gas_used: number;
70
+ intrinsic_gas: number;
71
+ storage_address: string;
72
+ input: string;
73
+ storage_slot: string[] | undefined;
74
+ calls: SimulatedTransactionCall[] | null;
75
+ }
76
+
77
+ // not fully complete
78
+ interface Simulation {
79
+ id: string;
80
+ project_id: string;
81
+ owner_id: string;
82
+ network_id: string;
83
+ block_number: number;
84
+ transaction_index: number;
85
+ from: string;
86
+ to: string;
87
+ input: string;
88
+ gas: number;
89
+ gas_price: string;
90
+ gas_used: number;
91
+ value: string;
92
+ method: string;
93
+ status: boolean;
94
+ }
95
+
96
+ // not complete, all details include a lot more info
97
+ interface SimulatedTransactionDetails {
98
+ transaction: {
99
+ hash: string;
100
+ block_hash: string;
101
+ block_number: number;
102
+ from: string;
103
+ gas: number;
104
+ gas_price: number;
105
+ gas_fee_cap: number;
106
+ gas_tip_cap: number;
107
+ cumulative_gas_used: number;
108
+ gas_used: number;
109
+ effective_gas_price: number;
110
+ input: string;
111
+ nonce: number;
112
+ to: string;
113
+ index: number;
114
+ value: string;
115
+ access_list: null;
116
+ status: boolean;
117
+ transaction_info: {
118
+ call_trace: SimulatedTransactionCall;
119
+ };
120
+ };
121
+ }
122
+
123
+ class TokenStorageSlotsCache {
124
+ private static TOKEN_STORAGE_SLOTS: Record<
125
+ number,
126
+ Record<string, TokenStorageSlots>
127
+ > | null = null;
128
+ private static readonly TOKEN_STORAGE_SLOTS_FILEPATH = path.join(
129
+ __dirname,
130
+ 'token-storage-slots.json',
131
+ );
132
+
133
+ private static async loadTokenStorageSlots(): Promise<
134
+ Record<number, Record<string, TokenStorageSlots>>
135
+ > {
136
+ return JSON.parse(
137
+ await fs.readFile(TokenStorageSlotsCache.TOKEN_STORAGE_SLOTS_FILEPATH, {
138
+ encoding: 'utf-8',
139
+ }),
140
+ );
141
+ }
142
+
143
+ private static async saveTokenStorageSlots(): Promise<void> {
144
+ await fs.writeFile(
145
+ TokenStorageSlotsCache.TOKEN_STORAGE_SLOTS_FILEPATH,
146
+ JSON.stringify(TokenStorageSlotsCache.TOKEN_STORAGE_SLOTS, null, 2),
147
+ { encoding: 'utf-8' },
148
+ );
149
+ }
150
+
151
+ static async getTokenStorageSlots(
152
+ chainId: number,
153
+ token: string,
154
+ ): Promise<TokenStorageSlots | null> {
155
+ if (!TokenStorageSlotsCache.TOKEN_STORAGE_SLOTS) {
156
+ TokenStorageSlotsCache.TOKEN_STORAGE_SLOTS =
157
+ await TokenStorageSlotsCache.loadTokenStorageSlots();
158
+ }
159
+
160
+ return (
161
+ TokenStorageSlotsCache.TOKEN_STORAGE_SLOTS[chainId]?.[
162
+ token.toLowerCase()
163
+ ] ?? null
164
+ );
165
+ }
166
+
167
+ static async setTokenStorageSlots(
168
+ chainId: number,
169
+ token: string,
170
+ slots: TokenStorageSlots,
171
+ ): Promise<void> {
172
+ if (!TokenStorageSlotsCache.TOKEN_STORAGE_SLOTS) {
173
+ TokenStorageSlotsCache.TOKEN_STORAGE_SLOTS =
174
+ await TokenStorageSlotsCache.loadTokenStorageSlots();
175
+ }
176
+
177
+ TokenStorageSlotsCache.TOKEN_STORAGE_SLOTS ||= {};
178
+ TokenStorageSlotsCache.TOKEN_STORAGE_SLOTS[chainId] ||= {};
179
+ TokenStorageSlotsCache.TOKEN_STORAGE_SLOTS[chainId][token.toLowerCase()] =
180
+ slots;
181
+
182
+ void TokenStorageSlotsCache.saveTokenStorageSlots();
183
+ }
184
+ }
185
+
186
+ export class TenderlySimulatorNew {
187
+ // public constants
188
+ static readonly DEFAULT_OWNER = '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
189
+ static readonly DEFAULT_SPENDER =
190
+ '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb';
191
+
192
+ // singleton
193
+ private static instance: TenderlySimulatorNew;
194
+
195
+ private constructor() {}
196
+
197
+ public static getInstance(): TenderlySimulatorNew {
198
+ if (!TenderlySimulatorNew.instance) {
199
+ TenderlySimulatorNew.instance = new TenderlySimulatorNew();
200
+ }
201
+
202
+ return TenderlySimulatorNew.instance;
203
+ }
204
+
205
+ public async simulateTransaction(
206
+ request: SimulateTransactionRequest,
207
+ ): Promise<Simulation> {
208
+ const data = {
209
+ network_id: request.chainId,
210
+ from: request.from,
211
+ to: request.to,
212
+ input: request.data,
213
+ value: request.value,
214
+ save: true,
215
+ save_if_fails: true,
216
+ state_objects: request.stateOverride,
217
+ };
218
+
219
+ console.log('Sending transaction simulation with params:');
220
+ console.log(JSON.stringify(data, null, 2));
221
+
222
+ const response = await axios.request({
223
+ method: 'POST',
224
+ url: `https://api.tenderly.co/api/v1/account/${TENDERLY_ACCOUNT_ID}/project/${TENDERLY_PROJECT}/simulate`,
225
+ headers: {
226
+ 'X-Access-Key': TENDERLY_TOKEN,
227
+ },
228
+ data,
229
+ });
230
+
231
+ const simulation = response.data.simulation as Simulation;
232
+ const url = `https://dashboard.tenderly.co/${TENDERLY_ACCOUNT_ID}/${TENDERLY_PROJECT}/simulator/${simulation.id}`;
233
+
234
+ console.log('Successfully simulated a transaction:');
235
+ console.log(`Simulation URL - ${url}`);
236
+
237
+ return simulation;
238
+ }
239
+
240
+ public async getSimulatedTransactionDetails(
241
+ id: string,
242
+ ): Promise<SimulatedTransactionDetails | null> {
243
+ try {
244
+ const { data } = await axios.post(
245
+ `https://api.tenderly.co/api/v1/account/${TENDERLY_ACCOUNT_ID}/project/${TENDERLY_PROJECT}/simulations/${id}`,
246
+ {},
247
+ { headers: { 'X-Access-Key': TENDERLY_TOKEN } },
248
+ );
249
+
250
+ return data;
251
+ } catch (e) {
252
+ console.error(e);
253
+ return null;
254
+ }
255
+ }
256
+
257
+ getSLOADCalls = (
258
+ callTrace: SimulatedTransactionCall,
259
+ ): SimulatedTransactionCall[] => {
260
+ const results: SimulatedTransactionCall[] = [];
261
+
262
+ if (callTrace.call_type === 'SLOAD') {
263
+ results.push(callTrace);
264
+ }
265
+
266
+ if (callTrace.calls) {
267
+ for (const call of callTrace.calls) {
268
+ results.push(...this.getSLOADCalls(call));
269
+ }
270
+ }
271
+
272
+ return results;
273
+ };
274
+
275
+ /**
276
+ *
277
+ * @param balanceOfSlot storage slot of `balanceOf` mapping
278
+ * @param owner account's address
279
+ * @param isVyper `true` if contract is written in Vyper
280
+ */
281
+ calculateAddressBalanceSlot(
282
+ balanceOfSlot: string,
283
+ owner: string,
284
+ isVyper = false,
285
+ ) {
286
+ return isVyper
287
+ ? this.calculateVyperAddressBalanceSlot(balanceOfSlot, owner)
288
+ : this.calculateSolidityAddressBalanceSlot(balanceOfSlot, owner);
289
+ }
290
+
291
+ /**
292
+ *
293
+ * @param balanceOfSlot storage slot of `balanceOf` mapping
294
+ * @param owner account's address
295
+ */
296
+ calculateSolidityAddressBalanceSlot(balanceOfSlot: string, owner: string) {
297
+ return ethers.utils.keccak256(
298
+ ethers.utils.concat([ethers.utils.hexZeroPad(owner, 32), balanceOfSlot]),
299
+ );
300
+ }
301
+
302
+ /**
303
+ *
304
+ * @param balanceOfSlot storage slot of `balanceOf` mapping
305
+ * @param owner account's address
306
+ */
307
+ calculateVyperAddressBalanceSlot(balanceOfSlot: string, owner: string) {
308
+ return ethers.utils.keccak256(
309
+ ethers.utils.concat([balanceOfSlot, ethers.utils.hexZeroPad(owner, 32)]),
310
+ );
311
+ }
312
+
313
+ /**
314
+ *
315
+ * @param allowanceSlot storage slot of `allowance` mapping
316
+ * @param owner account's address
317
+ * @param spender spender's address
318
+ * @param isVyper `true` if contract is written in Vyper
319
+ */
320
+ calculateAddressAllowanceSlot(
321
+ allowanceSlot: string,
322
+ owner: string,
323
+ spender: string,
324
+ isVyper = false,
325
+ ) {
326
+ return isVyper
327
+ ? this.calculateVyperAddressAllowanceSlot(allowanceSlot, owner, spender)
328
+ : this.calculateSolidityAddressAllowanceSlot(
329
+ allowanceSlot,
330
+ owner,
331
+ spender,
332
+ );
333
+ }
334
+
335
+ /**
336
+ *
337
+ * @param allowanceSlot storage slot of `allowance` mapping
338
+ * @param owner account's address
339
+ * @param spender spender's address
340
+ */
341
+ calculateSolidityAddressAllowanceSlot(
342
+ allowanceSlot: string,
343
+ owner: string,
344
+ spender: string,
345
+ ) {
346
+ const slotHash = ethers.utils.keccak256(
347
+ ethers.utils.concat([
348
+ ethers.utils.hexZeroPad(owner, 32),
349
+ ethers.utils.hexZeroPad(allowanceSlot, 32),
350
+ ]),
351
+ );
352
+
353
+ return ethers.utils.keccak256(
354
+ ethers.utils.concat([ethers.utils.hexZeroPad(spender, 32), slotHash]),
355
+ );
356
+ }
357
+
358
+ /**
359
+ *
360
+ * @param allowanceSlot storage slot of `allowance` mapping
361
+ * @param owner account's address
362
+ * @param spender spender's address
363
+ */
364
+ calculateVyperAddressAllowanceSlot(
365
+ allowanceSlot: string,
366
+ owner: string,
367
+ spender: string,
368
+ ) {
369
+ const slotHash = ethers.utils.keccak256(
370
+ ethers.utils.concat([
371
+ ethers.utils.hexZeroPad(allowanceSlot, 32),
372
+ ethers.utils.hexZeroPad(owner, 32),
373
+ ]),
374
+ );
375
+
376
+ return ethers.utils.keccak256(
377
+ ethers.utils.concat([slotHash, ethers.utils.hexZeroPad(spender, 32)]),
378
+ );
379
+ }
380
+
381
+ buildBalanceOfSimulationRequest(
382
+ chainId: number,
383
+ token: string,
384
+ owner: string,
385
+ ): SimulateTransactionRequest {
386
+ const iface = new ethers.utils.Interface([
387
+ 'function balanceOf(address owner) view returns (uint)',
388
+ ]);
389
+
390
+ return {
391
+ from: ethers.constants.AddressZero,
392
+ to: token,
393
+ data: iface.encodeFunctionData('balanceOf', [owner]),
394
+ chainId,
395
+ };
396
+ }
397
+
398
+ buildAllowanceSimulationRequest(
399
+ chainId: number,
400
+ token: string,
401
+ owner: string,
402
+ spender: string,
403
+ ): SimulateTransactionRequest {
404
+ const iface = new ethers.utils.Interface([
405
+ 'function allowance(address owner, address spender) view returns (uint)',
406
+ ]);
407
+
408
+ return {
409
+ from: ethers.constants.AddressZero,
410
+ to: token,
411
+ data: iface.encodeFunctionData('allowance', [owner, spender]),
412
+ chainId,
413
+ };
414
+ }
415
+
416
+ /**
417
+ * Finds the slot of the `balanceOf` mapping in given token contract's storage.
418
+ * Supports `Solidity` and `Vyper` contracts
419
+ * @param chainId token chain id
420
+ * @param token token address
421
+ */
422
+ async findTokenBalanceOfSlot(
423
+ chainId: number,
424
+ token: string,
425
+ ): Promise<{ slot: string; isVyper?: boolean }> {
426
+ const account = TenderlySimulatorNew.DEFAULT_OWNER;
427
+
428
+ const balanceOfSimulationRequest = this.buildBalanceOfSimulationRequest(
429
+ chainId,
430
+ token,
431
+ account,
432
+ );
433
+
434
+ const { id: simulationId } = await this.simulateTransaction(
435
+ balanceOfSimulationRequest,
436
+ );
437
+
438
+ const simulationDetails = await this.getSimulatedTransactionDetails(
439
+ simulationId,
440
+ );
441
+
442
+ if (!simulationDetails) {
443
+ throw `No simulation with id ${simulationId} details found`;
444
+ }
445
+
446
+ const callTrace = simulationDetails.transaction.transaction_info.call_trace;
447
+
448
+ const sloadCalls = this.getSLOADCalls(callTrace);
449
+ // token's storage slots that were read during the `balanceOf` call
450
+ const readSlots = sloadCalls
451
+ .map(call => call.storage_slot?.[0])
452
+ .filter<string>((slot): slot is string => !!slot);
453
+
454
+ const startingPoints = [
455
+ // regular contract
456
+ 0n,
457
+ // ERC20Upgradeable
458
+ BigInt(
459
+ '0x52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00',
460
+ ),
461
+ ];
462
+
463
+ for (const startingPoint of startingPoints) {
464
+ for (let i = startingPoint; i < startingPoint + 1_000n; i += 1n) {
465
+ const candidateSlot = ethers.utils.defaultAbiCoder.encode(
466
+ ['uint'],
467
+ [i],
468
+ );
469
+ // try solidity slot
470
+ const solitidyBalanceOfSlot = this.calculateSolidityAddressBalanceSlot(
471
+ candidateSlot,
472
+ account,
473
+ );
474
+ if (readSlots.includes(solitidyBalanceOfSlot)) {
475
+ return { slot: candidateSlot };
476
+ }
477
+ // try vyper slot
478
+ const vyperBalanceOfSlot = this.calculateVyperAddressBalanceSlot(
479
+ candidateSlot,
480
+ account,
481
+ );
482
+ if (readSlots.includes(vyperBalanceOfSlot)) {
483
+ return { slot: candidateSlot, isVyper: true };
484
+ }
485
+ }
486
+ }
487
+
488
+ throw new Error(
489
+ `Could not find a 'balanceOf' mapping slot for token ${token} on chain ${chainId}`,
490
+ );
491
+ }
492
+
493
+ /**
494
+ * Finds the slot of the `allowance` mapping in given token contract's storage
495
+ * Supports `Solidity` and `Vyper` contracts
496
+ * @param chainId token chain id
497
+ * @param token token address
498
+ */
499
+ async findTokenAllowanceSlot(
500
+ chainId: number,
501
+ token: string,
502
+ ): Promise<{ slot: string; isVyper?: boolean }> {
503
+ const account = TenderlySimulatorNew.DEFAULT_OWNER;
504
+ const spender = TenderlySimulatorNew.DEFAULT_SPENDER;
505
+
506
+ const allowanceSimulationRequest = this.buildAllowanceSimulationRequest(
507
+ chainId,
508
+ token,
509
+ account,
510
+ spender,
511
+ );
512
+
513
+ const { id: simulationId } = await this.simulateTransaction(
514
+ allowanceSimulationRequest,
515
+ );
516
+
517
+ const simulationDetails = await this.getSimulatedTransactionDetails(
518
+ simulationId,
519
+ );
520
+
521
+ if (!simulationDetails) {
522
+ throw `No simulation with id ${simulationId} details found`;
523
+ }
524
+
525
+ const callTrace = simulationDetails.transaction.transaction_info.call_trace;
526
+
527
+ const sloadCalls = this.getSLOADCalls(callTrace);
528
+ // token's storage slots that were read during the `allowance` call
529
+ const readSlots = sloadCalls
530
+ .map(call => call.storage_slot?.[0])
531
+ .filter<string>((slot): slot is string => !!slot);
532
+
533
+ const startingPoints = [
534
+ // regular contract
535
+ 0n,
536
+ // ERC20Upgradeable
537
+ BigInt(
538
+ '0x52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00',
539
+ ),
540
+ ];
541
+
542
+ for (const init of startingPoints) {
543
+ for (let i = init; i < init + 1000n; i += 1n) {
544
+ const candidateSlot = ethers.utils.defaultAbiCoder.encode(
545
+ ['uint'],
546
+ [i],
547
+ );
548
+ // try solidity
549
+ const solidityAllowanceSlot =
550
+ this.calculateSolidityAddressAllowanceSlot(
551
+ candidateSlot,
552
+ account,
553
+ spender,
554
+ );
555
+ if (readSlots.includes(solidityAllowanceSlot)) {
556
+ return { slot: candidateSlot };
557
+ }
558
+ // try vyper
559
+ const vyperAllowanceSlot = this.calculateVyperAddressAllowanceSlot(
560
+ candidateSlot,
561
+ account,
562
+ spender,
563
+ );
564
+ if (readSlots.includes(vyperAllowanceSlot)) {
565
+ return { slot: candidateSlot, isVyper: true };
566
+ }
567
+ }
568
+ }
569
+
570
+ throw new Error(
571
+ `Could not find a 'allowance' mapping slot for token ${token} on chain ${chainId}`,
572
+ );
573
+ }
574
+
575
+ /**
576
+ * Returns storage slots for the given token contract.
577
+ * @param chainId Token chain ID
578
+ * @param token Token address. Doesn't have to be normalized
579
+ */
580
+ async getTokenStorageSlots(
581
+ chainId: number,
582
+ token: string,
583
+ ): Promise<TokenStorageSlots> {
584
+ const normalizedToken = token.toLowerCase();
585
+
586
+ if (normalizedToken === ETHER_ADDRESS) {
587
+ throw new Error('Cannot provide storage slots for native token');
588
+ }
589
+
590
+ // check cache
591
+ const cachedSlots = await TokenStorageSlotsCache.getTokenStorageSlots(
592
+ chainId,
593
+ normalizedToken,
594
+ );
595
+
596
+ // return if cached
597
+ if (cachedSlots) {
598
+ return cachedSlots;
599
+ }
600
+
601
+ // find the slots
602
+ const [balanceSlot, allowanceSlot] = await Promise.all([
603
+ this.findTokenBalanceOfSlot(chainId, token),
604
+ this.findTokenAllowanceSlot(chainId, token),
605
+ ]);
606
+
607
+ // save the slots and return
608
+ const slots: TokenStorageSlots = {
609
+ balanceSlot: balanceSlot.slot,
610
+ allowanceSlot: allowanceSlot.slot,
611
+ isVyper: balanceSlot.isVyper,
612
+ };
613
+
614
+ // no need to await
615
+ void TokenStorageSlotsCache.setTokenStorageSlots(chainId, token, slots);
616
+
617
+ return slots;
618
+ }
619
+
620
+ /**
621
+ * Adds native balance override to an existing `StateOverride` object
622
+ * @param stateOverride object to add the override to
623
+ * @param account address to be given the balance
624
+ * @param amount token amount in wei
625
+ */
626
+ addBalanceOverride(
627
+ stateOverride: StateOverride,
628
+ account: string,
629
+ amount: bigint,
630
+ ): void {
631
+ // add the balance override
632
+ stateOverride[account] ||= {};
633
+ stateOverride[account].balance = amount.toString();
634
+ }
635
+
636
+ /**
637
+ * Adds token balance override to an existing `StateOverride` object
638
+ * @param stateOverride object to add the override to
639
+ * @param chainId token chain ID
640
+ * @param token token address
641
+ * @param account address to be given the balance
642
+ * @param amount token amount in wei
643
+ */
644
+ async addTokenBalanceOverride(
645
+ stateOverride: StateOverride,
646
+ chainId: number,
647
+ token: string,
648
+ account: string,
649
+ amount: bigint,
650
+ ): Promise<void> {
651
+ // get mapping slots
652
+ const tokenSlots = await this.getTokenStorageSlots(chainId, token);
653
+ // calculate balance slot
654
+ const slotToOverride = this.calculateAddressBalanceSlot(
655
+ tokenSlots.balanceSlot,
656
+ account,
657
+ tokenSlots.isVyper,
658
+ );
659
+ // add the balance override
660
+ stateOverride[token] ||= {};
661
+ stateOverride[token].storage ||= {};
662
+ stateOverride[token].storage[slotToOverride] =
663
+ ethers.utils.defaultAbiCoder.encode(['uint'], [amount]);
664
+ }
665
+
666
+ /**
667
+ * Adds token allowance override to an existing `StateOverride` object
668
+ * @param stateOverride object to add the override to
669
+ * @param chainId token chain ID
670
+ * @param token token address
671
+ * @param account owner address
672
+ * @param spender spender address
673
+ * @param amount token amount in wei
674
+ */
675
+ async addAllowanceOverride(
676
+ stateOverride: StateOverride,
677
+ chainId: number,
678
+ token: string,
679
+ account: string,
680
+ spender: string,
681
+ amount: bigint,
682
+ ): Promise<void> {
683
+ // get mapping slots
684
+ const tokenSlots = await this.getTokenStorageSlots(chainId, token);
685
+ // calculate allowance slot
686
+ const slotToOverride = this.calculateAddressAllowanceSlot(
687
+ tokenSlots.allowanceSlot,
688
+ account,
689
+ spender,
690
+ tokenSlots.isVyper,
691
+ );
692
+ // add the allowance override
693
+ stateOverride[token] ||= {};
694
+ stateOverride[token].storage ||= {};
695
+ stateOverride[token].storage[slotToOverride] =
696
+ ethers.utils.defaultAbiCoder.encode(['uint'], [amount]);
697
+ }
698
+ }