@humanode-hyperlane/rebalancer 27.2.2 → 1029.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (295) hide show
  1. package/dist/bridges/LiFiBridge.d.ts +3 -3
  2. package/dist/bridges/LiFiBridge.d.ts.map +1 -1
  3. package/dist/bridges/LiFiBridge.js +66 -55
  4. package/dist/bridges/LiFiBridge.js.map +1 -1
  5. package/dist/bridges/LiFiBridge.test.js +213 -0
  6. package/dist/bridges/LiFiBridge.test.js.map +1 -1
  7. package/dist/config/RebalancerConfig.d.ts +4 -3
  8. package/dist/config/RebalancerConfig.d.ts.map +1 -1
  9. package/dist/config/RebalancerConfig.js +7 -5
  10. package/dist/config/RebalancerConfig.js.map +1 -1
  11. package/dist/config/RebalancerConfig.test.js +278 -2
  12. package/dist/config/RebalancerConfig.test.js.map +1 -1
  13. package/dist/config/types.d.ts +707 -2664
  14. package/dist/config/types.d.ts.map +1 -1
  15. package/dist/config/types.js +62 -7
  16. package/dist/config/types.js.map +1 -1
  17. package/dist/core/InventoryRebalancer.d.ts +13 -19
  18. package/dist/core/InventoryRebalancer.d.ts.map +1 -1
  19. package/dist/core/InventoryRebalancer.js +404 -276
  20. package/dist/core/InventoryRebalancer.js.map +1 -1
  21. package/dist/core/InventoryRebalancer.test.js +814 -25
  22. package/dist/core/InventoryRebalancer.test.js.map +1 -1
  23. package/dist/core/Rebalancer.d.ts.map +1 -1
  24. package/dist/core/Rebalancer.js +12 -6
  25. package/dist/core/Rebalancer.js.map +1 -1
  26. package/dist/core/Rebalancer.test.js +51 -0
  27. package/dist/core/Rebalancer.test.js.map +1 -1
  28. package/dist/core/RebalancerOrchestrator.d.ts.map +1 -1
  29. package/dist/core/RebalancerOrchestrator.test.js +0 -1
  30. package/dist/core/RebalancerOrchestrator.test.js.map +1 -1
  31. package/dist/core/RebalancerService.d.ts.map +1 -1
  32. package/dist/core/RebalancerService.js +3 -2
  33. package/dist/core/RebalancerService.js.map +1 -1
  34. package/dist/core/RebalancerService.test.js +24 -0
  35. package/dist/core/RebalancerService.test.js.map +1 -1
  36. package/dist/e2e/fixtures/routes.d.ts +3 -3
  37. package/dist/e2e/fixtures/routes.d.ts.map +1 -1
  38. package/dist/e2e/harness/BaseLocalDeploymentManager.d.ts.map +1 -1
  39. package/dist/e2e/harness/BaseLocalDeploymentManager.js +3 -2
  40. package/dist/e2e/harness/BaseLocalDeploymentManager.js.map +1 -1
  41. package/dist/e2e/harness/Erc20InventoryLocalDeploymentManager.d.ts.map +1 -1
  42. package/dist/e2e/harness/Erc20InventoryLocalDeploymentManager.js.map +1 -1
  43. package/dist/e2e/harness/Erc20LocalDeploymentManager.d.ts.map +1 -1
  44. package/dist/e2e/harness/ForkIndexer.d.ts.map +1 -1
  45. package/dist/e2e/harness/ForkIndexer.js.map +1 -1
  46. package/dist/e2e/harness/LocalProvider.d.ts +3 -0
  47. package/dist/e2e/harness/LocalProvider.d.ts.map +1 -0
  48. package/dist/e2e/harness/LocalProvider.js +9 -0
  49. package/dist/e2e/harness/LocalProvider.js.map +1 -0
  50. package/dist/e2e/harness/LocalProvider.test.d.ts +2 -0
  51. package/dist/e2e/harness/LocalProvider.test.d.ts.map +1 -0
  52. package/dist/e2e/harness/LocalProvider.test.js +14 -0
  53. package/dist/e2e/harness/LocalProvider.test.js.map +1 -0
  54. package/dist/e2e/harness/MixedLocalDeploymentManager.d.ts.map +1 -1
  55. package/dist/e2e/harness/MixedLocalDeploymentManager.js.map +1 -1
  56. package/dist/e2e/harness/MockExplorerClient.d.ts.map +1 -1
  57. package/dist/e2e/harness/MockExplorerClient.js.map +1 -1
  58. package/dist/e2e/harness/MockExternalBridge.d.ts.map +1 -1
  59. package/dist/e2e/harness/MockExternalBridge.js.map +1 -1
  60. package/dist/e2e/harness/NativeLocalDeploymentManager.d.ts.map +1 -1
  61. package/dist/e2e/harness/NativeLocalDeploymentManager.js +2 -1
  62. package/dist/e2e/harness/NativeLocalDeploymentManager.js.map +1 -1
  63. package/dist/e2e/harness/SnapshotHelper.d.ts.map +1 -1
  64. package/dist/e2e/harness/SnapshotHelper.js +3 -5
  65. package/dist/e2e/harness/SnapshotHelper.js.map +1 -1
  66. package/dist/e2e/harness/TestHelpers.js +1 -2
  67. package/dist/e2e/harness/TestHelpers.js.map +1 -1
  68. package/dist/e2e/harness/TestRebalancer.d.ts +5 -1
  69. package/dist/e2e/harness/TestRebalancer.d.ts.map +1 -1
  70. package/dist/e2e/harness/TestRebalancer.js +14 -4
  71. package/dist/e2e/harness/TestRebalancer.js.map +1 -1
  72. package/dist/e2e/relayer-denylist.e2e-test.d.ts +2 -0
  73. package/dist/e2e/relayer-denylist.e2e-test.d.ts.map +1 -0
  74. package/dist/e2e/relayer-denylist.e2e-test.js +388 -0
  75. package/dist/e2e/relayer-denylist.e2e-test.js.map +1 -0
  76. package/dist/factories/RebalancerContextFactory.d.ts.map +1 -1
  77. package/dist/factories/RebalancerContextFactory.js +61 -31
  78. package/dist/factories/RebalancerContextFactory.js.map +1 -1
  79. package/dist/factories/RebalancerContextFactory.test.js +336 -5
  80. package/dist/factories/RebalancerContextFactory.test.js.map +1 -1
  81. package/dist/index.d.ts +2 -2
  82. package/dist/index.d.ts.map +1 -1
  83. package/dist/index.js +1 -1
  84. package/dist/index.js.map +1 -1
  85. package/dist/interfaces/IRebalancer.d.ts +4 -2
  86. package/dist/interfaces/IRebalancer.d.ts.map +1 -1
  87. package/dist/metrics/Metrics.d.ts.map +1 -1
  88. package/dist/metrics/Metrics.js +1 -1
  89. package/dist/metrics/Metrics.js.map +1 -1
  90. package/dist/metrics/PriceGetter.d.ts +1 -0
  91. package/dist/metrics/PriceGetter.d.ts.map +1 -1
  92. package/dist/metrics/PriceGetter.js +14 -4
  93. package/dist/metrics/PriceGetter.js.map +1 -1
  94. package/dist/metrics/PriceGetter.test.d.ts +2 -0
  95. package/dist/metrics/PriceGetter.test.d.ts.map +1 -0
  96. package/dist/metrics/PriceGetter.test.js +84 -0
  97. package/dist/metrics/PriceGetter.test.js.map +1 -0
  98. package/dist/metrics/scripts/metrics.d.ts +4 -4
  99. package/dist/monitor/Monitor.d.ts +1 -0
  100. package/dist/monitor/Monitor.d.ts.map +1 -1
  101. package/dist/monitor/Monitor.js +55 -46
  102. package/dist/monitor/Monitor.js.map +1 -1
  103. package/dist/monitor/Monitor.test.d.ts +2 -0
  104. package/dist/monitor/Monitor.test.d.ts.map +1 -0
  105. package/dist/monitor/Monitor.test.js +346 -0
  106. package/dist/monitor/Monitor.test.js.map +1 -0
  107. package/dist/service.d.ts +1 -1
  108. package/dist/service.d.ts.map +1 -1
  109. package/dist/service.js +9 -5
  110. package/dist/service.js.map +1 -1
  111. package/dist/strategy/BaseStrategy.d.ts.map +1 -1
  112. package/dist/strategy/BaseStrategy.js +13 -11
  113. package/dist/strategy/BaseStrategy.js.map +1 -1
  114. package/dist/strategy/CollateralDeficitStrategy.d.ts.map +1 -1
  115. package/dist/strategy/CollateralDeficitStrategy.js +2 -2
  116. package/dist/strategy/CollateralDeficitStrategy.js.map +1 -1
  117. package/dist/strategy/CompositeStrategy.d.ts.map +1 -1
  118. package/dist/strategy/CompositeStrategy.js.map +1 -1
  119. package/dist/strategy/CompositeStrategy.test.js.map +1 -1
  120. package/dist/strategy/MinAmountStrategy.d.ts +1 -0
  121. package/dist/strategy/MinAmountStrategy.d.ts.map +1 -1
  122. package/dist/strategy/MinAmountStrategy.js +12 -8
  123. package/dist/strategy/MinAmountStrategy.js.map +1 -1
  124. package/dist/strategy/MinAmountStrategy.test.js +189 -2
  125. package/dist/strategy/MinAmountStrategy.test.js.map +1 -1
  126. package/dist/strategy/StrategyFactory.d.ts.map +1 -1
  127. package/dist/strategy/StrategyFactory.js.map +1 -1
  128. package/dist/strategy/WeightedStrategy.d.ts.map +1 -1
  129. package/dist/test/helpers.d.ts +11 -3
  130. package/dist/test/helpers.d.ts.map +1 -1
  131. package/dist/test/helpers.js +9 -11
  132. package/dist/test/helpers.js.map +1 -1
  133. package/dist/test/lifiMocks.d.ts +6 -6
  134. package/dist/test/lifiMocks.d.ts.map +1 -1
  135. package/dist/test/lifiMocks.js +5 -2
  136. package/dist/test/lifiMocks.js.map +1 -1
  137. package/dist/test/pendingTransferMiddlewareMocks.d.ts +17 -0
  138. package/dist/test/pendingTransferMiddlewareMocks.d.ts.map +1 -0
  139. package/dist/test/pendingTransferMiddlewareMocks.js +43 -0
  140. package/dist/test/pendingTransferMiddlewareMocks.js.map +1 -0
  141. package/dist/tracking/ActionTracker.d.ts +4 -0
  142. package/dist/tracking/ActionTracker.d.ts.map +1 -1
  143. package/dist/tracking/ActionTracker.js +76 -9
  144. package/dist/tracking/ActionTracker.js.map +1 -1
  145. package/dist/tracking/ActionTracker.test.js +418 -1
  146. package/dist/tracking/ActionTracker.test.js.map +1 -1
  147. package/dist/tracking/IActionTracker.d.ts +4 -2
  148. package/dist/tracking/IActionTracker.d.ts.map +1 -1
  149. package/dist/tracking/InflightContextAdapter.d.ts.map +1 -1
  150. package/dist/tracking/InflightContextAdapter.js.map +1 -1
  151. package/dist/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.d.ts +64 -0
  152. package/dist/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.d.ts.map +1 -0
  153. package/dist/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.js +474 -0
  154. package/dist/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.js.map +1 -0
  155. package/dist/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.test.d.ts +2 -0
  156. package/dist/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.test.d.ts.map +1 -0
  157. package/dist/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.test.js +1168 -0
  158. package/dist/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.test.js.map +1 -0
  159. package/dist/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.d.ts +61 -0
  160. package/dist/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.d.ts.map +1 -0
  161. package/dist/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.js +160 -0
  162. package/dist/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.js.map +1 -0
  163. package/dist/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.test.d.ts +2 -0
  164. package/dist/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.test.d.ts.map +1 -0
  165. package/dist/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.test.js +369 -0
  166. package/dist/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.test.js.map +1 -0
  167. package/dist/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.d.ts +60 -0
  168. package/dist/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.d.ts.map +1 -0
  169. package/dist/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.js +288 -0
  170. package/dist/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.js.map +1 -0
  171. package/dist/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.test.d.ts +2 -0
  172. package/dist/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.test.d.ts.map +1 -0
  173. package/dist/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.test.js +606 -0
  174. package/dist/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.test.js.map +1 -0
  175. package/dist/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.d.ts +25 -0
  176. package/dist/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.d.ts.map +1 -0
  177. package/dist/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.js +41 -0
  178. package/dist/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.js.map +1 -0
  179. package/dist/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.test.d.ts +2 -0
  180. package/dist/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.test.d.ts.map +1 -0
  181. package/dist/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.test.js +141 -0
  182. package/dist/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.test.js.map +1 -0
  183. package/dist/tracking/pendingTransferMiddlewares/matchBody.d.ts +12 -0
  184. package/dist/tracking/pendingTransferMiddlewares/matchBody.d.ts.map +1 -0
  185. package/dist/tracking/pendingTransferMiddlewares/matchBody.js +29 -0
  186. package/dist/tracking/pendingTransferMiddlewares/matchBody.js.map +1 -0
  187. package/dist/tracking/pendingTransferMiddlewares/relayerDenylistRules.d.ts +59 -0
  188. package/dist/tracking/pendingTransferMiddlewares/relayerDenylistRules.d.ts.map +1 -0
  189. package/dist/tracking/pendingTransferMiddlewares/relayerDenylistRules.js +661 -0
  190. package/dist/tracking/pendingTransferMiddlewares/relayerDenylistRules.js.map +1 -0
  191. package/dist/tracking/pendingTransferMiddlewares/relayerDenylistRules.test.d.ts +2 -0
  192. package/dist/tracking/pendingTransferMiddlewares/relayerDenylistRules.test.d.ts.map +1 -0
  193. package/dist/tracking/pendingTransferMiddlewares/relayerDenylistRules.test.js +1093 -0
  194. package/dist/tracking/pendingTransferMiddlewares/relayerDenylistRules.test.js.map +1 -0
  195. package/dist/tracking/pendingTransferMiddlewares/types.d.ts +2 -0
  196. package/dist/tracking/pendingTransferMiddlewares/types.d.ts.map +1 -0
  197. package/dist/tracking/pendingTransferMiddlewares/types.js +4 -0
  198. package/dist/tracking/pendingTransferMiddlewares/types.js.map +1 -0
  199. package/dist/tracking/store/InMemoryStore.d.ts.map +1 -1
  200. package/dist/utils/ExplorerClient.d.ts +1 -0
  201. package/dist/utils/ExplorerClient.d.ts.map +1 -1
  202. package/dist/utils/ExplorerClient.js +8 -3
  203. package/dist/utils/ExplorerClient.js.map +1 -1
  204. package/dist/utils/ExplorerClient.test.js +25 -0
  205. package/dist/utils/ExplorerClient.test.js.map +1 -1
  206. package/dist/utils/balanceUtils.d.ts +7 -1
  207. package/dist/utils/balanceUtils.d.ts.map +1 -1
  208. package/dist/utils/balanceUtils.js +39 -1
  209. package/dist/utils/balanceUtils.js.map +1 -1
  210. package/dist/utils/balanceUtils.test.js +55 -1
  211. package/dist/utils/balanceUtils.test.js.map +1 -1
  212. package/dist/utils/blockTag.d.ts +5 -4
  213. package/dist/utils/blockTag.d.ts.map +1 -1
  214. package/dist/utils/blockTag.js +26 -10
  215. package/dist/utils/blockTag.js.map +1 -1
  216. package/dist/utils/blockTag.test.d.ts +2 -0
  217. package/dist/utils/blockTag.test.d.ts.map +1 -0
  218. package/dist/utils/blockTag.test.js +132 -0
  219. package/dist/utils/blockTag.test.js.map +1 -0
  220. package/dist/utils/bridgeUtils.js.map +1 -1
  221. package/dist/utils/errors.d.ts.map +1 -1
  222. package/dist/utils/gasEstimation.d.ts +2 -1
  223. package/dist/utils/gasEstimation.d.ts.map +1 -1
  224. package/dist/utils/gasEstimation.js +16 -13
  225. package/dist/utils/gasEstimation.js.map +1 -1
  226. package/dist/utils/gasEstimation.test.d.ts +2 -0
  227. package/dist/utils/gasEstimation.test.d.ts.map +1 -0
  228. package/dist/utils/gasEstimation.test.js +104 -0
  229. package/dist/utils/gasEstimation.test.js.map +1 -0
  230. package/dist/utils/tokenUtils.d.ts.map +1 -1
  231. package/dist/utils/tokenUtils.js +5 -2
  232. package/dist/utils/tokenUtils.js.map +1 -1
  233. package/package.json +23 -22
  234. package/src/bridges/LiFiBridge.test.ts +270 -0
  235. package/src/bridges/LiFiBridge.ts +90 -71
  236. package/src/config/RebalancerConfig.test.ts +315 -1
  237. package/src/config/RebalancerConfig.ts +6 -3
  238. package/src/config/types.ts +98 -8
  239. package/src/core/InventoryRebalancer.test.ts +1092 -38
  240. package/src/core/InventoryRebalancer.ts +584 -351
  241. package/src/core/Rebalancer.test.ts +84 -0
  242. package/src/core/Rebalancer.ts +22 -6
  243. package/src/core/RebalancerOrchestrator.test.ts +0 -1
  244. package/src/core/RebalancerService.test.ts +35 -0
  245. package/src/core/RebalancerService.ts +3 -2
  246. package/src/e2e/harness/BaseLocalDeploymentManager.ts +3 -1
  247. package/src/e2e/harness/LocalProvider.test.ts +16 -0
  248. package/src/e2e/harness/LocalProvider.ts +10 -0
  249. package/src/e2e/harness/NativeLocalDeploymentManager.ts +2 -1
  250. package/src/e2e/harness/SnapshotHelper.ts +2 -5
  251. package/src/e2e/harness/TestHelpers.ts +3 -3
  252. package/src/e2e/harness/TestRebalancer.ts +21 -1
  253. package/src/e2e/relayer-denylist.e2e-test.ts +558 -0
  254. package/src/factories/RebalancerContextFactory.test.ts +489 -8
  255. package/src/factories/RebalancerContextFactory.ts +86 -36
  256. package/src/index.ts +2 -0
  257. package/src/interfaces/IRebalancer.ts +4 -1
  258. package/src/metrics/Metrics.ts +1 -0
  259. package/src/metrics/PriceGetter.test.ts +113 -0
  260. package/src/metrics/PriceGetter.ts +18 -3
  261. package/src/monitor/Monitor.test.ts +408 -0
  262. package/src/monitor/Monitor.ts +123 -97
  263. package/src/service.ts +14 -8
  264. package/src/strategy/BaseStrategy.ts +18 -15
  265. package/src/strategy/CollateralDeficitStrategy.ts +4 -3
  266. package/src/strategy/MinAmountStrategy.test.ts +238 -2
  267. package/src/strategy/MinAmountStrategy.ts +29 -17
  268. package/src/test/helpers.ts +13 -12
  269. package/src/test/lifiMocks.ts +5 -2
  270. package/src/test/pendingTransferMiddlewareMocks.ts +65 -0
  271. package/src/tracking/ActionTracker.test.ts +635 -3
  272. package/src/tracking/ActionTracker.ts +92 -12
  273. package/src/tracking/IActionTracker.ts +4 -2
  274. package/src/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.test.ts +1693 -0
  275. package/src/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.ts +784 -0
  276. package/src/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.test.ts +554 -0
  277. package/src/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.ts +233 -0
  278. package/src/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.test.ts +894 -0
  279. package/src/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.ts +404 -0
  280. package/src/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.test.ts +205 -0
  281. package/src/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.ts +89 -0
  282. package/src/tracking/pendingTransferMiddlewares/fixtures/mainnet-denylist.json +3556 -0
  283. package/src/tracking/pendingTransferMiddlewares/matchBody.ts +43 -0
  284. package/src/tracking/pendingTransferMiddlewares/relayerDenylistRules.test.ts +1220 -0
  285. package/src/tracking/pendingTransferMiddlewares/relayerDenylistRules.ts +934 -0
  286. package/src/tracking/pendingTransferMiddlewares/types.ts +3 -0
  287. package/src/utils/ExplorerClient.test.ts +42 -0
  288. package/src/utils/ExplorerClient.ts +14 -3
  289. package/src/utils/balanceUtils.test.ts +87 -1
  290. package/src/utils/balanceUtils.ts +73 -2
  291. package/src/utils/blockTag.test.ts +173 -0
  292. package/src/utils/blockTag.ts +35 -15
  293. package/src/utils/gasEstimation.test.ts +192 -0
  294. package/src/utils/gasEstimation.ts +17 -11
  295. package/src/utils/tokenUtils.ts +5 -2
