@humanode-hyperlane/rebalancer 0.0.0-stage → 27.2.2-humanode.1

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 (439) hide show
  1. package/LICENSE.md +193 -0
  2. package/README.md +297 -2
  3. package/dist/bridges/LiFiBridge.d.ts +93 -0
  4. package/dist/bridges/LiFiBridge.d.ts.map +1 -0
  5. package/dist/bridges/LiFiBridge.js +527 -0
  6. package/dist/bridges/LiFiBridge.js.map +1 -0
  7. package/dist/bridges/LiFiBridge.test.d.ts +2 -0
  8. package/dist/bridges/LiFiBridge.test.d.ts.map +1 -0
  9. package/dist/bridges/LiFiBridge.test.js +504 -0
  10. package/dist/bridges/LiFiBridge.test.js.map +1 -0
  11. package/dist/config/RebalancerConfig.d.ts +20 -0
  12. package/dist/config/RebalancerConfig.d.ts.map +1 -0
  13. package/dist/config/RebalancerConfig.js +35 -0
  14. package/dist/config/RebalancerConfig.js.map +1 -0
  15. package/dist/config/RebalancerConfig.test.d.ts +2 -0
  16. package/dist/config/RebalancerConfig.test.d.ts.map +1 -0
  17. package/dist/config/RebalancerConfig.test.js +798 -0
  18. package/dist/config/RebalancerConfig.test.js.map +1 -0
  19. package/dist/config/types.d.ts +3008 -0
  20. package/dist/config/types.d.ts.map +1 -0
  21. package/dist/config/types.js +451 -0
  22. package/dist/config/types.js.map +1 -0
  23. package/dist/core/InventoryRebalancer.d.ts +201 -0
  24. package/dist/core/InventoryRebalancer.d.ts.map +1 -0
  25. package/dist/core/InventoryRebalancer.js +994 -0
  26. package/dist/core/InventoryRebalancer.js.map +1 -0
  27. package/dist/core/InventoryRebalancer.test.d.ts +2 -0
  28. package/dist/core/InventoryRebalancer.test.d.ts.map +1 -0
  29. package/dist/core/InventoryRebalancer.test.js +1511 -0
  30. package/dist/core/InventoryRebalancer.test.js.map +1 -0
  31. package/dist/core/Rebalancer.d.ts +37 -0
  32. package/dist/core/Rebalancer.d.ts.map +1 -0
  33. package/dist/core/Rebalancer.js +433 -0
  34. package/dist/core/Rebalancer.js.map +1 -0
  35. package/dist/core/Rebalancer.test.d.ts +2 -0
  36. package/dist/core/Rebalancer.test.d.ts.map +1 -0
  37. package/dist/core/Rebalancer.test.js +424 -0
  38. package/dist/core/Rebalancer.test.js.map +1 -0
  39. package/dist/core/RebalancerOrchestrator.d.ts +56 -0
  40. package/dist/core/RebalancerOrchestrator.d.ts.map +1 -0
  41. package/dist/core/RebalancerOrchestrator.js +154 -0
  42. package/dist/core/RebalancerOrchestrator.js.map +1 -0
  43. package/dist/core/RebalancerOrchestrator.test.d.ts +2 -0
  44. package/dist/core/RebalancerOrchestrator.test.d.ts.map +1 -0
  45. package/dist/core/RebalancerOrchestrator.test.js +721 -0
  46. package/dist/core/RebalancerOrchestrator.test.js.map +1 -0
  47. package/dist/core/RebalancerService.d.ts +127 -0
  48. package/dist/core/RebalancerService.d.ts.map +1 -0
  49. package/dist/core/RebalancerService.js +270 -0
  50. package/dist/core/RebalancerService.js.map +1 -0
  51. package/dist/core/RebalancerService.test.d.ts +2 -0
  52. package/dist/core/RebalancerService.test.d.ts.map +1 -0
  53. package/dist/core/RebalancerService.test.js +777 -0
  54. package/dist/core/RebalancerService.test.js.map +1 -0
  55. package/dist/e2e/collateral-deficit.e2e-test.d.ts +2 -0
  56. package/dist/e2e/collateral-deficit.e2e-test.d.ts.map +1 -0
  57. package/dist/e2e/collateral-deficit.e2e-test.js +227 -0
  58. package/dist/e2e/collateral-deficit.e2e-test.js.map +1 -0
  59. package/dist/e2e/composite.e2e-test.d.ts +2 -0
  60. package/dist/e2e/composite.e2e-test.d.ts.map +1 -0
  61. package/dist/e2e/composite.e2e-test.js +666 -0
  62. package/dist/e2e/composite.e2e-test.js.map +1 -0
  63. package/dist/e2e/fixtures/routes.d.ts +89 -0
  64. package/dist/e2e/fixtures/routes.d.ts.map +1 -0
  65. package/dist/e2e/fixtures/routes.js +420 -0
  66. package/dist/e2e/fixtures/routes.js.map +1 -0
  67. package/dist/e2e/harness/BaseLocalDeploymentManager.d.ts +38 -0
  68. package/dist/e2e/harness/BaseLocalDeploymentManager.d.ts.map +1 -0
  69. package/dist/e2e/harness/BaseLocalDeploymentManager.js +144 -0
  70. package/dist/e2e/harness/BaseLocalDeploymentManager.js.map +1 -0
  71. package/dist/e2e/harness/BridgeSetup.d.ts +11 -0
  72. package/dist/e2e/harness/BridgeSetup.d.ts.map +1 -0
  73. package/dist/e2e/harness/BridgeSetup.js +50 -0
  74. package/dist/e2e/harness/BridgeSetup.js.map +1 -0
  75. package/dist/e2e/harness/Erc20InventoryLocalDeploymentManager.d.ts +13 -0
  76. package/dist/e2e/harness/Erc20InventoryLocalDeploymentManager.d.ts.map +1 -0
  77. package/dist/e2e/harness/Erc20InventoryLocalDeploymentManager.js +105 -0
  78. package/dist/e2e/harness/Erc20InventoryLocalDeploymentManager.js.map +1 -0
  79. package/dist/e2e/harness/Erc20LocalDeploymentManager.d.ts +11 -0
  80. package/dist/e2e/harness/Erc20LocalDeploymentManager.d.ts.map +1 -0
  81. package/dist/e2e/harness/Erc20LocalDeploymentManager.js +105 -0
  82. package/dist/e2e/harness/Erc20LocalDeploymentManager.js.map +1 -0
  83. package/dist/e2e/harness/ForkIndexer.d.ts +23 -0
  84. package/dist/e2e/harness/ForkIndexer.d.ts.map +1 -0
  85. package/dist/e2e/harness/ForkIndexer.js +127 -0
  86. package/dist/e2e/harness/ForkIndexer.js.map +1 -0
  87. package/dist/e2e/harness/ForkIndexer.test.d.ts +2 -0
  88. package/dist/e2e/harness/ForkIndexer.test.d.ts.map +1 -0
  89. package/dist/e2e/harness/ForkIndexer.test.js +257 -0
  90. package/dist/e2e/harness/ForkIndexer.test.js.map +1 -0
  91. package/dist/e2e/harness/MixedLocalDeploymentManager.d.ts +21 -0
  92. package/dist/e2e/harness/MixedLocalDeploymentManager.d.ts.map +1 -0
  93. package/dist/e2e/harness/MixedLocalDeploymentManager.js +115 -0
  94. package/dist/e2e/harness/MixedLocalDeploymentManager.js.map +1 -0
  95. package/dist/e2e/harness/MockExplorerClient.d.ts +23 -0
  96. package/dist/e2e/harness/MockExplorerClient.d.ts.map +1 -0
  97. package/dist/e2e/harness/MockExplorerClient.js +57 -0
  98. package/dist/e2e/harness/MockExplorerClient.js.map +1 -0
  99. package/dist/e2e/harness/MockExternalBridge.d.ts +32 -0
  100. package/dist/e2e/harness/MockExternalBridge.d.ts.map +1 -0
  101. package/dist/e2e/harness/MockExternalBridge.js +247 -0
  102. package/dist/e2e/harness/MockExternalBridge.js.map +1 -0
  103. package/dist/e2e/harness/NativeLocalDeploymentManager.d.ts +13 -0
  104. package/dist/e2e/harness/NativeLocalDeploymentManager.d.ts.map +1 -0
  105. package/dist/e2e/harness/NativeLocalDeploymentManager.js +91 -0
  106. package/dist/e2e/harness/NativeLocalDeploymentManager.js.map +1 -0
  107. package/dist/e2e/harness/SnapshotHelper.d.ts +13 -0
  108. package/dist/e2e/harness/SnapshotHelper.d.ts.map +1 -0
  109. package/dist/e2e/harness/SnapshotHelper.js +36 -0
  110. package/dist/e2e/harness/SnapshotHelper.js.map +1 -0
  111. package/dist/e2e/harness/TestHelpers.d.ts +25 -0
  112. package/dist/e2e/harness/TestHelpers.d.ts.map +1 -0
  113. package/dist/e2e/harness/TestHelpers.js +103 -0
  114. package/dist/e2e/harness/TestHelpers.js.map +1 -0
  115. package/dist/e2e/harness/TestRebalancer.d.ts +85 -0
  116. package/dist/e2e/harness/TestRebalancer.d.ts.map +1 -0
  117. package/dist/e2e/harness/TestRebalancer.js +427 -0
  118. package/dist/e2e/harness/TestRebalancer.js.map +1 -0
  119. package/dist/e2e/harness/TransferHelper.d.ts +34 -0
  120. package/dist/e2e/harness/TransferHelper.d.ts.map +1 -0
  121. package/dist/e2e/harness/TransferHelper.js +93 -0
  122. package/dist/e2e/harness/TransferHelper.js.map +1 -0
  123. package/dist/e2e/inventory-erc20-minAmount.e2e-test.d.ts +2 -0
  124. package/dist/e2e/inventory-erc20-minAmount.e2e-test.d.ts.map +1 -0
  125. package/dist/e2e/inventory-erc20-minAmount.e2e-test.js +411 -0
  126. package/dist/e2e/inventory-erc20-minAmount.e2e-test.js.map +1 -0
  127. package/dist/e2e/inventory-erc20-weighted.e2e-test.d.ts +2 -0
  128. package/dist/e2e/inventory-erc20-weighted.e2e-test.d.ts.map +1 -0
  129. package/dist/e2e/inventory-erc20-weighted.e2e-test.js +408 -0
  130. package/dist/e2e/inventory-erc20-weighted.e2e-test.js.map +1 -0
  131. package/dist/e2e/inventory-minAmount.e2e-test.d.ts +2 -0
  132. package/dist/e2e/inventory-minAmount.e2e-test.d.ts.map +1 -0
  133. package/dist/e2e/inventory-minAmount.e2e-test.js +411 -0
  134. package/dist/e2e/inventory-minAmount.e2e-test.js.map +1 -0
  135. package/dist/e2e/inventory-weighted.e2e-test.d.ts +2 -0
  136. package/dist/e2e/inventory-weighted.e2e-test.d.ts.map +1 -0
  137. package/dist/e2e/inventory-weighted.e2e-test.js +409 -0
  138. package/dist/e2e/inventory-weighted.e2e-test.js.map +1 -0
  139. package/dist/e2e/minAmount.e2e-test.d.ts +2 -0
  140. package/dist/e2e/minAmount.e2e-test.d.ts.map +1 -0
  141. package/dist/e2e/minAmount.e2e-test.js +203 -0
  142. package/dist/e2e/minAmount.e2e-test.js.map +1 -0
  143. package/dist/e2e/mixed-weighted.e2e-test.d.ts +2 -0
  144. package/dist/e2e/mixed-weighted.e2e-test.d.ts.map +1 -0
  145. package/dist/e2e/mixed-weighted.e2e-test.js +189 -0
  146. package/dist/e2e/mixed-weighted.e2e-test.js.map +1 -0
  147. package/dist/e2e/weighted.e2e-test.d.ts +2 -0
  148. package/dist/e2e/weighted.e2e-test.d.ts.map +1 -0
  149. package/dist/e2e/weighted.e2e-test.js +198 -0
  150. package/dist/e2e/weighted.e2e-test.js.map +1 -0
  151. package/dist/factories/RebalancerContextFactory.d.ts +89 -0
  152. package/dist/factories/RebalancerContextFactory.d.ts.map +1 -0
  153. package/dist/factories/RebalancerContextFactory.js +420 -0
  154. package/dist/factories/RebalancerContextFactory.js.map +1 -0
  155. package/dist/factories/RebalancerContextFactory.test.d.ts +2 -0
  156. package/dist/factories/RebalancerContextFactory.test.d.ts.map +1 -0
  157. package/dist/factories/RebalancerContextFactory.test.js +270 -0
  158. package/dist/factories/RebalancerContextFactory.test.js.map +1 -0
  159. package/dist/index.d.ts +37 -0
  160. package/dist/index.d.ts.map +1 -0
  161. package/dist/index.js +37 -0
  162. package/dist/index.js.map +1 -0
  163. package/dist/interfaces/IExternalBridge.d.ts +103 -0
  164. package/dist/interfaces/IExternalBridge.d.ts.map +1 -0
  165. package/dist/interfaces/IExternalBridge.js +2 -0
  166. package/dist/interfaces/IExternalBridge.js.map +1 -0
  167. package/dist/interfaces/IMetrics.d.ts +5 -0
  168. package/dist/interfaces/IMetrics.d.ts.map +1 -0
  169. package/dist/interfaces/IMetrics.js +2 -0
  170. package/dist/interfaces/IMetrics.js.map +1 -0
  171. package/dist/interfaces/IMonitor.d.ts +50 -0
  172. package/dist/interfaces/IMonitor.d.ts.map +1 -0
  173. package/dist/interfaces/IMonitor.js +14 -0
  174. package/dist/interfaces/IMonitor.js.map +1 -0
  175. package/dist/interfaces/IRebalancer.d.ts +31 -0
  176. package/dist/interfaces/IRebalancer.d.ts.map +1 -0
  177. package/dist/interfaces/IRebalancer.js +2 -0
  178. package/dist/interfaces/IRebalancer.js.map +1 -0
  179. package/dist/interfaces/IStrategy.d.ts +60 -0
  180. package/dist/interfaces/IStrategy.d.ts.map +1 -0
  181. package/dist/interfaces/IStrategy.js +13 -0
  182. package/dist/interfaces/IStrategy.js.map +1 -0
  183. package/dist/metrics/Metrics.d.ts +25 -0
  184. package/dist/metrics/Metrics.d.ts.map +1 -0
  185. package/dist/metrics/Metrics.js +164 -0
  186. package/dist/metrics/Metrics.js.map +1 -0
  187. package/dist/metrics/PriceGetter.d.ts +10 -0
  188. package/dist/metrics/PriceGetter.d.ts.map +1 -0
  189. package/dist/metrics/PriceGetter.js +41 -0
  190. package/dist/metrics/PriceGetter.js.map +1 -0
  191. package/dist/metrics/scripts/metrics.d.ts +28 -0
  192. package/dist/metrics/scripts/metrics.d.ts.map +1 -0
  193. package/dist/metrics/scripts/metrics.js +61 -0
  194. package/dist/metrics/scripts/metrics.js.map +1 -0
  195. package/dist/monitor/Monitor.d.ts +40 -0
  196. package/dist/monitor/Monitor.d.ts.map +1 -0
  197. package/dist/monitor/Monitor.js +211 -0
  198. package/dist/monitor/Monitor.js.map +1 -0
  199. package/dist/service.d.ts +3 -0
  200. package/dist/service.d.ts.map +1 -0
  201. package/dist/service.js +205 -0
  202. package/dist/service.js.map +1 -0
  203. package/dist/strategy/BaseStrategy.d.ts +86 -0
  204. package/dist/strategy/BaseStrategy.d.ts.map +1 -0
  205. package/dist/strategy/BaseStrategy.js +342 -0
  206. package/dist/strategy/BaseStrategy.js.map +1 -0
  207. package/dist/strategy/CollateralDeficitStrategy.d.ts +65 -0
  208. package/dist/strategy/CollateralDeficitStrategy.d.ts.map +1 -0
  209. package/dist/strategy/CollateralDeficitStrategy.js +253 -0
  210. package/dist/strategy/CollateralDeficitStrategy.js.map +1 -0
  211. package/dist/strategy/CollateralDeficitStrategy.test.d.ts +2 -0
  212. package/dist/strategy/CollateralDeficitStrategy.test.d.ts.map +1 -0
  213. package/dist/strategy/CollateralDeficitStrategy.test.js +497 -0
  214. package/dist/strategy/CollateralDeficitStrategy.test.js.map +1 -0
  215. package/dist/strategy/CompositeStrategy.d.ts +18 -0
  216. package/dist/strategy/CompositeStrategy.d.ts.map +1 -0
  217. package/dist/strategy/CompositeStrategy.js +63 -0
  218. package/dist/strategy/CompositeStrategy.js.map +1 -0
  219. package/dist/strategy/CompositeStrategy.test.d.ts +2 -0
  220. package/dist/strategy/CompositeStrategy.test.d.ts.map +1 -0
  221. package/dist/strategy/CompositeStrategy.test.js +278 -0
  222. package/dist/strategy/CompositeStrategy.test.js.map +1 -0
  223. package/dist/strategy/MinAmountStrategy.d.ts +35 -0
  224. package/dist/strategy/MinAmountStrategy.d.ts.map +1 -0
  225. package/dist/strategy/MinAmountStrategy.js +112 -0
  226. package/dist/strategy/MinAmountStrategy.js.map +1 -0
  227. package/dist/strategy/MinAmountStrategy.test.d.ts +2 -0
  228. package/dist/strategy/MinAmountStrategy.test.d.ts.map +1 -0
  229. package/dist/strategy/MinAmountStrategy.test.js +547 -0
  230. package/dist/strategy/MinAmountStrategy.test.js.map +1 -0
  231. package/dist/strategy/StrategyFactory.d.ts +27 -0
  232. package/dist/strategy/StrategyFactory.d.ts.map +1 -0
  233. package/dist/strategy/StrategyFactory.js +79 -0
  234. package/dist/strategy/StrategyFactory.js.map +1 -0
  235. package/dist/strategy/StrategyFactory.test.d.ts +2 -0
  236. package/dist/strategy/StrategyFactory.test.d.ts.map +1 -0
  237. package/dist/strategy/StrategyFactory.test.js +81 -0
  238. package/dist/strategy/StrategyFactory.test.js.map +1 -0
  239. package/dist/strategy/WeightedStrategy.d.ts +32 -0
  240. package/dist/strategy/WeightedStrategy.d.ts.map +1 -0
  241. package/dist/strategy/WeightedStrategy.js +73 -0
  242. package/dist/strategy/WeightedStrategy.js.map +1 -0
  243. package/dist/strategy/WeightedStrategy.test.d.ts +2 -0
  244. package/dist/strategy/WeightedStrategy.test.d.ts.map +1 -0
  245. package/dist/strategy/WeightedStrategy.test.js +403 -0
  246. package/dist/strategy/WeightedStrategy.test.js.map +1 -0
  247. package/dist/strategy/index.d.ts +7 -0
  248. package/dist/strategy/index.d.ts.map +1 -0
  249. package/dist/strategy/index.js +7 -0
  250. package/dist/strategy/index.js.map +1 -0
  251. package/dist/test/helpers.d.ts +99 -0
  252. package/dist/test/helpers.d.ts.map +1 -0
  253. package/dist/test/helpers.js +311 -0
  254. package/dist/test/helpers.js.map +1 -0
  255. package/dist/test/lifiMocks.d.ts +52 -0
  256. package/dist/test/lifiMocks.d.ts.map +1 -0
  257. package/dist/test/lifiMocks.js +160 -0
  258. package/dist/test/lifiMocks.js.map +1 -0
  259. package/dist/tracking/ActionTracker.d.ts +90 -0
  260. package/dist/tracking/ActionTracker.d.ts.map +1 -0
  261. package/dist/tracking/ActionTracker.js +678 -0
  262. package/dist/tracking/ActionTracker.js.map +1 -0
  263. package/dist/tracking/ActionTracker.test.d.ts +2 -0
  264. package/dist/tracking/ActionTracker.test.d.ts.map +1 -0
  265. package/dist/tracking/ActionTracker.test.js +1351 -0
  266. package/dist/tracking/ActionTracker.test.js.map +1 -0
  267. package/dist/tracking/IActionTracker.d.ts +157 -0
  268. package/dist/tracking/IActionTracker.d.ts.map +1 -0
  269. package/dist/tracking/IActionTracker.js +2 -0
  270. package/dist/tracking/IActionTracker.js.map +1 -0
  271. package/dist/tracking/InflightContextAdapter.d.ts +18 -0
  272. package/dist/tracking/InflightContextAdapter.d.ts.map +1 -0
  273. package/dist/tracking/InflightContextAdapter.js +52 -0
  274. package/dist/tracking/InflightContextAdapter.js.map +1 -0
  275. package/dist/tracking/InflightContextAdapter.test.d.ts +2 -0
  276. package/dist/tracking/InflightContextAdapter.test.d.ts.map +1 -0
  277. package/dist/tracking/InflightContextAdapter.test.js +175 -0
  278. package/dist/tracking/InflightContextAdapter.test.js.map +1 -0
  279. package/dist/tracking/store/IStore.d.ts +41 -0
  280. package/dist/tracking/store/IStore.d.ts.map +1 -0
  281. package/dist/tracking/store/IStore.js +2 -0
  282. package/dist/tracking/store/IStore.js.map +1 -0
  283. package/dist/tracking/store/InMemoryStore.d.ts +21 -0
  284. package/dist/tracking/store/InMemoryStore.d.ts.map +1 -0
  285. package/dist/tracking/store/InMemoryStore.js +40 -0
  286. package/dist/tracking/store/InMemoryStore.js.map +1 -0
  287. package/dist/tracking/store/InMemoryStore.test.d.ts +2 -0
  288. package/dist/tracking/store/InMemoryStore.test.d.ts.map +1 -0
  289. package/dist/tracking/store/InMemoryStore.test.js +290 -0
  290. package/dist/tracking/store/InMemoryStore.test.js.map +1 -0
  291. package/dist/tracking/store/index.d.ts +3 -0
  292. package/dist/tracking/store/index.d.ts.map +1 -0
  293. package/dist/tracking/store/index.js +2 -0
  294. package/dist/tracking/store/index.js.map +1 -0
  295. package/dist/tracking/types.d.ts +77 -0
  296. package/dist/tracking/types.d.ts.map +1 -0
  297. package/dist/tracking/types.js +2 -0
  298. package/dist/tracking/types.js.map +1 -0
  299. package/dist/utils/ExplorerClient.d.ts +68 -0
  300. package/dist/utils/ExplorerClient.d.ts.map +1 -0
  301. package/dist/utils/ExplorerClient.js +324 -0
  302. package/dist/utils/ExplorerClient.js.map +1 -0
  303. package/dist/utils/ExplorerClient.test.d.ts +2 -0
  304. package/dist/utils/ExplorerClient.test.d.ts.map +1 -0
  305. package/dist/utils/ExplorerClient.test.js +261 -0
  306. package/dist/utils/ExplorerClient.test.js.map +1 -0
  307. package/dist/utils/balanceUtils.d.ts +12 -0
  308. package/dist/utils/balanceUtils.d.ts.map +1 -0
  309. package/dist/utils/balanceUtils.js +40 -0
  310. package/dist/utils/balanceUtils.js.map +1 -0
  311. package/dist/utils/balanceUtils.test.d.ts +2 -0
  312. package/dist/utils/balanceUtils.test.d.ts.map +1 -0
  313. package/dist/utils/balanceUtils.test.js +55 -0
  314. package/dist/utils/balanceUtils.test.js.map +1 -0
  315. package/dist/utils/blockTag.d.ts +14 -0
  316. package/dist/utils/blockTag.d.ts.map +1 -0
  317. package/dist/utils/blockTag.js +46 -0
  318. package/dist/utils/blockTag.js.map +1 -0
  319. package/dist/utils/bridgeUtils.d.ts +40 -0
  320. package/dist/utils/bridgeUtils.d.ts.map +1 -0
  321. package/dist/utils/bridgeUtils.js +54 -0
  322. package/dist/utils/bridgeUtils.js.map +1 -0
  323. package/dist/utils/bridgeUtils.test.d.ts +2 -0
  324. package/dist/utils/bridgeUtils.test.d.ts.map +1 -0
  325. package/dist/utils/bridgeUtils.test.js +163 -0
  326. package/dist/utils/bridgeUtils.test.js.map +1 -0
  327. package/dist/utils/errors.d.ts +4 -0
  328. package/dist/utils/errors.d.ts.map +1 -0
  329. package/dist/utils/errors.js +6 -0
  330. package/dist/utils/errors.js.map +1 -0
  331. package/dist/utils/gasEstimation.d.ts +65 -0
  332. package/dist/utils/gasEstimation.d.ts.map +1 -0
  333. package/dist/utils/gasEstimation.js +206 -0
  334. package/dist/utils/gasEstimation.js.map +1 -0
  335. package/dist/utils/solanaKeyParser.d.ts +10 -0
  336. package/dist/utils/solanaKeyParser.d.ts.map +1 -0
  337. package/dist/utils/solanaKeyParser.js +87 -0
  338. package/dist/utils/solanaKeyParser.js.map +1 -0
  339. package/dist/utils/solanaKeyParser.test.d.ts +2 -0
  340. package/dist/utils/solanaKeyParser.test.d.ts.map +1 -0
  341. package/dist/utils/solanaKeyParser.test.js +71 -0
  342. package/dist/utils/solanaKeyParser.test.js.map +1 -0
  343. package/dist/utils/tokenUtils.d.ts +38 -0
  344. package/dist/utils/tokenUtils.d.ts.map +1 -0
  345. package/dist/utils/tokenUtils.js +65 -0
  346. package/dist/utils/tokenUtils.js.map +1 -0
  347. package/dist/utils/transactionUtils.d.ts +22 -0
  348. package/dist/utils/transactionUtils.d.ts.map +1 -0
  349. package/dist/utils/transactionUtils.js +26 -0
  350. package/dist/utils/transactionUtils.js.map +1 -0
  351. package/package.json +87 -3
  352. package/src/bridges/LiFiBridge.test.ts +615 -0
  353. package/src/bridges/LiFiBridge.ts +731 -0
  354. package/src/config/RebalancerConfig.test.ts +921 -0
  355. package/src/config/RebalancerConfig.ts +63 -0
  356. package/src/config/types.ts +613 -0
  357. package/src/core/InventoryRebalancer.test.ts +1858 -0
  358. package/src/core/InventoryRebalancer.ts +1455 -0
  359. package/src/core/Rebalancer.test.ts +686 -0
  360. package/src/core/Rebalancer.ts +686 -0
  361. package/src/core/RebalancerOrchestrator.test.ts +871 -0
  362. package/src/core/RebalancerOrchestrator.ts +273 -0
  363. package/src/core/RebalancerService.test.ts +1094 -0
  364. package/src/core/RebalancerService.ts +412 -0
  365. package/src/e2e/E2E_TEST_CONVENTIONS.md +222 -0
  366. package/src/e2e/collateral-deficit.e2e-test.ts +403 -0
  367. package/src/e2e/composite.e2e-test.ts +1008 -0
  368. package/src/e2e/fixtures/routes.ts +532 -0
  369. package/src/e2e/harness/BaseLocalDeploymentManager.ts +246 -0
  370. package/src/e2e/harness/BridgeSetup.ts +95 -0
  371. package/src/e2e/harness/Erc20InventoryLocalDeploymentManager.ts +183 -0
  372. package/src/e2e/harness/Erc20LocalDeploymentManager.ts +190 -0
  373. package/src/e2e/harness/ForkIndexer.test.ts +448 -0
  374. package/src/e2e/harness/ForkIndexer.ts +183 -0
  375. package/src/e2e/harness/MixedLocalDeploymentManager.ts +196 -0
  376. package/src/e2e/harness/MockExplorerClient.ts +94 -0
  377. package/src/e2e/harness/MockExternalBridge.ts +411 -0
  378. package/src/e2e/harness/NativeLocalDeploymentManager.ts +138 -0
  379. package/src/e2e/harness/SnapshotHelper.ts +56 -0
  380. package/src/e2e/harness/TestHelpers.ts +169 -0
  381. package/src/e2e/harness/TestRebalancer.ts +758 -0
  382. package/src/e2e/harness/TransferHelper.ts +188 -0
  383. package/src/e2e/inventory-erc20-minAmount.e2e-test.ts +735 -0
  384. package/src/e2e/inventory-erc20-weighted.e2e-test.ts +733 -0
  385. package/src/e2e/inventory-minAmount.e2e-test.ts +734 -0
  386. package/src/e2e/inventory-weighted.e2e-test.ts +734 -0
  387. package/src/e2e/minAmount.e2e-test.ts +313 -0
  388. package/src/e2e/mixed-weighted.e2e-test.ts +267 -0
  389. package/src/e2e/weighted.e2e-test.ts +304 -0
  390. package/src/factories/RebalancerContextFactory.test.ts +394 -0
  391. package/src/factories/RebalancerContextFactory.ts +771 -0
  392. package/src/index.ts +125 -0
  393. package/src/interfaces/IExternalBridge.ts +117 -0
  394. package/src/interfaces/IMetrics.ts +5 -0
  395. package/src/interfaces/IMonitor.ts +71 -0
  396. package/src/interfaces/IRebalancer.ts +57 -0
  397. package/src/interfaces/IStrategy.ts +81 -0
  398. package/src/metrics/Metrics.ts +335 -0
  399. package/src/metrics/PriceGetter.ts +74 -0
  400. package/src/metrics/scripts/metrics.ts +139 -0
  401. package/src/monitor/Monitor.ts +324 -0
  402. package/src/service.ts +285 -0
  403. package/src/strategy/BaseStrategy.ts +553 -0
  404. package/src/strategy/CollateralDeficitStrategy.test.ts +728 -0
  405. package/src/strategy/CollateralDeficitStrategy.ts +421 -0
  406. package/src/strategy/CompositeStrategy.test.ts +418 -0
  407. package/src/strategy/CompositeStrategy.ts +102 -0
  408. package/src/strategy/MinAmountStrategy.test.ts +732 -0
  409. package/src/strategy/MinAmountStrategy.ts +200 -0
  410. package/src/strategy/StrategyFactory.test.ts +108 -0
  411. package/src/strategy/StrategyFactory.ts +157 -0
  412. package/src/strategy/WeightedStrategy.test.ts +529 -0
  413. package/src/strategy/WeightedStrategy.ts +126 -0
  414. package/src/strategy/index.ts +6 -0
  415. package/src/test/helpers.ts +477 -0
  416. package/src/test/lifiMocks.ts +211 -0
  417. package/src/tracking/ActionTracker.test.ts +1625 -0
  418. package/src/tracking/ActionTracker.ts +1019 -0
  419. package/src/tracking/IActionTracker.ts +213 -0
  420. package/src/tracking/InflightContextAdapter.test.ts +206 -0
  421. package/src/tracking/InflightContextAdapter.ts +75 -0
  422. package/src/tracking/store/IStore.ts +48 -0
  423. package/src/tracking/store/InMemoryStore.test.ts +338 -0
  424. package/src/tracking/store/InMemoryStore.ts +59 -0
  425. package/src/tracking/store/index.ts +2 -0
  426. package/src/tracking/types.ts +118 -0
  427. package/src/utils/ExplorerClient.test.ts +386 -0
  428. package/src/utils/ExplorerClient.ts +439 -0
  429. package/src/utils/balanceUtils.test.ts +79 -0
  430. package/src/utils/balanceUtils.ts +65 -0
  431. package/src/utils/blockTag.ts +68 -0
  432. package/src/utils/bridgeUtils.test.ts +193 -0
  433. package/src/utils/bridgeUtils.ts +101 -0
  434. package/src/utils/errors.ts +5 -0
  435. package/src/utils/gasEstimation.ts +306 -0
  436. package/src/utils/solanaKeyParser.test.ts +103 -0
  437. package/src/utils/solanaKeyParser.ts +117 -0
  438. package/src/utils/tokenUtils.ts +89 -0
  439. package/src/utils/transactionUtils.ts +41 -0
