@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,1019 @@
1
+ import type { Logger } from 'pino';
2
+ import { v4 as uuidv4 } from 'uuid';
3
+
4
+ import type { MultiProtocolCore } from '@humanode-hyperlane/sdk';
5
+ import type { Address, Domain } from '@humanode-hyperlane/utils';
6
+ import { assert, parseWarpRouteMessage } from '@humanode-hyperlane/utils';
7
+
8
+ import { DEFAULT_MOVEMENT_STALENESS_MS } from '../config/types.js';
9
+ import type { ExternalBridgeRegistry } from '../interfaces/IExternalBridge.js';
10
+ import type { ConfirmedBlockTags } from '../interfaces/IMonitor.js';
11
+ import type {
12
+ ExplorerMessage,
13
+ IExplorerClient,
14
+ } from '../utils/ExplorerClient.js';
15
+ import { getConfirmedBlockTag } from '../utils/blockTag.js';
16
+
17
+ import type {
18
+ CreateRebalanceActionParams,
19
+ CreateRebalanceIntentParams,
20
+ IActionTracker,
21
+ } from './IActionTracker.js';
22
+ import type {
23
+ ActionType,
24
+ IRebalanceActionStore,
25
+ IRebalanceIntentStore,
26
+ ITransferStore,
27
+ PartialInventoryIntent,
28
+ RebalanceAction,
29
+ RebalanceIntent,
30
+ Transfer,
31
+ } from './types.js';
32
+
33
+ export interface ActionTrackerConfig {
34
+ routersByDomain: Record<number, string>; // Domain ID → router address (source of truth for routers and domains)
35
+ bridges: Address[]; // Bridge contract addresses for rebalance action queries
36
+ rebalancerAddress: Address;
37
+ inventorySignerAddresses?: Address[]; // Optional - for excluding inventory signers from user transfers query
38
+ intentTTL: number; // Max age in ms before in-progress intent is expired
39
+ }
40
+
41
+ /**
42
+ * ActionTracker implementation managing the lifecycle of tracked entities.
43
+ */
44
+ export class ActionTracker implements IActionTracker {
45
+ constructor(
46
+ private readonly transferStore: ITransferStore,
47
+ private readonly rebalanceIntentStore: IRebalanceIntentStore,
48
+ private readonly rebalanceActionStore: IRebalanceActionStore,
49
+ private readonly explorerClient: IExplorerClient,
50
+ private readonly core: MultiProtocolCore,
51
+ private readonly config: ActionTrackerConfig,
52
+ private readonly logger: Logger,
53
+ ) {}
54
+
55
+ // === Lifecycle ===
56
+
57
+ async initialize(): Promise<void> {
58
+ this.logger.info('ActionTracker initializing');
59
+
60
+ // Log config for debugging
61
+ this.logger.debug(
62
+ {
63
+ routersByDomain: this.config.routersByDomain,
64
+ bridges: this.config.bridges,
65
+ rebalancerAddress: this.config.rebalancerAddress,
66
+ },
67
+ 'ActionTracker config',
68
+ );
69
+
70
+ // 1. Startup recovery: query Explorer for inflight rebalance messages
71
+ const inflightMessages =
72
+ await this.explorerClient.getInflightRebalanceActions(
73
+ {
74
+ bridges: this.config.bridges,
75
+ routersByDomain: this.config.routersByDomain,
76
+ rebalancerAddress: this.config.rebalancerAddress,
77
+ inventorySignerAddresses: this.config.inventorySignerAddresses,
78
+ },
79
+ this.logger,
80
+ );
81
+
82
+ this.logger.info(
83
+ { count: inflightMessages.length },
84
+ 'Found inflight rebalance messages during startup',
85
+ );
86
+
87
+ // 2. For each message, create synthetic intent + action
88
+ for (const msg of inflightMessages) {
89
+ await this.recoverAction(msg);
90
+ }
91
+
92
+ // 3. Sync all stores
93
+ await this.syncTransfers();
94
+ await this.syncRebalanceIntents();
95
+ await this.syncRebalanceActions();
96
+
97
+ // Log store contents for debugging
98
+ await this.logStoreContents();
99
+
100
+ this.logger.info('ActionTracker initialized');
101
+ }
102
+
103
+ // === Sync Operations ===
104
+
105
+ async syncTransfers(confirmedBlockTags?: ConfirmedBlockTags): Promise<void> {
106
+ this.logger.debug('Syncing transfers');
107
+
108
+ // Build list of addresses to exclude (rebalancer + optional inventory signer)
109
+ const excludeTxSenders = [this.config.rebalancerAddress];
110
+ if (this.config.inventorySignerAddresses) {
111
+ excludeTxSenders.push(...this.config.inventorySignerAddresses);
112
+ }
113
+
114
+ const inflightMessages = await this.explorerClient.getInflightUserTransfers(
115
+ {
116
+ routersByDomain: this.config.routersByDomain,
117
+ excludeTxSenders,
118
+ },
119
+ this.logger,
120
+ );
121
+
122
+ this.logger.debug(
123
+ { count: inflightMessages.length },
124
+ 'Received inflight user transfers from Explorer',
125
+ );
126
+
127
+ let newTransfers = 0;
128
+ let completedTransfers = 0;
129
+
130
+ for (const msg of inflightMessages) {
131
+ const transfer = await this.transferStore.get(msg.msg_id);
132
+
133
+ if (!transfer) {
134
+ this.logger.debug(
135
+ {
136
+ msgId: msg.msg_id,
137
+ origin: msg.origin_domain_id,
138
+ destination: msg.destination_domain_id,
139
+ sender: msg.sender,
140
+ recipient: msg.recipient,
141
+ messageBodyLength: msg.message_body?.length,
142
+ messageBodyPreview: msg.message_body?.substring(0, 66),
143
+ },
144
+ 'Processing new transfer message',
145
+ );
146
+
147
+ try {
148
+ const { amount } = parseWarpRouteMessage(msg.message_body);
149
+ const newTransfer: Transfer = {
150
+ id: msg.msg_id,
151
+ status: 'in_progress',
152
+ messageId: msg.msg_id,
153
+ origin: msg.origin_domain_id,
154
+ destination: msg.destination_domain_id,
155
+ sender: msg.sender,
156
+ recipient: msg.recipient,
157
+ amount,
158
+ createdAt: Date.now(),
159
+ updatedAt: Date.now(),
160
+ };
161
+ await this.transferStore.save(newTransfer);
162
+ newTransfers++;
163
+ this.logger.debug(
164
+ { id: newTransfer.id, amount: amount.toString() },
165
+ 'Created new transfer',
166
+ );
167
+ } catch (error) {
168
+ this.logger.warn(
169
+ {
170
+ msgId: msg.msg_id,
171
+ messageBody: msg.message_body,
172
+ messageBodyLength: msg.message_body?.length,
173
+ origin: msg.origin_domain_id,
174
+ destination: msg.destination_domain_id,
175
+ error: error instanceof Error ? error.message : String(error),
176
+ },
177
+ 'Failed to parse message body, skipping transfer',
178
+ );
179
+ }
180
+ }
181
+ }
182
+
183
+ const existingTransfers = await this.getInProgressTransfers();
184
+ for (const transfer of existingTransfers) {
185
+ const blockTag = await this.getConfirmedBlockTag(
186
+ transfer.destination,
187
+ confirmedBlockTags,
188
+ );
189
+ const delivered = await this.isMessageDelivered(
190
+ transfer.messageId,
191
+ transfer.destination,
192
+ blockTag,
193
+ );
194
+
195
+ if (delivered) {
196
+ await this.transferStore.update(transfer.id, { status: 'complete' });
197
+ completedTransfers++;
198
+ this.logger.debug({ id: transfer.id }, 'Transfer completed');
199
+ }
200
+ }
201
+
202
+ const inProgressCount = (await this.getInProgressTransfers()).length;
203
+ this.logger.info(
204
+ {
205
+ newTransfers,
206
+ completed: completedTransfers,
207
+ inProgress: inProgressCount,
208
+ },
209
+ 'Transfers synced',
210
+ );
211
+ }
212
+
213
+ async syncRebalanceIntents(): Promise<void> {
214
+ this.logger.debug('Syncing rebalance intents');
215
+
216
+ // Check in_progress intents for completion or TTL expiry
217
+ const inProgressIntents =
218
+ await this.rebalanceIntentStore.getByStatus('in_progress');
219
+ const allInProgressActions =
220
+ await this.rebalanceActionStore.getByStatus('in_progress');
221
+ const now = Date.now();
222
+ for (const intent of inProgressIntents) {
223
+ const completedAmount = await this.getCompletedAmountForIntent(intent.id);
224
+ if (completedAmount >= intent.amount) {
225
+ await this.rebalanceIntentStore.update(intent.id, {
226
+ status: 'complete',
227
+ });
228
+ this.logger.debug({ id: intent.id }, 'RebalanceIntent completed');
229
+ } else if (now - intent.createdAt > this.config.intentTTL) {
230
+ await this.rebalanceIntentStore.update(intent.id, {
231
+ status: 'failed',
232
+ });
233
+
234
+ // Fail any in-progress actions associated with the expired intent
235
+ for (const action of allInProgressActions) {
236
+ if (action.intentId === intent.id) {
237
+ await this.rebalanceActionStore.update(action.id, {
238
+ status: 'failed',
239
+ });
240
+ this.logger.warn(
241
+ { actionId: action.id, intentId: intent.id },
242
+ 'RebalanceAction failed due to parent intent TTL expiry',
243
+ );
244
+ }
245
+ }
246
+
247
+ this.logger.debug(
248
+ {
249
+ id: intent.id,
250
+ origin: intent.origin,
251
+ destination: intent.destination,
252
+ amount: intent.amount.toString(),
253
+ ageMs: now - intent.createdAt,
254
+ ttlMs: this.config.intentTTL,
255
+ },
256
+ 'RebalanceIntent TTL expiry details',
257
+ );
258
+ this.logger.warn(
259
+ { id: intent.id },
260
+ 'RebalanceIntent expired due to TTL',
261
+ );
262
+ }
263
+ }
264
+
265
+ this.logger.debug('Rebalance intents synced');
266
+ }
267
+
268
+ async syncRebalanceActions(
269
+ confirmedBlockTags?: ConfirmedBlockTags,
270
+ ): Promise<void> {
271
+ this.logger.debug('Syncing rebalance actions');
272
+
273
+ let discoveredActions = 0;
274
+ let completedActions = 0;
275
+
276
+ const inflightMessages =
277
+ await this.explorerClient.getInflightRebalanceActions(
278
+ {
279
+ bridges: this.config.bridges,
280
+ routersByDomain: this.config.routersByDomain,
281
+ rebalancerAddress: this.config.rebalancerAddress,
282
+ inventorySignerAddresses: this.config.inventorySignerAddresses,
283
+ },
284
+ this.logger,
285
+ );
286
+
287
+ this.logger.debug(
288
+ { count: inflightMessages.length },
289
+ 'Found inflight rebalance actions from Explorer',
290
+ );
291
+
292
+ const allActions = await this.rebalanceActionStore.getAll();
293
+
294
+ for (const msg of inflightMessages) {
295
+ const existingAction = allActions.find((a) => a.messageId === msg.msg_id);
296
+
297
+ if (!existingAction) {
298
+ this.logger.info(
299
+ {
300
+ msgId: msg.msg_id,
301
+ origin: msg.origin_domain_id,
302
+ destination: msg.destination_domain_id,
303
+ },
304
+ 'Discovered new rebalance action, recovering...',
305
+ );
306
+ await this.recoverAction(msg);
307
+ discoveredActions++;
308
+ }
309
+ }
310
+
311
+ // Check delivery status for all in-progress actions in our store
312
+ // Only check delivery for actions that have a messageId (rebalance_message, inventory_deposit)
313
+ // inventory_movement actions are synced separately via LiFi status API
314
+ const inProgressActions =
315
+ await this.rebalanceActionStore.getByStatus('in_progress');
316
+ for (const action of inProgressActions) {
317
+ // Skip actions without messageId (e.g., inventory_movement)
318
+ if (!action.messageId) {
319
+ continue;
320
+ }
321
+
322
+ const blockTag = await this.getConfirmedBlockTag(
323
+ action.destination,
324
+ confirmedBlockTags,
325
+ );
326
+ const delivered = await this.isMessageDelivered(
327
+ action.messageId,
328
+ action.destination,
329
+ blockTag,
330
+ );
331
+
332
+ if (delivered) {
333
+ await this.completeRebalanceAction(action.id);
334
+ completedActions++;
335
+ this.logger.debug({ id: action.id }, 'RebalanceAction completed');
336
+ }
337
+ }
338
+
339
+ const inProgressCount = (
340
+ await this.rebalanceActionStore.getByStatus('in_progress')
341
+ ).length;
342
+ this.logger.info(
343
+ {
344
+ discovered: discoveredActions,
345
+ completed: completedActions,
346
+ inProgress: inProgressCount,
347
+ },
348
+ 'Actions synced',
349
+ );
350
+ }
351
+
352
+ // === Transfer Queries ===
353
+
354
+ async getTransfer(id: string): Promise<Transfer | undefined> {
355
+ return this.transferStore.get(id);
356
+ }
357
+
358
+ async getInProgressTransfers(): Promise<Transfer[]> {
359
+ return this.transferStore.getByStatus('in_progress');
360
+ }
361
+
362
+ async getTransfersByDestination(destination: Domain): Promise<Transfer[]> {
363
+ return this.transferStore.getByDestination(destination);
364
+ }
365
+
366
+ // === RebalanceIntent Queries ===
367
+
368
+ async getRebalanceIntent(id: string): Promise<RebalanceIntent | undefined> {
369
+ return this.rebalanceIntentStore.get(id);
370
+ }
371
+
372
+ async getActiveRebalanceIntents(): Promise<RebalanceIntent[]> {
373
+ // Only return in_progress intents - their origin tx is confirmed
374
+ // so simulation only needs to add to destination (origin already deducted on-chain)
375
+ return this.rebalanceIntentStore.getByStatus('in_progress');
376
+ }
377
+
378
+ async getRebalanceIntentsByDestination(
379
+ destination: Domain,
380
+ ): Promise<RebalanceIntent[]> {
381
+ return this.rebalanceIntentStore.getByDestination(destination);
382
+ }
383
+
384
+ // === RebalanceIntent Management ===
385
+
386
+ async createRebalanceIntent(
387
+ params: CreateRebalanceIntentParams,
388
+ ): Promise<RebalanceIntent> {
389
+ const intent: RebalanceIntent = {
390
+ id: uuidv4(),
391
+ status: 'not_started',
392
+ origin: params.origin,
393
+ destination: params.destination,
394
+ amount: params.amount,
395
+ bridge: params.bridge,
396
+ priority: params.priority,
397
+ strategyType: params.strategyType,
398
+ executionMethod: params.executionMethod,
399
+ externalBridge: params.externalBridge,
400
+ createdAt: Date.now(),
401
+ updatedAt: Date.now(),
402
+ };
403
+
404
+ await this.rebalanceIntentStore.save(intent);
405
+ this.logger.debug(
406
+ {
407
+ id: intent.id,
408
+ origin: intent.origin,
409
+ destination: intent.destination,
410
+ executionMethod: intent.executionMethod,
411
+ },
412
+ 'Created RebalanceIntent',
413
+ );
414
+
415
+ return intent;
416
+ }
417
+
418
+ async completeRebalanceIntent(id: string): Promise<void> {
419
+ await this.rebalanceIntentStore.update(id, { status: 'complete' });
420
+ this.logger.info({ id }, 'Intent completed');
421
+ }
422
+
423
+ async cancelRebalanceIntent(id: string): Promise<void> {
424
+ await this.rebalanceIntentStore.update(id, { status: 'cancelled' });
425
+ this.logger.debug({ id }, 'Cancelled RebalanceIntent');
426
+ }
427
+
428
+ async failRebalanceIntent(id: string): Promise<void> {
429
+ await this.rebalanceIntentStore.update(id, { status: 'failed' });
430
+ this.logger.info({ id }, 'Intent failed');
431
+ }
432
+
433
+ // === RebalanceAction Queries ===
434
+
435
+ async getRebalanceAction(id: string): Promise<RebalanceAction | undefined> {
436
+ return this.rebalanceActionStore.get(id);
437
+ }
438
+
439
+ async getInProgressActions(): Promise<RebalanceAction[]> {
440
+ return this.rebalanceActionStore.getByStatus('in_progress');
441
+ }
442
+
443
+ // === RebalanceAction Management ===
444
+
445
+ async createRebalanceAction(
446
+ params: CreateRebalanceActionParams,
447
+ ): Promise<RebalanceAction> {
448
+ const action: RebalanceAction = {
449
+ id: uuidv4(),
450
+ status: 'in_progress',
451
+ type: params.type,
452
+ intentId: params.intentId,
453
+ messageId: params.messageId,
454
+ txHash: params.txHash,
455
+ externalBridgeTransferId: params.externalBridgeTransferId,
456
+ externalBridgeId: params.externalBridgeId,
457
+ origin: params.origin,
458
+ destination: params.destination,
459
+ amount: params.amount,
460
+ createdAt: Date.now(),
461
+ updatedAt: Date.now(),
462
+ };
463
+
464
+ await this.rebalanceActionStore.save(action);
465
+
466
+ // Transition parent intent from not_started to in_progress
467
+ const intent = await this.rebalanceIntentStore.get(params.intentId);
468
+ if (intent && intent.status === 'not_started') {
469
+ await this.rebalanceIntentStore.update(intent.id, {
470
+ status: 'in_progress',
471
+ });
472
+ this.logger.debug(
473
+ { intentId: intent.id },
474
+ 'Transitioned RebalanceIntent to in_progress',
475
+ );
476
+ }
477
+
478
+ this.logger.debug(
479
+ { id: action.id, intentId: action.intentId, type: action.type },
480
+ 'Created RebalanceAction',
481
+ );
482
+
483
+ return action;
484
+ }
485
+
486
+ async completeRebalanceAction(id: string): Promise<void> {
487
+ const action = await this.rebalanceActionStore.get(id);
488
+ if (!action) {
489
+ throw new Error(`RebalanceAction ${id} not found`);
490
+ }
491
+
492
+ await this.rebalanceActionStore.update(id, { status: 'complete' });
493
+
494
+ // Check if parent intent is now complete (derive from action states)
495
+ await this.checkAndCompleteIntent(action.intentId);
496
+
497
+ this.logger.info(
498
+ { id, intentId: action.intentId, type: action.type },
499
+ 'Action completed',
500
+ );
501
+ }
502
+
503
+ /**
504
+ * Check if an intent is fully fulfilled based on completed action amounts.
505
+ * Only `inventory_deposit` and `rebalance_message` actions count toward fulfillment.
506
+ */
507
+ private async checkAndCompleteIntent(intentId: string): Promise<void> {
508
+ const intent = await this.rebalanceIntentStore.get(intentId);
509
+ if (!intent || intent.status === 'complete') return;
510
+
511
+ const completedAmount = await this.getCompletedAmountForIntent(intentId);
512
+
513
+ if (completedAmount >= intent.amount) {
514
+ await this.rebalanceIntentStore.update(intentId, { status: 'complete' });
515
+ this.logger.debug(
516
+ { intentId, completedAmount: completedAmount.toString() },
517
+ 'RebalanceIntent fully fulfilled',
518
+ );
519
+ }
520
+ }
521
+
522
+ /**
523
+ * Get the total completed amount for an intent from its actions.
524
+ * Only `inventory_deposit` and `rebalance_message` actions count.
525
+ */
526
+ private async getCompletedAmountForIntent(intentId: string): Promise<bigint> {
527
+ const actions = await this.getActionsForIntent(intentId);
528
+ return actions
529
+ .filter(
530
+ (a) =>
531
+ a.status === 'complete' &&
532
+ (a.type === 'inventory_deposit' || a.type === 'rebalance_message'),
533
+ )
534
+ .reduce((sum, a) => sum + a.amount, 0n);
535
+ }
536
+
537
+ async failRebalanceAction(id: string): Promise<void> {
538
+ await this.rebalanceActionStore.update(id, { status: 'failed' });
539
+ this.logger.info({ id }, 'Action failed');
540
+ }
541
+
542
+ // === RebalanceAction Queries ===
543
+
544
+ async getActionsByType(type: ActionType): Promise<RebalanceAction[]> {
545
+ const allActions = await this.rebalanceActionStore.getAll();
546
+ return allActions.filter((action) => action.type === type);
547
+ }
548
+
549
+ async getInflightInventoryMovements(origin: Domain): Promise<bigint> {
550
+ const allActions = await this.rebalanceActionStore.getAll();
551
+ const inflightMovements = allActions.filter(
552
+ (action) =>
553
+ action.type === 'inventory_movement' &&
554
+ action.status === 'in_progress' &&
555
+ action.origin === origin,
556
+ );
557
+
558
+ return inflightMovements.reduce(
559
+ (sum, action) => sum + action.amount,
560
+ BigInt(0),
561
+ );
562
+ }
563
+
564
+ /**
565
+ * Get inventory intents that are in_progress or not_started but not fully settled.
566
+ * Intents with in-flight deposits are included but flagged via hasInflightDeposit.
567
+ * Returns enriched data with computed values derived from action states.
568
+ *
569
+ * NOTE: We include 'not_started' intents because they may have been created
570
+ * but failed to execute (e.g., all bridges failed viability check). Without
571
+ * checking for these, we would create duplicate intents every polling cycle.
572
+ */
573
+ async getPartiallyFulfilledInventoryIntents(): Promise<
574
+ PartialInventoryIntent[]
575
+ > {
576
+ // Query both in_progress AND not_started intents
577
+ // not_started intents may exist if execution failed before any action was created
578
+ const [inProgressIntents, notStartedIntents] = await Promise.all([
579
+ this.rebalanceIntentStore.getByStatus('in_progress'),
580
+ this.rebalanceIntentStore.getByStatus('not_started'),
581
+ ]);
582
+
583
+ const allActiveIntents = [...inProgressIntents, ...notStartedIntents];
584
+ const partialIntents: PartialInventoryIntent[] = [];
585
+
586
+ for (const intent of allActiveIntents) {
587
+ // Only inventory execution method
588
+ if (intent.executionMethod !== 'inventory') continue;
589
+
590
+ const actions = await this.getActionsForIntent(intent.id);
591
+
592
+ // Check for in-flight inventory_movement actions
593
+ // Skip intents with active bridge movement(s). Movements still `pending` on
594
+ // the bridge are kept alive regardless of age. Non-pending movements are only
595
+ // failed after they've been in a non-pending state for the staleness window
596
+ // (nonPendingSince), preventing premature failure from transient not_found polls.
597
+ // `undefined` status (pre-deploy data) falls back to createdAt for staleness.
598
+ const inflightMovements = actions.filter(
599
+ (a) => a.status === 'in_progress' && a.type === 'inventory_movement',
600
+ );
601
+ const now = Date.now();
602
+ const staleMovements = inflightMovements.filter((a) => {
603
+ if (a.lastBridgeStatus === 'pending') return false;
604
+ // Use nonPendingSince when available; fall back to createdAt for
605
+ // pre-deploy data that lacks the field.
606
+ const nonPendingStart = a.nonPendingSince ?? a.createdAt;
607
+ return now - nonPendingStart >= DEFAULT_MOVEMENT_STALENESS_MS;
608
+ });
609
+ const staleMovementIds = new Set(staleMovements.map((a) => a.id));
610
+
611
+ const hasBlockingInflightMovement = inflightMovements.some(
612
+ (a) => !staleMovementIds.has(a.id),
613
+ );
614
+
615
+ if (hasBlockingInflightMovement) {
616
+ this.logger.debug(
617
+ { intentId: intent.id },
618
+ 'Skipping partial intent - has in-flight inventory movement',
619
+ );
620
+ continue;
621
+ }
622
+
623
+ // Fail stale movements so the intent can proceed
624
+ for (const movement of staleMovements) {
625
+ await this.failRebalanceAction(movement.id);
626
+ this.logger.warn(
627
+ {
628
+ actionId: movement.id,
629
+ age: now - movement.createdAt,
630
+ nonPendingDuration:
631
+ now - (movement.nonPendingSince ?? movement.createdAt),
632
+ intentId: intent.id,
633
+ lastBridgeStatus: movement.lastBridgeStatus,
634
+ },
635
+ 'Failing stale inventory movement to unblock intent',
636
+ );
637
+ }
638
+
639
+ // Compute amounts from action states
640
+ const completedAmount = actions
641
+ .filter(
642
+ (a) => a.status === 'complete' && a.type === 'inventory_deposit',
643
+ )
644
+ .reduce((sum, a) => sum + a.amount, 0n);
645
+
646
+ const inflightAmount = actions
647
+ .filter(
648
+ (a) => a.status === 'in_progress' && a.type === 'inventory_deposit',
649
+ )
650
+ .reduce((sum, a) => sum + a.amount, 0n);
651
+
652
+ const remaining = intent.amount - completedAmount - inflightAmount;
653
+
654
+ if (remaining > 0n || inflightAmount > 0n) {
655
+ partialIntents.push({
656
+ intent,
657
+ completedAmount,
658
+ remaining,
659
+ hasInflightDeposit: inflightAmount > 0n,
660
+ });
661
+ }
662
+ }
663
+
664
+ return partialIntents;
665
+ }
666
+
667
+ /**
668
+ * Get all actions associated with a specific intent.
669
+ */
670
+ async getActionsForIntent(intentId: string): Promise<RebalanceAction[]> {
671
+ const allActions = await this.rebalanceActionStore.getAll();
672
+ return allActions.filter((a) => a.intentId === intentId);
673
+ }
674
+
675
+ async syncInventoryMovementActions(
676
+ externalBridgeRegistry: Partial<ExternalBridgeRegistry>,
677
+ ): Promise<{ completed: number; failed: number }> {
678
+ this.logger.debug('Syncing inventory movement actions');
679
+
680
+ let completed = 0;
681
+ let failed = 0;
682
+
683
+ // Get all in-progress inventory_movement actions
684
+ const inProgressActions =
685
+ await this.rebalanceActionStore.getByStatus('in_progress');
686
+ const inventoryMovements = inProgressActions.filter(
687
+ (a) => a.type === 'inventory_movement',
688
+ );
689
+
690
+ this.logger.debug(
691
+ { count: inventoryMovements.length },
692
+ 'Found in-progress inventory movements',
693
+ );
694
+
695
+ for (const action of inventoryMovements) {
696
+ // Skip if no txHash (shouldn't happen but be safe)
697
+ if (!action.txHash) {
698
+ this.logger.warn(
699
+ { actionId: action.id },
700
+ 'Inventory movement action has no txHash',
701
+ );
702
+ continue;
703
+ }
704
+
705
+ // Skip if no externalBridgeId (shouldn't happen but be safe)
706
+ if (!action.externalBridgeId) {
707
+ this.logger.warn(
708
+ { actionId: action.id },
709
+ 'Inventory movement action has no externalBridgeId',
710
+ );
711
+ continue;
712
+ }
713
+
714
+ const externalBridge = externalBridgeRegistry[action.externalBridgeId];
715
+ if (!externalBridge) {
716
+ this.logger.warn(
717
+ { actionId: action.id, bridgeId: action.externalBridgeId },
718
+ 'Bridge not found in registry',
719
+ );
720
+ continue;
721
+ }
722
+
723
+ try {
724
+ const status = await externalBridge.getStatus(
725
+ action.txHash,
726
+ action.origin,
727
+ action.destination,
728
+ );
729
+
730
+ if (status.status === 'complete') {
731
+ await this.completeRebalanceAction(action.id);
732
+ completed++;
733
+ this.logger.info(
734
+ {
735
+ actionId: action.id,
736
+ txHash: action.txHash,
737
+ receivedAmount: status.receivedAmount?.toString(),
738
+ },
739
+ 'Inventory movement completed',
740
+ );
741
+ } else if (status.status === 'failed') {
742
+ await this.failRebalanceAction(action.id);
743
+ failed++;
744
+ this.logger.warn(
745
+ {
746
+ actionId: action.id,
747
+ txHash: action.txHash,
748
+ error: status.error,
749
+ },
750
+ 'Inventory movement failed',
751
+ );
752
+ } else if (status.status === 'pending') {
753
+ await this.rebalanceActionStore.update(action.id, {
754
+ lastBridgeStatus: 'pending',
755
+ nonPendingSince: undefined,
756
+ });
757
+ this.logger.debug(
758
+ {
759
+ actionId: action.id,
760
+ txHash: action.txHash,
761
+ substatus: status.substatus,
762
+ },
763
+ 'Inventory movement still pending',
764
+ );
765
+ } else if (status.status === 'not_found') {
766
+ await this.rebalanceActionStore.update(action.id, {
767
+ lastBridgeStatus: 'not_found',
768
+ nonPendingSince: action.nonPendingSince ?? Date.now(),
769
+ });
770
+ this.logger.debug(
771
+ {
772
+ actionId: action.id,
773
+ txHash: action.txHash,
774
+ },
775
+ 'Inventory movement not found',
776
+ );
777
+ }
778
+ } catch (error) {
779
+ this.logger.debug(
780
+ {
781
+ actionId: action.id,
782
+ txHash: action.txHash,
783
+ error: (error as Error).message,
784
+ },
785
+ 'Failed to get inventory movement status',
786
+ );
787
+ }
788
+ }
789
+
790
+ if (inventoryMovements.length > 0) {
791
+ this.logger.info(
792
+ {
793
+ completed,
794
+ failed,
795
+ pending: inventoryMovements.length - completed - failed,
796
+ },
797
+ 'Inventory movements synced',
798
+ );
799
+ }
800
+
801
+ return { completed, failed };
802
+ }
803
+
804
+ // === Debug Helpers ===
805
+
806
+ /**
807
+ * Log the contents of all stores.
808
+ * Logs each item separately for full visibility (avoids [Object] truncation).
809
+ */
810
+ async logStoreContents(): Promise<void> {
811
+ const transfers = await this.transferStore.getAll();
812
+ const intents = await this.rebalanceIntentStore.getAll();
813
+ const actions = await this.rebalanceActionStore.getAll();
814
+
815
+ const activeIntents = intents.filter((i) =>
816
+ ['not_started', 'in_progress'].includes(i.status),
817
+ );
818
+ const inProgressTransfers = transfers.filter(
819
+ (t) => t.status === 'in_progress',
820
+ );
821
+ const inProgressActions = actions.filter((a) => a.status === 'in_progress');
822
+
823
+ // Log summary
824
+ this.logger.info(
825
+ {
826
+ transfers: inProgressTransfers.length,
827
+ intents: activeIntents.length,
828
+ actions: inProgressActions.length,
829
+ },
830
+ 'Store summary',
831
+ );
832
+
833
+ // Log each transfer separately
834
+ for (const t of inProgressTransfers) {
835
+ this.logger.info(
836
+ {
837
+ type: 'transfer',
838
+ origin: t.origin,
839
+ destination: t.destination,
840
+ amount: t.amount.toString(),
841
+ messageId: t.messageId,
842
+ },
843
+ 'In-progress transfer',
844
+ );
845
+ }
846
+
847
+ // Log each intent separately
848
+ for (const i of activeIntents) {
849
+ this.logger.info(
850
+ {
851
+ type: 'intent',
852
+ id: i.id,
853
+ origin: i.origin,
854
+ destination: i.destination,
855
+ amount: i.amount.toString(),
856
+ status: i.status,
857
+ bridge: i.bridge,
858
+ },
859
+ 'Active intent',
860
+ );
861
+ }
862
+
863
+ // Log each action separately
864
+ for (const a of inProgressActions) {
865
+ this.logger.info(
866
+ {
867
+ type: 'action',
868
+ id: a.id,
869
+ origin: a.origin,
870
+ destination: a.destination,
871
+ amount: a.amount.toString(),
872
+ messageId: a.messageId,
873
+ intentId: a.intentId,
874
+ },
875
+ 'In-progress action',
876
+ );
877
+ }
878
+ }
879
+
880
+ // === Private Helpers ===
881
+
882
+ /**
883
+ * Get the confirmed block tag for delivery checks.
884
+ * Uses cached value from Monitor event if available, otherwise computes on-demand.
885
+ */
886
+ private async getConfirmedBlockTag(
887
+ destination: Domain,
888
+ confirmedBlockTags?: ConfirmedBlockTags,
889
+ ): Promise<string | number | undefined> {
890
+ const chainName = this.core.multiProvider.getChainName(destination);
891
+
892
+ // If tags provided (from Monitor event), use cached value
893
+ if (confirmedBlockTags) {
894
+ return confirmedBlockTags[chainName];
895
+ }
896
+
897
+ // Otherwise compute on-demand (e.g., during initialize())
898
+ return getConfirmedBlockTag(
899
+ this.core.multiProvider,
900
+ chainName,
901
+ this.logger,
902
+ );
903
+ }
904
+
905
+ private async isMessageDelivered(
906
+ messageId: string,
907
+ destination: Domain,
908
+ blockTag?: string | number,
909
+ ): Promise<boolean> {
910
+ try {
911
+ const chainName = this.core.multiProvider.getChainName(destination);
912
+ const delivered = await this.core
913
+ .adapter(chainName)
914
+ .isDelivered(messageId, blockTag);
915
+
916
+ this.logger.debug(
917
+ { messageId, destination: chainName, delivered, blockTag },
918
+ 'Checked message delivery',
919
+ );
920
+
921
+ return delivered;
922
+ } catch (error) {
923
+ this.logger.warn(
924
+ { messageId, destination, error },
925
+ 'Failed to check message delivery status',
926
+ );
927
+ return false;
928
+ }
929
+ }
930
+
931
+ private async recoverAction(msg: ExplorerMessage): Promise<void> {
932
+ // Check if action already exists
933
+ const existing = await this.rebalanceActionStore.get(msg.msg_id);
934
+ if (existing) {
935
+ this.logger.debug({ id: msg.msg_id }, 'Action already exists, skipping');
936
+ return;
937
+ }
938
+
939
+ this.logger.debug(
940
+ {
941
+ msgId: msg.msg_id,
942
+ origin: msg.origin_domain_id,
943
+ destination: msg.destination_domain_id,
944
+ sender: msg.sender,
945
+ recipient: msg.recipient,
946
+ txHash: msg.origin_tx_hash,
947
+ messageBodyLength: msg.message_body?.length,
948
+ messageBodyPreview: msg.message_body?.substring(0, 66),
949
+ },
950
+ 'Recovering rebalance action',
951
+ );
952
+
953
+ try {
954
+ // Create synthetic intent
955
+ const { amount } = parseWarpRouteMessage(msg.message_body);
956
+ // Hasura returns block timestamps as UTC without 'Z' suffix (e.g. "2024-01-15T12:30:45").
957
+ // Null when the scraper hasn't indexed the origin block yet, so fall back to now.
958
+ // Note: when null, TTL effectively extends by scraper lag since recoverAction won't update createdAt later.
959
+ const createdAt = msg.send_occurred_at
960
+ ? new Date(msg.send_occurred_at + 'Z').getTime()
961
+ : Date.now();
962
+ assert(
963
+ !isNaN(createdAt),
964
+ `Invalid send_occurred_at timestamp: ${msg.send_occurred_at}`,
965
+ );
966
+ const intent: RebalanceIntent = {
967
+ id: uuidv4(),
968
+ status: 'in_progress',
969
+ origin: msg.origin_domain_id,
970
+ destination: msg.destination_domain_id,
971
+ amount,
972
+ priority: undefined,
973
+ strategyType: undefined,
974
+ createdAt,
975
+ updatedAt: Date.now(),
976
+ };
977
+
978
+ await this.rebalanceIntentStore.save(intent);
979
+ this.logger.debug(
980
+ { id: intent.id, amount: amount.toString(), createdAt },
981
+ 'Created synthetic RebalanceIntent',
982
+ );
983
+
984
+ // Create action (recovered actions are always rebalance_message type)
985
+ const action: RebalanceAction = {
986
+ id: msg.msg_id,
987
+ status: 'in_progress',
988
+ type: 'rebalance_message',
989
+ intentId: intent.id,
990
+ messageId: msg.msg_id,
991
+ txHash: msg.origin_tx_hash,
992
+ origin: msg.origin_domain_id,
993
+ destination: msg.destination_domain_id,
994
+ amount,
995
+ createdAt,
996
+ updatedAt: Date.now(),
997
+ };
998
+
999
+ await this.rebalanceActionStore.save(action);
1000
+ this.logger.debug(
1001
+ { id: action.id, intentId: action.intentId, amount: amount.toString() },
1002
+ 'Recovered RebalanceAction',
1003
+ );
1004
+ } catch (error) {
1005
+ this.logger.warn(
1006
+ {
1007
+ msgId: msg.msg_id,
1008
+ messageBody: msg.message_body,
1009
+ messageBodyLength: msg.message_body?.length,
1010
+ origin: msg.origin_domain_id,
1011
+ destination: msg.destination_domain_id,
1012
+ txHash: msg.origin_tx_hash,
1013
+ error: error instanceof Error ? error.message : String(error),
1014
+ },
1015
+ 'Failed to parse message body during recovery, skipping action',
1016
+ );
1017
+ }
1018
+ }
1019
+ }