@@ -0,0 +1,408 @@
1
+ import { expect } from 'chai';
2
+ import { providers } from 'ethers';
3
+ import { pino } from 'pino';
4
+ import sinon from 'sinon';
5
+
6
+ import {
7
+ type ChainMap,
8
+ type ChainMetadata,
9
+ EvmHypSyntheticAdapter,
10
+ MultiProtocolProvider,
11
+ Token,
12
+ TokenStandard,
13
+ WarpCore,
14
+ } from '@humanode-hyperlane/sdk';
15
+ import { ProtocolType } from '@humanode-hyperlane/utils';
16
+
17
+ import { type MonitorEvent, MonitorEventType } from '../interfaces/IMonitor.js';
18
+
19
+ import { Monitor } from './Monitor.js';
20
+
21
+ const ADDRESS = '0x0000000000000000000000000000000000000001';
22
+ const logger = pino({ level: 'silent' });
23
+
24
+ async function nextTurn(): Promise<void> {
25
+ await new Promise<void>((resolve) => setImmediate(resolve));
26
+ }
27
+
28
+ function setup(count: number, withInventory = false) {
29
+ const metadata: ChainMap<ChainMetadata> = {};
30
+ const tokens = Array.from({ length: count }, (_, index) => {
31
+ const chainName = `chain${index}`;
32
+ metadata[chainName] = {
33
+ name: chainName,
34
+ protocol: ProtocolType.Ethereum,
35
+ chainId: index + 1,
36
+ domainId: index + 1,
37
+ rpcUrls: [{ http: 'http://unused' }],
38
+ blocks: { reorgPeriod: 2, confirmations: 1 },
39
+ };
40
+ return new Token({
41
+ chainName,
42
+ standard: TokenStandard.EvmHypSynthetic,
43
+ addressOrDenom: ADDRESS,
44
+ decimals: 18,
45
+ name: 'Token',
46
+ symbol: 'TOK',
47
+ });
48
+ });
49
+ const multiProvider = new MultiProtocolProvider(metadata);
50
+ const blocks = tokens.map(() =>
51
+ sinon.createStubInstance(providers.JsonRpcProvider),
52
+ );
53
+ const adapters = tokens.map(() =>
54
+ sinon.createStubInstance(EvmHypSyntheticAdapter),
55
+ );
56
+ const getProvider = sinon.stub(multiProvider, 'getEthersV5Provider');
57
+ tokens.forEach((token, index) => {
58
+ getProvider.withArgs(token.chainName).returns(blocks[index]);
59
+ blocks[index].send.resolves('0x64');
60
+ adapters[index].getBridgedSupply.resolves(BigInt(index));
61
+ adapters[index].getBalance.resolves(BigInt(index + 10));
62
+ sinon.stub(token, 'getHypAdapter').returns(adapters[index]);
63
+ sinon.stub(token, 'getAdapter').returns(adapters[index]);
64
+ });
65
+ const monitor = new Monitor(
66
+ 0,
67
+ new WarpCore(multiProvider, tokens),
68
+ logger,
69
+ withInventory
70
+ ? {
71
+ chains: tokens.map((token) => token.chainName),
72
+ inventoryAddresses: { [ProtocolType.Ethereum]: ADDRESS },
73
+ }
74
+ : undefined,
75
+ );
76
+ return { monitor, tokens, blocks, adapters };
77
+ }
78
+
79
+ function setupTron(reorgPeriod: number | string) {
80
+ const token = new Token({
81
+ chainName: 'tron',
82
+ standard: TokenStandard.TronHypSynthetic,
83
+ addressOrDenom: 'TJRabPrwbZy45sbavfcjinPJC18kjpRTv8',
84
+ decimals: 6,
85
+ name: 'Token',
86
+ symbol: 'TOK',
87
+ });
88
+ const metadata: ChainMap<ChainMetadata> = {
89
+ tron: {
90
+ name: 'tron',
91
+ protocol: ProtocolType.Tron,
92
+ chainId: 728126428,
93
+ domainId: 728126428,
94
+ rpcUrls: [{ http: 'http://unused' }],
95
+ blocks: { reorgPeriod, confirmations: 1 },
96
+ },
97
+ };
98
+ const multiProvider = new MultiProtocolProvider(metadata);
99
+ const block = sinon.createStubInstance(providers.JsonRpcProvider);
100
+ block.send.resolves('0x64');
101
+ sinon.stub(multiProvider, 'getEthersV5Provider').returns(block);
102
+ const adapter = sinon.createStubInstance(EvmHypSyntheticAdapter);
103
+ adapter.getBridgedSupply.resolves(5n);
104
+ sinon.stub(token, 'getHypAdapter').returns(adapter);
105
+ const monitor = new Monitor(0, new WarpCore(multiProvider, [token]), logger);
106
+ return { monitor, adapter, block, token };
107
+ }
108
+
109
+ describe('Monitor concurrency', () => {
110
+ afterEach(() => sinon.restore());
111
+
112
+ it('bounds block and inventory reads, filling slots while another chain is slow', async () => {
113
+ const { monitor, tokens, blocks, adapters } = setup(10, true);
114
+ const blockGates = blocks.map(() => sinon.promise<string>());
115
+ const inventoryGates = adapters.map(() => sinon.promise<bigint>());
116
+ blocks.forEach((block, index) => block.send.returns(blockGates[index]));
117
+ adapters.forEach((adapter, index) =>
118
+ adapter.getBalance.returns(inventoryGates[index]),
119
+ );
120
+ let event: MonitorEvent | undefined;
121
+ monitor.on(MonitorEventType.TokenInfo, (received) => {
122
+ event = received;
123
+ void monitor.stop();
124
+ });
125
+
126
+ const running = monitor.start();
127
+ await nextTurn();
128
+ expect(blocks.filter((block) => block.send.called).length).to.equal(8);
129
+ expect(
130
+ adapters.filter((adapter) => adapter.getBalance.called).length,
131
+ ).to.equal(8);
132
+ void blockGates[1].resolve('0x65');
133
+ void inventoryGates[1].resolve(11n);
134
+ await nextTurn();
135
+ expect(blocks[8].send.calledOnce).to.equal(true);
136
+ expect(adapters[8].getBalance.calledOnce).to.equal(true);
137
+ expect(blocks[0].send.calledOnce).to.equal(true);
138
+ expect(blocks[9].send.called).to.equal(false);
139
+ expect(adapters[9].getBalance.called).to.equal(false);
140
+ expect(
141
+ adapters.every((adapter) => adapter.getBridgedSupply.notCalled),
142
+ ).to.equal(true);
143
+
144
+ blockGates.forEach((gate, index) => {
145
+ if (index !== 1) void gate.resolve(`0x${(100 + index).toString(16)}`);
146
+ });
147
+ await nextTurn();
148
+ expect(
149
+ adapters.every((adapter) => adapter.getBridgedSupply.calledOnce),
150
+ ).to.equal(true);
151
+ expect(event).to.equal(undefined);
152
+ inventoryGates.forEach((gate, index) => {
153
+ if (index !== 1) void gate.resolve(BigInt(index + 10));
154
+ });
155
+ await running;
156
+ expect(event?.tokensInfo.map(({ token }) => token)).to.deep.equal(tokens);
157
+ expect(event?.confirmedBlockTags.chain1).to.equal(99);
158
+ expect(event?.inventoryBalances?.chain9).to.equal(19n);
159
+ adapters.forEach((adapter, index) => {
160
+ expect(adapter.getBridgedSupply.firstCall.args).to.deep.equal([
161
+ { blockTag: 98 + index },
162
+ ]);
163
+ });
164
+ });
165
+
166
+ it('bounds supply reads and preserves token order when reads complete out of order', async () => {
167
+ const { monitor, tokens, adapters } = setup(10);
168
+ const gates = adapters.map(() => sinon.promise<bigint>());
169
+ adapters.forEach((adapter, index) =>
170
+ adapter.getBridgedSupply.returns(gates[index]),
171
+ );
172
+ let event: MonitorEvent | undefined;
173
+ monitor.on(MonitorEventType.TokenInfo, (received) => {
174
+ event = received;
175
+ void monitor.stop();
176
+ });
177
+
178
+ const running = monitor.start();
179
+ await nextTurn();
180
+ expect(
181
+ adapters.filter((adapter) => adapter.getBridgedSupply.called).length,
182
+ ).to.equal(8);
183
+ void gates[1].resolve(1n);
184
+ await nextTurn();
185
+ expect(adapters[8].getBridgedSupply.calledOnce).to.equal(true);
186
+ gates.forEach((gate, index) => {
187
+ if (index !== 1) void gate.resolve(BigInt(index));
188
+ });
189
+ await running;
190
+ expect(event?.tokensInfo).to.deep.equal(
191
+ tokens.map((token, index) => ({ token, bridgedSupply: BigInt(index) })),
192
+ );
193
+ });
194
+
195
+ it('drains reads after a supply failure before reporting the failed cycle', async () => {
196
+ const { monitor, adapters } = setup(2, true);
197
+ const supply = sinon.promise<bigint>();
198
+ const inventory = sinon.promise<bigint>();
199
+ adapters[0].getBridgedSupply.rejects(new Error('supply unavailable'));
200
+ adapters[1].getBridgedSupply.returns(supply);
201
+ adapters[0].getBalance.returns(inventory);
202
+ const handler = sinon.stub();
203
+ const errors = sinon.stub();
204
+ monitor.on(MonitorEventType.TokenInfo, handler);
205
+ monitor.on(MonitorEventType.Error, (error) => {
206
+ errors(error);
207
+ void monitor.stop();
208
+ });
209
+
210
+ const running = monitor.start();
211
+ await nextTurn();
212
+ expect(errors.notCalled).to.equal(true);
213
+ void supply.resolve(1n);
214
+ await nextTurn();
215
+ expect(errors.notCalled).to.equal(true);
216
+ void inventory.resolve(2n);
217
+ await running;
218
+ expect(errors.calledOnce).to.equal(true);
219
+ expect(handler.notCalled).to.equal(true);
220
+ expect(adapters[0].getBridgedSupply.calledOnce).to.equal(true);
221
+ expect(adapters[0].getBridgedSupply.firstCall.args).to.deep.equal([
222
+ { blockTag: 98 },
223
+ ]);
224
+ });
225
+
226
+ it('drains supply and inventory reads after an inventory failure without emitting balances', async () => {
227
+ const { monitor, adapters } = setup(2, true);
228
+ const supply = sinon.promise<bigint>();
229
+ const inventory = sinon.promise<bigint>();
230
+ adapters[0].getBalance.rejects(new Error('inventory unavailable'));
231
+ adapters[1].getBalance.returns(inventory);
232
+ adapters[0].getBridgedSupply.returns(supply);
233
+ const handler = sinon.stub();
234
+ const errors = sinon.stub();
235
+ monitor.on(MonitorEventType.TokenInfo, handler);
236
+ monitor.on(MonitorEventType.Error, (error) => {
237
+ errors(error);
238
+ void monitor.stop();
239
+ });
240
+
241
+ const running = monitor.start();
242
+ await nextTurn();
243
+ expect(errors.notCalled).to.equal(true);
244
+ void inventory.resolve(11n);
245
+ await nextTurn();
246
+ expect(errors.notCalled).to.equal(true);
247
+ void supply.resolve(0n);
248
+ await running;
249
+ expect(errors.calledOnce).to.equal(true);
250
+ expect(errors.firstCall.args[0].message).to.contain(
251
+ 'inventory unavailable',
252
+ );
253
+ expect(handler.notCalled).to.equal(true);
254
+ });
255
+
256
+ it('retries a failed confirmed block lookup without emitting latest-state supply', async () => {
257
+ const { monitor, blocks, adapters } = setup(1, true);
258
+ blocks[0].send.onFirstCall().rejects(new Error('block unavailable'));
259
+ const errors = sinon.stub();
260
+ let event: MonitorEvent | undefined;
261
+ let supplyReadOnError = false;
262
+ monitor.on(MonitorEventType.Error, (error) => {
263
+ errors(error);
264
+ supplyReadOnError = adapters[0].getBridgedSupply.called;
265
+ });
266
+ monitor.on(MonitorEventType.TokenInfo, (received) => {
267
+ event = received;
268
+ void monitor.stop();
269
+ });
270
+
271
+ await monitor.start();
272
+ expect(errors.calledOnce).to.equal(true);
273
+ expect(errors.firstCall.args[0].message).to.contain(
274
+ 'Confirmed block unavailable for chain0',
275
+ );
276
+ expect(supplyReadOnError).to.equal(false);
277
+ expect(blocks[0].send.calledTwice).to.equal(true);
278
+ expect(adapters[0].getBridgedSupply.calledOnce).to.equal(true);
279
+ expect(adapters[0].getBridgedSupply.firstCall.args).to.deep.equal([
280
+ { blockTag: 98 },
281
+ ]);
282
+ expect(event?.confirmedBlockTags.chain0).to.equal(98);
283
+ expect(event?.tokensInfo[0].bridgedSupply).to.equal(0n);
284
+ expect(event?.inventoryBalances).to.deep.equal({ chain0: 10n });
285
+ });
286
+
287
+ it('preserves optional block tags for Tron named reorg periods', async () => {
288
+ const { monitor, adapter, block, token } = setupTron('finalized');
289
+ const errors = sinon.stub();
290
+ let event: MonitorEvent | undefined;
291
+ monitor.on(MonitorEventType.Error, (error) => {
292
+ errors(error);
293
+ void monitor.stop();
294
+ });
295
+ monitor.on(MonitorEventType.TokenInfo, (received) => {
296
+ event = received;
297
+ void monitor.stop();
298
+ });
299
+
300
+ await monitor.start();
301
+ expect(errors.notCalled).to.equal(true);
302
+ expect(block.send.notCalled).to.equal(true);
303
+ expect(adapter.getBridgedSupply.calledOnce).to.equal(true);
304
+ expect(adapter.getBridgedSupply.firstCall.args).to.deep.equal([
305
+ { blockTag: undefined },
306
+ ]);
307
+ expect(event?.confirmedBlockTags.tron).to.equal(undefined);
308
+ expect(event?.tokensInfo).to.deep.equal([{ token, bridgedSupply: 5n }]);
309
+ });
310
+
311
+ for (const failWithRpcError of [true, false]) {
312
+ it(`retries a numeric Tron block ${failWithRpcError ? 'RPC failure' : 'invalid response'} before emitting supply`, async () => {
313
+ const { monitor, adapter, block } = setupTron(20);
314
+ if (failWithRpcError) {
315
+ block.send.onFirstCall().rejects(new Error('block unavailable'));
316
+ } else {
317
+ block.send.onFirstCall().resolves(undefined);
318
+ }
319
+ const errors = sinon.stub();
320
+ let supplyReadOnError = false;
321
+ let event: MonitorEvent | undefined;
322
+ monitor.on(MonitorEventType.Error, (error) => {
323
+ errors(error);
324
+ supplyReadOnError = adapter.getBridgedSupply.called;
325
+ });
326
+ monitor.on(MonitorEventType.TokenInfo, (received) => {
327
+ event = received;
328
+ void monitor.stop();
329
+ });
330
+
331
+ await monitor.start();
332
+ expect(errors.calledOnce).to.equal(true);
333
+ expect(errors.firstCall.args[0].message).to.contain(
334
+ failWithRpcError
335
+ ? 'block unavailable'
336
+ : 'Invalid eth_blockNumber response',
337
+ );
338
+ expect(supplyReadOnError).to.equal(false);
339
+ expect(block.send.calledTwice).to.equal(true);
340
+ expect(adapter.getBridgedSupply.calledOnce).to.equal(true);
341
+ expect(adapter.getBridgedSupply.firstCall.args).to.deep.equal([
342
+ { blockTag: 80 },
343
+ ]);
344
+ expect(event?.confirmedBlockTags.tron).to.equal(80);
345
+ expect(event?.tokensInfo[0].bridgedSupply).to.equal(5n);
346
+ });
347
+ }
348
+
349
+ it('emits inventory balances from a later cycle after an inventory failure', async () => {
350
+ const { monitor, adapters } = setup(1, true);
351
+ adapters[0].getBalance
352
+ .onFirstCall()
353
+ .rejects(new Error('inventory unavailable'));
354
+ const errors = sinon.stub();
355
+ const handler = sinon.stub();
356
+ let event: MonitorEvent | undefined;
357
+ monitor.on(MonitorEventType.Error, errors);
358
+ monitor.on(MonitorEventType.TokenInfo, (received) => {
359
+ handler(received);
360
+ event = received;
361
+ void monitor.stop();
362
+ });
363
+
364
+ await monitor.start();
365
+ expect(errors.calledOnce).to.equal(true);
366
+ expect(errors.firstCall.args[0].message).to.contain(
367
+ 'inventory unavailable',
368
+ );
369
+ expect(handler.calledOnce).to.equal(true);
370
+ sinon.assert.callOrder(errors, handler);
371
+ expect(adapters[0].getBalance.calledTwice).to.equal(true);
372
+ expect(event?.inventoryBalances).to.deep.equal({ chain0: 10n });
373
+ });
374
+
375
+ it('emits successfully read zero inventory balances', async () => {
376
+ const { monitor, adapters } = setup(1, true);
377
+ adapters[0].getBalance.resolves(0n);
378
+ let event: MonitorEvent | undefined;
379
+ monitor.on(MonitorEventType.TokenInfo, (received) => {
380
+ event = received;
381
+ void monitor.stop();
382
+ });
383
+ await monitor.start();
384
+ expect(event?.inventoryBalances).to.deep.equal({ chain0: 0n });
385
+ });
386
+
387
+ it('awaits its consumer before another polling cycle and before stop completes', async () => {
388
+ const { monitor, adapters } = setup(1);
389
+ const consumer = sinon.promise<void>();
390
+ const handler = sinon.stub().returns(consumer);
391
+ monitor.on(MonitorEventType.TokenInfo, handler);
392
+ const running = monitor.start();
393
+ await nextTurn();
394
+ expect(handler.calledOnce).to.equal(true);
395
+ expect(adapters[0].getBridgedSupply.calledOnce).to.equal(true);
396
+
397
+ let stopped = false;
398
+ const stop = monitor.stop().then(() => {
399
+ stopped = true;
400
+ });
401
+ await nextTurn();
402
+ expect(stopped).to.equal(false);
403
+ expect(adapters[0].getBridgedSupply.calledOnce).to.equal(true);
404
+ void consumer.resolve(undefined);
405
+ await Promise.all([running, stop]);
406
+ expect(stopped).to.equal(true);
407
+ });
408
+ });
@@ -6,7 +6,14 @@ import {
6
6
  type Token,
7
7
  type WarpCore,
8
8
  } from '@humanode-hyperlane/sdk';