@@ -0,0 +1,1094 @@
1
+ import chai, { expect } from 'chai';
2
+ import chaiAsPromised from 'chai-as-promised';
3
+ import { pino } from 'pino';
4
+ import Sinon from 'sinon';
5
+
6
+ import type { MultiProvider, Token, WarpCore } from '@humanode-hyperlane/sdk';
7
+
8
+ import type { RebalancerConfig } from '../config/RebalancerConfig.js';
9
+ import {
10
+ DEFAULT_INTENT_TTL_MS,
11
+ RebalancerStrategyOptions,
12
+ } from '../config/types.js';
13
+ import { RebalancerContextFactory } from '../factories/RebalancerContextFactory.js';
14
+ import type { ExternalBridgeRegistry } from '../interfaces/IExternalBridge.js';
15
+ import { MonitorEventType } from '../interfaces/IMonitor.js';
16
+ import type { IRebalancer } from '../interfaces/IRebalancer.js';
17
+ import type { IStrategy } from '../interfaces/IStrategy.js';
18
+ import { Metrics } from '../metrics/Metrics.js';
19
+ import { Monitor } from '../monitor/Monitor.js';
20
+ import { TEST_ADDRESSES, getTestAddress } from '../test/helpers.js';
21
+ import type { IActionTracker } from '../tracking/IActionTracker.js';
22
+ import { InflightContextAdapter } from '../tracking/InflightContextAdapter.js';
23
+
24
+ import {
25
+ RebalancerService,
26
+ type RebalancerServiceConfig,
27
+ } from './RebalancerService.js';
28
+
29
+ chai.use(chaiAsPromised);
30
+
31
+ const testLogger = pino({ level: 'silent' });
32
+
33
+ function createMockRebalancerConfig(): RebalancerConfig {
34
+ return {
35
+ warpRouteId: 'TEST/route',
36
+ strategyConfig: [
37
+ {
38
+ rebalanceStrategy: RebalancerStrategyOptions.Weighted,
39
+ chains: {
40
+ ethereum: {
41
+ bridge: TEST_ADDRESSES.bridge,
42
+ bridgeMinAcceptedAmount: 0,
43
+ weighted: { weight: 50n, tolerance: 10n },
44
+ },
45
+ arbitrum: {
46
+ bridge: TEST_ADDRESSES.bridge,
47
+ bridgeMinAcceptedAmount: 0,
48
+ weighted: { weight: 50n, tolerance: 10n },
49
+ },
50
+ },
51
+ },
52
+ ],
53
+ intentTTL: DEFAULT_INTENT_TTL_MS,
54
+ } as RebalancerConfig;
55
+ }
56
+
57
+ function createMockMultiProvider(): MultiProvider {
58
+ return {
59
+ getDomainId: Sinon.stub().callsFake((chain: string) => {
60
+ const domains: Record<string, number> = { ethereum: 1, arbitrum: 42161 };
61
+ return domains[chain] ?? 0;
62
+ }),
63
+ getSigner: Sinon.stub().returns({
64
+ getAddress: Sinon.stub().resolves(TEST_ADDRESSES.signer),
65
+ }),
66
+ metadata: {
67
+ ethereum: { domainId: 1 },
68
+ arbitrum: { domainId: 42161 },
69
+ },
70
+ } as unknown as MultiProvider;
71
+ }
72
+
73
+ function createMockToken(chainName: string): Token {
74
+ return {
75
+ chainName,
76
+ name: `${chainName}Token`,
77
+ decimals: 18,
78
+ addressOrDenom: getTestAddress(chainName),
79
+ standard: 'EvmHypCollateral',
80
+ isCollateralized: () => true,
81
+ } as unknown as Token;
82
+ }
83
+
84
+ function createMockWarpCore(): WarpCore {
85
+ return {
86
+ tokens: [createMockToken('ethereum'), createMockToken('arbitrum')],
87
+ multiProvider: createMockMultiProvider(),
88
+ } as unknown as WarpCore;
89
+ }
90
+
91
+ function createMockRebalancer(): IRebalancer & { rebalance: Sinon.SinonStub } {
92
+ return {
93
+ rebalancerType: 'movableCollateral' as const,
94
+ rebalance: Sinon.stub().resolves([]),
95
+ };
96
+ }
97
+
98
+ function createMockStrategy(): IStrategy & {
99
+ getRebalancingRoutes: Sinon.SinonStub;
100
+ } {
101
+ return {
102
+ name: 'mock-strategy',
103
+ getRebalancingRoutes: Sinon.stub().returns([]),
104
+ };
105
+ }
106
+
107
+ function createMockActionTracker(): IActionTracker {
108
+ return {
109
+ initialize: Sinon.stub().resolves(),
110
+ createRebalanceIntent: Sinon.stub().callsFake(async () => ({
111
+ id: `intent-${Date.now()}`,
112
+ status: 'not_started',
113
+ })),
114
+ createRebalanceAction: Sinon.stub().resolves(),
115
+ completeRebalanceAction: Sinon.stub().resolves(),
116
+ failRebalanceAction: Sinon.stub().resolves(),
117
+ completeRebalanceIntent: Sinon.stub().resolves(),
118
+ cancelRebalanceIntent: Sinon.stub().resolves(),
119
+ failRebalanceIntent: Sinon.stub().resolves(),
120
+ syncTransfers: Sinon.stub().resolves(),
121
+ syncRebalanceIntents: Sinon.stub().resolves(),
122
+ syncRebalanceActions: Sinon.stub().resolves(),
123
+ syncInventoryMovementActions: Sinon.stub().resolves({
124
+ completed: 0,
125
+ failed: 0,
126
+ }),
127
+ logStoreContents: Sinon.stub().resolves(),
128
+ getInProgressTransfers: Sinon.stub().resolves([]),
129
+ getActiveRebalanceIntents: Sinon.stub().resolves([]),
130
+ getTransfersByDestination: Sinon.stub().resolves([]),
131
+ getRebalanceIntentsByDestination: Sinon.stub().resolves([]),
132
+ getTransfer: Sinon.stub().resolves(undefined),
133
+ getRebalanceIntent: Sinon.stub().resolves(undefined),
134
+ getRebalanceAction: Sinon.stub().resolves(undefined),
135
+ getInProgressActions: Sinon.stub().resolves([]),
136
+ getPartiallyFulfilledInventoryIntents: Sinon.stub().resolves([]),
137
+ getActionsByType: Sinon.stub().resolves([]),
138
+ getActionsForIntent: Sinon.stub().resolves([]),
139
+ getInflightInventoryMovements: Sinon.stub().resolves(0n),
140
+ };
141
+ }
142
+
143
+ function createMockInflightContextAdapter(): InflightContextAdapter & {
144
+ getInflightContext: Sinon.SinonStub;
145
+ } {
146
+ return {
147
+ getInflightContext: Sinon.stub().resolves({
148
+ pendingRebalances: [],
149
+ pendingTransfers: [],
150
+ }),
151
+ } as unknown as InflightContextAdapter & {
152
+ getInflightContext: Sinon.SinonStub;
153
+ };
154
+ }
155
+
156
+ function createMockContextFactory(
157
+ overrides: {
158
+ warpCore?: WarpCore;
159
+ rebalancer?: IRebalancer;
160
+ strategy?: IStrategy;
161
+ actionTracker?: IActionTracker;
162
+ inflightAdapter?: InflightContextAdapter;
163
+ monitor?: Monitor;
164
+ metrics?: Metrics;
165
+ } = {},
166
+ ): RebalancerContextFactory {
167
+ const warpCore = overrides.warpCore ?? createMockWarpCore();
168
+ const rebalancer = overrides.rebalancer ?? createMockRebalancer();
169
+ const strategy = overrides.strategy ?? createMockStrategy();
170
+ const actionTracker = overrides.actionTracker ?? createMockActionTracker();
171
+ const inflightAdapter =
172
+ overrides.inflightAdapter ?? createMockInflightContextAdapter();
173
+ const monitor =
174
+ overrides.monitor ??
175
+ ({
176
+ on: Sinon.stub().returnsThis(),
177
+ start: Sinon.stub().resolves(),
178
+ stop: Sinon.stub().resolves(),
179
+ } as unknown as Monitor);
180
+
181
+ return {
182
+ getWarpCore: () => warpCore,
183
+ getTokenForChain: (chain: string) =>
184
+ warpCore.tokens.find((t) => t.chainName === chain),
185
+ createRebalancer: (_actionTracker: IActionTracker) => rebalancer,
186
+ createRebalancers: async (_actionTracker: IActionTracker) => ({
187
+ rebalancers: [rebalancer],
188
+ externalBridgeRegistry: {},
189
+ inventoryConfig: undefined,
190
+ }),
191
+ createStrategy: async () => strategy,
192
+ createMonitor: () => monitor,
193
+ createMetrics: async () => overrides.metrics ?? ({} as Metrics),
194
+ createActionTracker: async () => ({
195
+ tracker: actionTracker,
196
+ adapter: inflightAdapter,
197
+ }),
198
+ createOrchestrator: (options: {
199
+ strategy: IStrategy;
200
+ actionTracker: IActionTracker;
201
+ inflightContextAdapter: InflightContextAdapter;
202
+ rebalancers: IRebalancer[];
203
+ externalBridgeRegistry: Partial<ExternalBridgeRegistry>;
204
+ metrics?: Metrics;
205
+ }) => ({
206
+ executeCycle: Sinon.stub().callsFake(async (_event: any) => {
207
+ // Simulate orchestrator behavior: call strategy, then rebalancer, then record metrics
208
+ const strategyWithGetRoutes = options.strategy as any;
209
+ const routes = strategyWithGetRoutes.getRebalancingRoutes?.() ?? [];
210
+ if (routes.length > 0 && options.rebalancers[0]) {
211
+ const results = await options.rebalancers[0].rebalance(routes);
212
+ if (options.metrics && results) {
213
+ results.forEach((result: any) => {
214
+ if (result.success) {
215
+ (options.metrics as any).recordRebalancerSuccess?.();
216
+ } else {
217
+ (options.metrics as any).recordRebalancerFailure?.();
218
+ }
219
+ });
220
+ }
221
+ }
222
+ return { success: true };
223
+ }),
224
+ }),
225
+ } as unknown as RebalancerContextFactory;
226
+ }
227
+
228
+ interface DaemonTestSetup {
229
+ actionTracker: IActionTracker;
230
+ rebalancer: IRebalancer & { rebalance: Sinon.SinonStub };
231
+ strategy: IStrategy & { getRebalancingRoutes: Sinon.SinonStub };
232
+ triggerCycle: () => Promise<void>;
233
+ }
234
+
235
+ async function setupDaemonTest(
236
+ sandbox: Sinon.SinonSandbox,
237
+ options: {
238
+ rebalanceResults: Array<{
239
+ route: {
240
+ origin: string;
241
+ destination: string;
242
+ amount: bigint;
243
+ bridge: string;
244
+ };
245
+ success: boolean;
246
+ messageId?: string;
247
+ txHash?: string;
248
+ error?: string;
249
+ }>;
250
+ strategyRoutes: Array<{
251
+ origin: string;
252
+ destination: string;
253
+ amount: bigint;
254
+ bridge: string;
255
+ }>;
256
+ },
257
+ ): Promise<DaemonTestSetup> {
258
+ const actionTracker = createMockActionTracker();
259
+
260
+ const rebalancer = createMockRebalancer();
261
+ rebalancer.rebalance.resolves(options.rebalanceResults);
262
+
263
+ const strategy = createMockStrategy();
264
+ strategy.getRebalancingRoutes.returns(options.strategyRoutes);
265
+
266
+ const inflightAdapter = createMockInflightContextAdapter();
267
+
268
+ let tokenInfoHandler: ((event: any) => Promise<void>) | undefined;
269
+ const monitor = {
270
+ on: Sinon.stub().callsFake((event: string, handler: any) => {
271
+ if (event === MonitorEventType.TokenInfo) {
272
+ tokenInfoHandler = handler;
273
+ }
274
+ return monitor;
275
+ }),
276
+ start: Sinon.stub().resolves(),
277
+ stop: Sinon.stub().resolves(),
278
+ } as unknown as Monitor;
279
+
280
+ const contextFactory = createMockContextFactory({
281
+ rebalancer,
282
+ strategy,
283
+ actionTracker,
284
+ inflightAdapter,
285
+ monitor,
286
+ });
287
+ sandbox.stub(RebalancerContextFactory, 'create').resolves(contextFactory);
288
+
289
+ const service = new RebalancerService(
290
+ createMockMultiProvider(),
291
+ undefined,
292
+ {} as any,
293
+ createMockRebalancerConfig(),
294
+ { mode: 'daemon', checkFrequency: 60000, logger: testLogger },
295
+ );
296
+
297
+ await service.start();
298
+
299
+ return {
300
+ actionTracker,
301
+ rebalancer,
302
+ strategy,
303
+ triggerCycle: async () => {
304
+ expect(tokenInfoHandler).to.not.be.undefined;
305
+ await tokenInfoHandler!({
306
+ tokensInfo: [
307
+ { token: createMockToken('ethereum'), bridgedSupply: 5000n },
308
+ { token: createMockToken('arbitrum'), bridgedSupply: 5000n },
309
+ ],
310
+ });
311
+ },
312
+ };
313
+ }
314
+
315
+ describe('RebalancerService', () => {
316
+ let sandbox: Sinon.SinonSandbox;
317
+
318
+ beforeEach(() => {
319
+ sandbox = Sinon.createSandbox();
320
+ });
321
+
322
+ afterEach(() => {
323
+ sandbox.restore();
324
+ });
325
+
326
+ describe('executeManual()', () => {
327
+ it('should execute manual rebalance successfully', async () => {
328
+ const rebalancer = createMockRebalancer();
329
+ rebalancer.rebalance.resolves([
330
+ {
331
+ route: { origin: 'ethereum', destination: 'arbitrum', amount: 1000n },
332
+ success: true,
333
+ messageId:
334
+ '0x1111111111111111111111111111111111111111111111111111111111111111',
335
+ txHash:
336
+ '0x2222222222222222222222222222222222222222222222222222222222222222',
337
+ },
338
+ ]);
339
+
340
+ const contextFactory = createMockContextFactory({ rebalancer });
341
+ sandbox.stub(RebalancerContextFactory, 'create').resolves(contextFactory);
342
+
343
+ const config: RebalancerServiceConfig = {
344
+ mode: 'manual',
345
+ logger: testLogger,
346
+ };
347
+
348
+ const service = new RebalancerService(
349
+ createMockMultiProvider(),
350
+ undefined,
351
+ {} as any,
352
+ createMockRebalancerConfig(),
353
+ config,
354
+ );
355
+
356
+ await service.executeManual({
357
+ origin: 'ethereum',
358
+ destination: 'arbitrum',
359
+ amount: '100',
360
+ });
361
+
362
+ expect(rebalancer.rebalance.calledOnce).to.be.true;
363
+ const calledRoutes = rebalancer.rebalance.firstCall.args[0];
364
+ expect(calledRoutes).to.have.lengthOf(1);
365
+ expect(calledRoutes[0].origin).to.equal('ethereum');
366
+ expect(calledRoutes[0].destination).to.equal('arbitrum');
367
+ });
368
+
369
+ it('should throw when origin token not found', async () => {
370
+ const warpCore = {
371
+ tokens: [createMockToken('arbitrum')],
372
+ multiProvider: createMockMultiProvider(),
373
+ } as unknown as WarpCore;
374
+
375
+ const contextFactory = createMockContextFactory({ warpCore });
376
+ sandbox.stub(RebalancerContextFactory, 'create').resolves(contextFactory);
377
+
378
+ const config: RebalancerServiceConfig = {
379
+ mode: 'manual',
380
+ logger: testLogger,
381
+ };
382
+
383
+ const service = new RebalancerService(
384
+ createMockMultiProvider(),
385
+ undefined,
386
+ {} as any,
387
+ createMockRebalancerConfig(),
388
+ config,
389
+ );
390
+
391
+ await expect(
392
+ service.executeManual({
393
+ origin: 'ethereum',
394
+ destination: 'arbitrum',
395
+ amount: '100',
396
+ }),
397
+ ).to.be.rejectedWith('Origin token not found');
398
+ });
399
+
400
+ it('should throw when amount is invalid', async () => {
401
+ const contextFactory = createMockContextFactory();
402
+ sandbox.stub(RebalancerContextFactory, 'create').resolves(contextFactory);
403
+
404
+ const config: RebalancerServiceConfig = {
405
+ mode: 'manual',
406
+ logger: testLogger,
407
+ };
408
+
409
+ const service = new RebalancerService(
410
+ createMockMultiProvider(),
411
+ undefined,
412
+ {} as any,
413
+ createMockRebalancerConfig(),
414
+ config,
415
+ );
416
+
417
+ await expect(
418
+ service.executeManual({
419
+ origin: 'ethereum',
420
+ destination: 'arbitrum',
421
+ amount: 'invalid',
422
+ }),
423
+ ).to.be.rejectedWith('Amount must be a valid number');
424
+ });
425
+
426
+ it('should throw when amount is zero or negative', async () => {
427
+ const contextFactory = createMockContextFactory();
428
+ sandbox.stub(RebalancerContextFactory, 'create').resolves(contextFactory);
429
+
430
+ const config: RebalancerServiceConfig = {
431
+ mode: 'manual',
432
+ logger: testLogger,
433
+ };
434
+
435
+ const service = new RebalancerService(
436
+ createMockMultiProvider(),
437
+ undefined,
438
+ {} as any,
439
+ createMockRebalancerConfig(),
440
+ config,
441
+ );
442
+
443
+ await expect(
444
+ service.executeManual({
445
+ origin: 'ethereum',
446
+ destination: 'arbitrum',
447
+ amount: '0',
448
+ }),
449
+ ).to.be.rejectedWith('Amount must be greater than 0');
450
+
451
+ await expect(
452
+ service.executeManual({
453
+ origin: 'ethereum',
454
+ destination: 'arbitrum',
455
+ amount: '-100',
456
+ }),
457
+ ).to.be.rejectedWith('Amount must be greater than 0');
458
+ });
459
+
460
+ it('should throw when origin chain has no bridge configured', async () => {
461
+ const contextFactory = createMockContextFactory();
462
+ sandbox.stub(RebalancerContextFactory, 'create').resolves(contextFactory);
463
+
464
+ const configWithoutBridge: RebalancerConfig = {
465
+ warpRouteId: 'TEST/route',
466
+ strategyConfig: [
467
+ {
468
+ rebalanceStrategy: RebalancerStrategyOptions.Weighted,
469
+ chains: {
470
+ arbitrum: {
471
+ bridge: TEST_ADDRESSES.bridge,
472
+ bridgeMinAcceptedAmount: 0,
473
+ weighted: { weight: 100n, tolerance: 10n },
474
+ },
475
+ },
476
+ },
477
+ ],
478
+ intentTTL: DEFAULT_INTENT_TTL_MS,
479
+ } as RebalancerConfig;
480
+
481
+ const config: RebalancerServiceConfig = {
482
+ mode: 'manual',
483
+ logger: testLogger,
484
+ };
485
+
486
+ const service = new RebalancerService(
487
+ createMockMultiProvider(),
488
+ undefined,
489
+ {} as any,
490
+ configWithoutBridge,
491
+ config,
492
+ );
493
+
494
+ await expect(
495
+ service.executeManual({
496
+ origin: 'ethereum',
497
+ destination: 'arbitrum',
498
+ amount: '100',
499
+ }),
500
+ ).to.be.rejectedWith('No bridge configured for origin chain ethereum');
501
+ });
502
+
503
+ it('should throw when in monitorOnly mode', async () => {
504
+ const contextFactory = createMockContextFactory();
505
+ sandbox.stub(RebalancerContextFactory, 'create').resolves(contextFactory);
506
+
507
+ const config: RebalancerServiceConfig = {
508
+ mode: 'manual',
509
+ monitorOnly: true,
510
+ logger: testLogger,
511
+ };
512
+
513
+ const service = new RebalancerService(
514
+ createMockMultiProvider(),
515
+ undefined,
516
+ {} as any,
517
+ createMockRebalancerConfig(),
518
+ config,
519
+ );
520
+
521
+ await expect(
522
+ service.executeManual({
523
+ origin: 'ethereum',
524
+ destination: 'arbitrum',
525
+ amount: '100',
526
+ }),
527
+ ).to.be.rejectedWith('MonitorOnly mode cannot execute manual rebalances');
528
+ });
529
+
530
+ it('should propagate errors from rebalancer', async () => {
531
+ const rebalancer = createMockRebalancer();
532
+ rebalancer.rebalance.rejects(new Error('Rebalance failed'));
533
+
534
+ const contextFactory = createMockContextFactory({ rebalancer });
535
+ sandbox.stub(RebalancerContextFactory, 'create').resolves(contextFactory);
536
+
537
+ const config: RebalancerServiceConfig = {
538
+ mode: 'manual',
539
+ logger: testLogger,
540
+ };
541
+
542
+ const service = new RebalancerService(
543
+ createMockMultiProvider(),
544
+ undefined,
545
+ {} as any,
546
+ createMockRebalancerConfig(),
547
+ config,
548
+ );
549
+
550
+ await expect(
551
+ service.executeManual({
552
+ origin: 'ethereum',
553
+ destination: 'arbitrum',
554
+ amount: '100',
555
+ }),
556
+ ).to.be.rejectedWith('Rebalance failed');
557
+ });
558
+ });
559
+
560
+ describe('start()', () => {
561
+ it('should throw when not in daemon mode', async () => {
562
+ const config: RebalancerServiceConfig = {
563
+ mode: 'manual',
564
+ logger: testLogger,
565
+ };
566
+
567
+ const service = new RebalancerService(
568
+ createMockMultiProvider(),
569
+ undefined,
570
+ {} as any,
571
+ createMockRebalancerConfig(),
572
+ config,
573
+ );
574
+
575
+ await expect(service.start()).to.be.rejectedWith(
576
+ 'start() can only be called in daemon mode',
577
+ );
578
+ });
579
+
580
+ it('should start monitor in daemon mode', async () => {
581
+ const monitor = {
582
+ on: Sinon.stub().returnsThis(),
583
+ start: Sinon.stub().resolves(),
584
+ stop: Sinon.stub().resolves(),
585
+ } as unknown as Monitor;
586
+
587
+ const contextFactory = createMockContextFactory({ monitor });
588
+ sandbox.stub(RebalancerContextFactory, 'create').resolves(contextFactory);
589
+
590
+ const config: RebalancerServiceConfig = {
591
+ mode: 'daemon',
592
+ checkFrequency: 60000,
593
+ logger: testLogger,
594
+ };
595
+
596
+ const service = new RebalancerService(
597
+ createMockMultiProvider(),
598
+ undefined,
599
+ {} as any,
600
+ createMockRebalancerConfig(),
601
+ config,
602
+ );
603
+
604
+ await service.start();
605
+
606
+ expect((monitor.on as Sinon.SinonStub).called).to.be.true;
607
+ expect((monitor.start as Sinon.SinonStub).calledOnce).to.be.true;
608
+ });
609
+ });
610
+
611
+ describe('stop()', () => {
612
+ it('should stop monitor', async () => {
613
+ const monitor = {
614
+ on: Sinon.stub().returnsThis(),
615
+ start: Sinon.stub().resolves(),
616
+ stop: Sinon.stub().resolves(),
617
+ } as unknown as Monitor;
618
+
619
+ const contextFactory = createMockContextFactory({ monitor });
620
+ sandbox.stub(RebalancerContextFactory, 'create').resolves(contextFactory);
621
+
622
+ const config: RebalancerServiceConfig = {
623
+ mode: 'daemon',
624
+ checkFrequency: 60000,
625
+ logger: testLogger,
626
+ };
627
+
628
+ const service = new RebalancerService(
629
+ createMockMultiProvider(),
630
+ undefined,
631
+ {} as any,
632
+ createMockRebalancerConfig(),
633
+ config,
634
+ );
635
+
636
+ await service.start();
637
+ await service.stop();
638
+
639
+ expect((monitor.stop as Sinon.SinonStub).calledOnce).to.be.true;
640
+ });
641
+ });
642
+
643
+ describe('daemon mode metrics', () => {
644
+ it('should record failure metric when rebalance has failed results', async () => {
645
+ const rebalancer = createMockRebalancer();
646
+ rebalancer.rebalance.resolves([
647
+ {
648
+ route: {
649
+ origin: 'ethereum',
650
+ destination: 'arbitrum',
651
+ amount: 1000n,
652
+ intentId: 'intent-1',
653
+ bridge: TEST_ADDRESSES.bridge,
654
+ },
655
+ success: false,
656
+ error: 'Gas estimation failed',
657
+ },
658
+ ]);
659
+
660
+ const strategy = createMockStrategy();
661
+ strategy.getRebalancingRoutes.returns([
662
+ {
663
+ origin: 'ethereum',
664
+ destination: 'arbitrum',
665
+ amount: 1000n,
666
+ bridge: TEST_ADDRESSES.bridge,
667
+ },
668
+ ]);
669
+
670
+ const actionTracker = createMockActionTracker();
671
+ const inflightAdapter = createMockInflightContextAdapter();
672
+
673
+ const recordRebalancerSuccess = Sinon.stub();
674
+ const recordRebalancerFailure = Sinon.stub();
675
+ const metrics = {
676
+ recordRebalancerSuccess,
677
+ recordRebalancerFailure,
678
+ recordIntentCreated: Sinon.stub(),
679
+ processToken: Sinon.stub().resolves(),
680
+ } as unknown as Metrics;
681
+
682
+ let tokenInfoHandler: ((event: any) => Promise<void>) | undefined;
683
+ const monitor = {
684
+ on: Sinon.stub().callsFake((event: string, handler: any) => {
685
+ if (event === MonitorEventType.TokenInfo) {
686
+ tokenInfoHandler = handler;
687
+ }
688
+ return monitor;
689
+ }),
690
+ start: Sinon.stub().resolves(),
691
+ stop: Sinon.stub().resolves(),
692
+ } as unknown as Monitor;
693
+
694
+ const contextFactory = createMockContextFactory({
695
+ rebalancer,
696
+ strategy,
697
+ actionTracker,
698
+ inflightAdapter,
699
+ monitor,
700
+ metrics,
701
+ });
702
+ sandbox.stub(RebalancerContextFactory, 'create').resolves(contextFactory);
703
+
704
+ const config: RebalancerServiceConfig = {
705
+ mode: 'daemon',
706
+ checkFrequency: 60000,
707
+ withMetrics: true,
708
+ logger: testLogger,
709
+ };
710
+
711
+ const service = new RebalancerService(
712
+ createMockMultiProvider(),
713
+ undefined,
714
+ {} as any,
715
+ createMockRebalancerConfig(),
716
+ config,
717
+ );
718
+
719
+ await service.start();
720
+
721
+ expect(tokenInfoHandler).to.not.be.undefined;
722
+ await tokenInfoHandler!({
723
+ tokensInfo: [
724
+ { token: createMockToken('ethereum'), bridgedSupply: 5000n },
725
+ { token: createMockToken('arbitrum'), bridgedSupply: 5000n },
726
+ ],
727
+ });
728
+
729
+ expect(recordRebalancerFailure.calledOnce).to.be.true;
730
+ expect(recordRebalancerSuccess.called).to.be.false;
731
+ });
732
+
733
+ it('should record success metric when all rebalance results succeed', async () => {
734
+ const rebalancer = createMockRebalancer();
735
+ rebalancer.rebalance.resolves([
736
+ {
737
+ route: {
738
+ origin: 'ethereum',
739
+ destination: 'arbitrum',
740
+ amount: 1000n,
741
+ intentId: 'intent-1',
742
+ bridge: TEST_ADDRESSES.bridge,
743
+ },
744
+ success: true,
745
+ messageId:
746
+ '0x1111111111111111111111111111111111111111111111111111111111111111',
747
+ txHash:
748
+ '0x2222222222222222222222222222222222222222222222222222222222222222',
749
+ },
750
+ ]);
751
+
752
+ const strategy = createMockStrategy();
753
+ strategy.getRebalancingRoutes.returns([
754
+ {
755
+ origin: 'ethereum',
756
+ destination: 'arbitrum',
757
+ amount: 1000n,
758
+ bridge: TEST_ADDRESSES.bridge,
759
+ },
760
+ ]);
761
+
762
+ const actionTracker = createMockActionTracker();
763
+ const inflightAdapter = createMockInflightContextAdapter();
764
+
765
+ const recordRebalancerSuccess = Sinon.stub();
766
+ const recordRebalancerFailure = Sinon.stub();
767
+ const metrics = {
768
+ recordRebalancerSuccess,
769
+ recordRebalancerFailure,
770
+ recordIntentCreated: Sinon.stub(),
771
+ processToken: Sinon.stub().resolves(),
772
+ } as unknown as Metrics;
773
+
774
+ let tokenInfoHandler: ((event: any) => Promise<void>) | undefined;
775
+ const monitor = {
776
+ on: Sinon.stub().callsFake((event: string, handler: any) => {
777
+ if (event === MonitorEventType.TokenInfo) {
778
+ tokenInfoHandler = handler;
779
+ }
780
+ return monitor;
781
+ }),
782
+ start: Sinon.stub().resolves(),
783
+ stop: Sinon.stub().resolves(),
784
+ } as unknown as Monitor;
785
+
786
+ const contextFactory = createMockContextFactory({
787
+ rebalancer,
788
+ strategy,
789
+ actionTracker,
790
+ inflightAdapter,
791
+ monitor,
792
+ metrics,
793
+ });
794
+ sandbox.stub(RebalancerContextFactory, 'create').resolves(contextFactory);
795
+
796
+ const config: RebalancerServiceConfig = {
797
+ mode: 'daemon',
798
+ checkFrequency: 60000,
799
+ withMetrics: true,
800
+ logger: testLogger,
801
+ };
802
+
803
+ const service = new RebalancerService(
804
+ createMockMultiProvider(),
805
+ undefined,
806
+ {} as any,
807
+ createMockRebalancerConfig(),
808
+ config,
809
+ );
810
+
811
+ await service.start();
812
+
813
+ expect(tokenInfoHandler).to.not.be.undefined;
814
+ await tokenInfoHandler!({
815
+ tokensInfo: [
816
+ { token: createMockToken('ethereum'), bridgedSupply: 5000n },
817
+ { token: createMockToken('arbitrum'), bridgedSupply: 5000n },
818
+ ],
819
+ });
820
+
821
+ expect(recordRebalancerSuccess.calledOnce).to.be.true;
822
+ expect(recordRebalancerFailure.called).to.be.false;
823
+ });
824
+
825
+ it('should record failure metric when rebalance has mixed results', async () => {
826
+ const rebalancer = createMockRebalancer();
827
+ rebalancer.rebalance.resolves([
828
+ {
829
+ route: {
830
+ origin: 'ethereum',
831
+ destination: 'arbitrum',
832
+ amount: 1000n,
833
+ intentId: 'intent-1',
834
+ bridge: TEST_ADDRESSES.bridge,
835
+ },
836
+ success: true,
837
+ messageId:
838
+ '0x1111111111111111111111111111111111111111111111111111111111111111',
839
+ txHash:
840
+ '0x2222222222222222222222222222222222222222222222222222222222222222',
841
+ },
842
+ {
843
+ route: {
844
+ origin: 'arbitrum',
845
+ destination: 'ethereum',
846
+ amount: 500n,
847
+ intentId: 'intent-2',
848
+ bridge: TEST_ADDRESSES.bridge,
849
+ },
850
+ success: false,
851
+ error: 'Insufficient balance',
852
+ },
853
+ ]);
854
+
855
+ const strategy = createMockStrategy();
856
+ strategy.getRebalancingRoutes.returns([
857
+ {
858
+ origin: 'ethereum',
859
+ destination: 'arbitrum',
860
+ amount: 1000n,
861
+ bridge: TEST_ADDRESSES.bridge,
862
+ },
863
+ {
864
+ origin: 'arbitrum',
865
+ destination: 'ethereum',
866
+ amount: 500n,
867
+ bridge: TEST_ADDRESSES.bridge,
868
+ },
869
+ ]);
870
+
871
+ const actionTracker = createMockActionTracker();
872
+ const inflightAdapter = createMockInflightContextAdapter();
873
+
874
+ const recordRebalancerSuccess = Sinon.stub();
875
+ const recordRebalancerFailure = Sinon.stub();
876
+ const metrics = {
877
+ recordRebalancerSuccess,
878
+ recordRebalancerFailure,
879
+ recordIntentCreated: Sinon.stub(),
880
+ processToken: Sinon.stub().resolves(),
881
+ } as unknown as Metrics;
882
+
883
+ let tokenInfoHandler: ((event: any) => Promise<void>) | undefined;
884
+ const monitor = {
885
+ on: Sinon.stub().callsFake((event: string, handler: any) => {
886
+ if (event === MonitorEventType.TokenInfo) {
887
+ tokenInfoHandler = handler;
888
+ }
889
+ return monitor;
890
+ }),
891
+ start: Sinon.stub().resolves(),
892
+ stop: Sinon.stub().resolves(),
893
+ } as unknown as Monitor;
894
+
895
+ const contextFactory = createMockContextFactory({
896
+ rebalancer,
897
+ strategy,
898
+ actionTracker,
899
+ inflightAdapter,
900
+ monitor,
901
+ metrics,
902
+ });
903
+ sandbox.stub(RebalancerContextFactory, 'create').resolves(contextFactory);
904
+
905
+ const config: RebalancerServiceConfig = {
906
+ mode: 'daemon',
907
+ checkFrequency: 60000,
908
+ withMetrics: true,
909
+ logger: testLogger,
910
+ };
911
+
912
+ const service = new RebalancerService(
913
+ createMockMultiProvider(),
914
+ undefined,
915
+ {} as any,
916
+ createMockRebalancerConfig(),
917
+ config,
918
+ );
919
+
920
+ await service.start();
921
+
922
+ expect(tokenInfoHandler).to.not.be.undefined;
923
+ await tokenInfoHandler!({
924
+ tokensInfo: [
925
+ { token: createMockToken('ethereum'), bridgedSupply: 5000n },
926
+ { token: createMockToken('arbitrum'), bridgedSupply: 5000n },
927
+ ],
928
+ });
929
+
930
+ expect(recordRebalancerFailure.calledOnce).to.be.true;
931
+ expect(recordRebalancerSuccess.calledOnce).to.be.true;
932
+ });
933
+ });
934
+
935
+ describe('daemon mode rebalancer calls', () => {
936
+ it('should call rebalancer with routes from strategy', async () => {
937
+ const { rebalancer, triggerCycle } = await setupDaemonTest(sandbox, {
938
+ rebalanceResults: [
939
+ {
940
+ route: {
941
+ origin: 'ethereum',
942
+ destination: 'arbitrum',
943
+ amount: 1000n,
944
+ bridge: TEST_ADDRESSES.bridge,
945
+ },
946
+ success: true,
947
+ messageId:
948
+ '0x1111111111111111111111111111111111111111111111111111111111111111',
949
+ },
950
+ ],
951
+ strategyRoutes: [
952
+ {
953
+ origin: 'ethereum',
954
+ destination: 'arbitrum',
955
+ amount: 1000n,
956
+ bridge: TEST_ADDRESSES.bridge,
957
+ },
958
+ ],
959
+ });
960
+
961
+ await triggerCycle();
962
+
963
+ expect(rebalancer.rebalance.calledOnce).to.be.true;
964
+ const routesPassedToRebalancer = rebalancer.rebalance.firstCall.args[0];
965
+ expect(routesPassedToRebalancer).to.have.lengthOf(1);
966
+ expect(routesPassedToRebalancer[0].origin).to.equal('ethereum');
967
+ expect(routesPassedToRebalancer[0].destination).to.equal('arbitrum');
968
+ });
969
+
970
+ it('should call rebalancer with multiple routes', async () => {
971
+ const { rebalancer, triggerCycle } = await setupDaemonTest(sandbox, {
972
+ rebalanceResults: [
973
+ {
974
+ route: {
975
+ origin: 'ethereum',
976
+ destination: 'arbitrum',
977
+ amount: 1000n,
978
+ bridge: TEST_ADDRESSES.bridge,
979
+ },
980
+ success: true,
981
+ },
982
+ {
983
+ route: {
984
+ origin: 'arbitrum',
985
+ destination: 'ethereum',
986
+ amount: 500n,
987
+ bridge: TEST_ADDRESSES.bridge,
988
+ },
989
+ success: true,
990
+ },
991
+ ],
992
+ strategyRoutes: [
993
+ {
994
+ origin: 'ethereum',
995
+ destination: 'arbitrum',
996
+ amount: 1000n,
997
+ bridge: TEST_ADDRESSES.bridge,
998
+ },
999
+ {
1000
+ origin: 'arbitrum',
1001
+ destination: 'ethereum',
1002
+ amount: 500n,
1003
+ bridge: TEST_ADDRESSES.bridge,
1004
+ },
1005
+ ],
1006
+ });
1007
+
1008
+ await triggerCycle();
1009
+
1010
+ expect(rebalancer.rebalance.calledOnce).to.be.true;
1011
+ const routesPassedToRebalancer = rebalancer.rebalance.firstCall.args[0];
1012
+ expect(routesPassedToRebalancer).to.have.lengthOf(2);
1013
+ });
1014
+
1015
+ it('should not call rebalancer when no routes proposed', async () => {
1016
+ const { rebalancer, triggerCycle } = await setupDaemonTest(sandbox, {
1017
+ rebalanceResults: [],
1018
+ strategyRoutes: [],
1019
+ });
1020
+
1021
+ await triggerCycle();
1022
+
1023
+ expect(rebalancer.rebalance.called).to.be.false;
1024
+ });
1025
+ });
1026
+
1027
+ describe('initialization', () => {
1028
+ it('should initialize only once', async () => {
1029
+ const contextFactory = createMockContextFactory();
1030
+ const createStub = sandbox
1031
+ .stub(RebalancerContextFactory, 'create')
1032
+ .resolves(contextFactory);
1033
+
1034
+ const config: RebalancerServiceConfig = {
1035
+ mode: 'manual',
1036
+ logger: testLogger,
1037
+ };
1038
+
1039
+ const service = new RebalancerService(
1040
+ createMockMultiProvider(),
1041
+ undefined,
1042
+ {} as any,
1043
+ createMockRebalancerConfig(),
1044
+ config,
1045
+ );
1046
+
1047
+ await service.executeManual({
1048
+ origin: 'ethereum',
1049
+ destination: 'arbitrum',
1050
+ amount: '100',
1051
+ });
1052
+
1053
+ await service.executeManual({
1054
+ origin: 'ethereum',
1055
+ destination: 'arbitrum',
1056
+ amount: '200',
1057
+ });
1058
+
1059
+ expect(createStub.calledOnce).to.be.true;
1060
+ });
1061
+
1062
+ it('should create metrics when withMetrics is enabled', async () => {
1063
+ const metrics = {} as Metrics;
1064
+ const contextFactory = createMockContextFactory({ metrics });
1065
+ const createMetricsSpy = Sinon.spy(contextFactory, 'createMetrics');
1066
+
1067
+ sandbox.stub(RebalancerContextFactory, 'create').resolves(contextFactory);
1068
+
1069
+ const config: RebalancerServiceConfig = {
1070
+ mode: 'manual',
1071
+ withMetrics: true,
1072
+ coingeckoApiKey: 'test-key',
1073
+ logger: testLogger,
1074
+ };
1075
+
1076
+ const service = new RebalancerService(
1077
+ createMockMultiProvider(),
1078
+ undefined,
1079
+ {} as any,
1080
+ createMockRebalancerConfig(),
1081
+ config,
1082
+ );
1083
+
1084
+ await service.executeManual({
1085
+ origin: 'ethereum',
1086
+ destination: 'arbitrum',
1087
+ amount: '100',
1088
+ });
1089
+
1090
+ expect(createMetricsSpy.calledOnce).to.be.true;
1091
+ expect(createMetricsSpy.firstCall.args[0]).to.equal('test-key');
1092
+ });
1093
+ });
1094
+ });