9
- import { Address, ProtocolType, sleep } from '@humanode-hyperlane/utils';
9
+ import {
10
+ Address,
11
+ ProtocolType,
12
+ assert,
13
+ concurrentMap,
14
+ fromWei,
15
+ sleep,
16
+ } from '@humanode-hyperlane/utils';
10
17
 
11
18
  import {
12
19
  type ConfirmedBlockTag,
@@ -19,6 +26,9 @@ import {
19
26
  } from '../interfaces/IMonitor.js';
20
27
  import { getConfirmedBlockTag } from '../utils/blockTag.js';
21
28
 
29
+ // Bound each read group so route size cannot create unlimited RPC work.
30
+ const MONITOR_READ_CONCURRENCY = 8;
31
+
22
32
  /**
23
33
  * Configuration for the Monitor's inventory tracking.
24
34
  */
@@ -53,17 +63,43 @@ export class Monitor implements IMonitor {
53
63
  const blockTags: ConfirmedBlockTags = {};
54
64
  const chains = new Set(this.warpCore.tokens.map((t) => t.chainName));
55
65
 
56
- for (const chain of chains) {
57
- blockTags[chain] = await getConfirmedBlockTag(
58
- this.warpCore.multiProvider,
59
- chain,
60
- this.logger,
61
- );
62
- }
66
+ await concurrentMap(
67
+ MONITOR_READ_CONCURRENCY,
68
+ [...chains],
69
+ async (chain) => {
70
+ blockTags[chain] = await getConfirmedBlockTag(
71
+ this.warpCore.multiProvider,
72
+ chain,
73
+ this.logger,
74
+ );
75
+ },
76
+ );
63
77
 
64
78
  return blockTags;
65
79
  }
66
80
 
81
+ private async fetchTokenInfo(): Promise<MonitorEvent> {
82
+ const confirmedBlockTags = await this.computeConfirmedBlockTags();
83
+ const tokensInfo = await concurrentMap(
84
+ MONITOR_READ_CONCURRENCY,
85
+ this.warpCore.tokens,
86
+ async (token) => {
87
+ this.logger.debug(
88
+ {
89
+ chain: token.chainName,
90
+ tokenSymbol: token.symbol,
91
+ tokenAddress: token.addressOrDenom,
92
+ },
93
+ 'Checking token',
94
+ );
95
+ const blockTag = confirmedBlockTags[token.chainName];
96
+ const bridgedSupply = await this.getTokenBridgedSupply(token, blockTag);
97
+ return { token, bridgedSupply };
98
+ },
99
+ );
100
+ return { tokensInfo, confirmedBlockTags };
101
+ }
102
+
67
103
  // overloads from IMonitor
68
104
  on(
69
105
  eventName: MonitorEventType.TokenInfo,
@@ -109,47 +145,40 @@ export class Monitor implements IMonitor {
109
145
  try {
110
146
  this.logger.debug('Polling cycle started');
111
147
 
112
- const confirmedBlockTags = await this.computeConfirmedBlockTags();
113
-
114
- const event: MonitorEvent = {
115
- tokensInfo: [],
116
- confirmedBlockTags,
117
- };
118
-
119
- for (const token of this.warpCore.tokens) {
120
- this.logger.debug(
121
- {
122
- chain: token.chainName,
123
- tokenSymbol: token.symbol,
124
- tokenAddress: token.addressOrDenom,
125
- },
126
- 'Checking token',
127
- );
128
- const blockTag = confirmedBlockTags[token.chainName];
129
- const bridgedSupply = await this.getTokenBridgedSupply(
130
- token,
131
- blockTag,
132
- );
133
-
134
- event.tokensInfo.push({
135
- token,
136
- bridgedSupply,
137
- });
138
- }
139
-
140
- const inventoryBalances = await this.fetchInventoryBalances();
148
+ // Drain both groups on error before a retry can start another cycle.
149
+ const [tokenRead, inventoryRead] = await Promise.allSettled([
150
+ this.fetchTokenInfo(),
151
+ this.fetchInventoryBalances(),
152
+ ]);
153
+ if (tokenRead.status === 'rejected') throw tokenRead.reason;
154
+ if (inventoryRead.status === 'rejected') throw inventoryRead.reason;
155
+ const event = tokenRead.value;
156
+ const inventoryBalances = inventoryRead.value;
141
157
  if (Object.keys(inventoryBalances).length > 0) {
142
158
  event.inventoryBalances = inventoryBalances;
159
+ const tokensByChain = new Map(
160
+ this.warpCore.tokens.map((token) => [token.chainName, token]),
161
+ );
162
+ // CAST: inventoryBalances keys come from configured monitor chains,
163
+ // but Object.entries widens them to string.
164
+ const inventoryBalanceEntries = Object.entries(
165
+ inventoryBalances,
166
+ ) as [ChainName, bigint][];
143
167
  this.logger.info(
144
168
  {
145
169
  chainsMonitored: Object.keys(inventoryBalances).length,
146
- balances: Object.entries(inventoryBalances).map(
147
- ([chain, balance]) => ({
170
+ balances: inventoryBalanceEntries.map(([chain, balance]) => {
171
+ const token = tokensByChain.get(chain);
172
+ return {
148
173
  chain,
174
+ tokenSymbol: token?.symbol,
149
175
  balance: balance.toString(),
150
- balanceEth: (Number(balance) / 1e18).toFixed(6),
151
- }),
152
- ),
176
+ balanceFormatted: fromWei(
177
+ balance.toString(),
178
+ token?.decimals,
179
+ ),
180
+ };
181
+ }),
153
182
  },
154
183
  'Inventory balances fetched',
155
184
  );
@@ -215,26 +244,16 @@ export class Monitor implements IMonitor {
215
244
  return;
216
245
  }
217
246
 
247
+ assert(
248
+ token.protocol !== ProtocolType.Ethereum || blockTag !== undefined,
249
+ `Confirmed block unavailable for ${token.chainName}`,
250
+ );
218
251
  const adapter = token.getHypAdapter(this.warpCore.multiProvider);
219
- let bridgedSupply: bigint | undefined;
220
-
221
- try {
222
- bridgedSupply = await adapter.getBridgedSupply({ blockTag });
223
- this.logger.debug(
224
- { chain: token.chainName, blockTag },
225
- 'Queried confirmed balance',
226
- );
227
- } catch (error) {
228
- this.logger.warn(
229
- {
230
- chain: token.chainName,
231
- blockTag,
232
- error: (error as Error).message,
233
- },
234
- 'Historical block query failed, falling back to latest',
235
- );
236
- bridgedSupply = await adapter.getBridgedSupply();
237
- }
252
+ const bridgedSupply = await adapter.getBridgedSupply({ blockTag });
253
+ this.logger.debug(
254
+ { chain: token.chainName, blockTag },
255
+ 'Queried confirmed balance',
256
+ );
238
257
 
239
258
  if (bridgedSupply === undefined) {
240
259
  this.logger.warn(
@@ -251,51 +270,58 @@ export class Monitor implements IMonitor {
251
270
  }
252
271
 
253
272
  private async fetchInventoryBalances(): Promise<ChainMap<bigint>> {
254
- if (!this.inventoryConfig) return {};
273
+ const inventoryConfig = this.inventoryConfig;
274
+ if (!inventoryConfig) return {};
255
275
 
256
276
  const balances: ChainMap<bigint> = {};
257
277
 
258
- const readPromises = this.inventoryConfig.chains.map(async (chainName) => {
259
- const token = this.warpCore.tokens.find((t) => t.chainName === chainName);
260
- if (!token) {
261
- this.logger.warn(
262
- { chain: chainName },
263
- 'No token found for inventory chain',
278
+ const results = await concurrentMap(
279
+ MONITOR_READ_CONCURRENCY,
280
+ inventoryConfig.chains,
281
+ async (chainName) => {
282
+ const token = this.warpCore.tokens.find(
283
+ (t) => t.chainName === chainName,
264
284
  );
265
- return { chainName, balance: 0n };
266
- }
267
-
268
- try {
269
- const address =
270
- this.inventoryConfig!.inventoryAddresses[token.protocol];
271
- if (!address) {
285
+ if (!token) {
272
286
  this.logger.warn(
273
- { chain: chainName, protocol: token.protocol },
274
- 'No inventory address for chain protocol, skipping',
287
+ { chain: chainName },
288
+ 'No token found for inventory chain',
275
289
  );
276
290
  return { chainName, balance: 0n };
277
291
  }
278
- const adapter = token.getAdapter(this.warpCore.multiProvider);
279
- const balance = await adapter.getBalance(address);
280
- this.logger.debug(
281
- {
282
- chain: chainName,
283
- token: token.addressOrDenom,
284
- balance: balance.toString(),
285
- },
286
- 'Read inventory balance',
287
- );
288
- return { chainName, balance };
289
- } catch (error) {
290
- this.logger.error(
291
- { chain: chainName, error: (error as Error).message },
292
- 'Failed to read inventory balance',
293
- );
294
- return { chainName, balance: 0n };
295
- }
296
- });
297
292
 
298
- const results = await Promise.all(readPromises);
293
+ try {
294
+ const address = inventoryConfig.inventoryAddresses[token.protocol];
295
+ if (!address) {
296
+ this.logger.warn(
297
+ { chain: chainName, protocol: token.protocol },
298
+ 'No inventory address for chain protocol, skipping',
299
+ );
300
+ return { chainName, balance: 0n };
301
+ }
302
+ const adapter = token.getAdapter(this.warpCore.multiProvider);
303
+ const balance = await adapter.getBalance(address);
304
+ this.logger.debug(
305
+ {
306
+ chain: chainName,
307
+ token: token.addressOrDenom,
308
+ balance: balance.toString(),
309
+ },
310
+ 'Read inventory balance',
311
+ );
312
+ return { chainName, balance };
313
+ } catch (error) {
314
+ this.logger.error(
315
+ {
316
+ chain: chainName,
317
+ error: error instanceof Error ? error.message : String(error),
318
+ },
319
+ 'Failed to read inventory balance',
320
+ );
321
+ throw error;
322
+ }
323
+ },
324
+ );
299
325
 
300
326
  for (const { chainName, balance } of results) {
301
327
  balances[chainName] = balance;