@hyperlane-xyz/rebalancer 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/dist/bridges/LiFiBridge.d.ts +67 -0
  2. package/dist/bridges/LiFiBridge.d.ts.map +1 -0
  3. package/dist/bridges/LiFiBridge.js +386 -0
  4. package/dist/bridges/LiFiBridge.js.map +1 -0
  5. package/dist/config/RebalancerConfig.d.ts +7 -2
  6. package/dist/config/RebalancerConfig.d.ts.map +1 -1
  7. package/dist/config/RebalancerConfig.js +7 -4
  8. package/dist/config/RebalancerConfig.js.map +1 -1
  9. package/dist/config/RebalancerConfig.test.js +134 -1
  10. package/dist/config/RebalancerConfig.test.js.map +1 -1
  11. package/dist/config/types.d.ts +1016 -304
  12. package/dist/config/types.d.ts.map +1 -1
  13. package/dist/config/types.js +105 -10
  14. package/dist/config/types.js.map +1 -1
  15. package/dist/core/InventoryRebalancer.d.ts +190 -0
  16. package/dist/core/InventoryRebalancer.d.ts.map +1 -0
  17. package/dist/core/InventoryRebalancer.js +885 -0
  18. package/dist/core/InventoryRebalancer.js.map +1 -0
  19. package/dist/core/InventoryRebalancer.test.d.ts +2 -0
  20. package/dist/core/InventoryRebalancer.test.d.ts.map +1 -0
  21. package/dist/core/InventoryRebalancer.test.js +1351 -0
  22. package/dist/core/InventoryRebalancer.test.js.map +1 -0
  23. package/dist/core/Rebalancer.d.ts +11 -4
  24. package/dist/core/Rebalancer.d.ts.map +1 -1
  25. package/dist/core/Rebalancer.js +92 -9
  26. package/dist/core/Rebalancer.js.map +1 -1
  27. package/dist/core/Rebalancer.test.js +82 -49
  28. package/dist/core/Rebalancer.test.js.map +1 -1
  29. package/dist/core/RebalancerOrchestrator.d.ts +30 -9
  30. package/dist/core/RebalancerOrchestrator.d.ts.map +1 -1
  31. package/dist/core/RebalancerOrchestrator.js +79 -71
  32. package/dist/core/RebalancerOrchestrator.js.map +1 -1
  33. package/dist/core/RebalancerOrchestrator.test.d.ts +2 -0
  34. package/dist/core/RebalancerOrchestrator.test.d.ts.map +1 -0
  35. package/dist/core/RebalancerOrchestrator.test.js +714 -0
  36. package/dist/core/RebalancerOrchestrator.test.js.map +1 -0
  37. package/dist/core/RebalancerService.d.ts +7 -3
  38. package/dist/core/RebalancerService.d.ts.map +1 -1
  39. package/dist/core/RebalancerService.js +44 -24
  40. package/dist/core/RebalancerService.js.map +1 -1
  41. package/dist/core/RebalancerService.test.js +71 -109
  42. package/dist/core/RebalancerService.test.js.map +1 -1
  43. package/dist/e2e/collateral-deficit.e2e-test.js +1 -3
  44. package/dist/e2e/collateral-deficit.e2e-test.js.map +1 -1
  45. package/dist/e2e/composite.e2e-test.js.map +1 -1
  46. package/dist/e2e/harness/BridgeSetup.d.ts +6 -0
  47. package/dist/e2e/harness/BridgeSetup.d.ts.map +1 -1
  48. package/dist/e2e/harness/BridgeSetup.js +10 -1
  49. package/dist/e2e/harness/BridgeSetup.js.map +1 -1
  50. package/dist/e2e/harness/TestHelpers.d.ts.map +1 -1
  51. package/dist/e2e/harness/TestHelpers.js +1 -4
  52. package/dist/e2e/harness/TestHelpers.js.map +1 -1
  53. package/dist/e2e/harness/TestRebalancer.d.ts +1 -1
  54. package/dist/e2e/harness/TestRebalancer.d.ts.map +1 -1
  55. package/dist/e2e/harness/TestRebalancer.js +6 -7
  56. package/dist/e2e/harness/TestRebalancer.js.map +1 -1
  57. package/dist/e2e/minAmount.e2e-test.js +0 -1
  58. package/dist/e2e/minAmount.e2e-test.js.map +1 -1
  59. package/dist/e2e/weighted.e2e-test.js +0 -1
  60. package/dist/e2e/weighted.e2e-test.js.map +1 -1
  61. package/dist/factories/RebalancerContextFactory.d.ts +48 -6
  62. package/dist/factories/RebalancerContextFactory.d.ts.map +1 -1
  63. package/dist/factories/RebalancerContextFactory.js +170 -17
  64. package/dist/factories/RebalancerContextFactory.js.map +1 -1
  65. package/dist/index.d.ts +5 -5
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +1 -1
  68. package/dist/index.js.map +1 -1
  69. package/dist/interfaces/IExternalBridge.d.ts +101 -0
  70. package/dist/interfaces/IExternalBridge.d.ts.map +1 -0
  71. package/dist/interfaces/IExternalBridge.js +2 -0
  72. package/dist/interfaces/IExternalBridge.js.map +1 -0
  73. package/dist/interfaces/IMonitor.d.ts +1 -0
  74. package/dist/interfaces/IMonitor.d.ts.map +1 -1
  75. package/dist/interfaces/IRebalancer.d.ts +25 -25
  76. package/dist/interfaces/IRebalancer.d.ts.map +1 -1
  77. package/dist/interfaces/IStrategy.d.ts +36 -3
  78. package/dist/interfaces/IStrategy.d.ts.map +1 -1
  79. package/dist/interfaces/IStrategy.js +12 -1
  80. package/dist/interfaces/IStrategy.js.map +1 -1
  81. package/dist/metrics/PriceGetter.js +1 -1
  82. package/dist/metrics/PriceGetter.js.map +1 -1
  83. package/dist/metrics/scripts/metrics.d.ts +3 -3
  84. package/dist/monitor/Monitor.d.ts +12 -2
  85. package/dist/monitor/Monitor.d.ts.map +1 -1
  86. package/dist/monitor/Monitor.js +46 -1
  87. package/dist/monitor/Monitor.js.map +1 -1
  88. package/dist/service.js +40 -17
  89. package/dist/service.js.map +1 -1
  90. package/dist/strategy/BaseStrategy.d.ts +12 -6
  91. package/dist/strategy/BaseStrategy.d.ts.map +1 -1
  92. package/dist/strategy/BaseStrategy.js +56 -21
  93. package/dist/strategy/BaseStrategy.js.map +1 -1
  94. package/dist/strategy/CollateralDeficitStrategy.d.ts +1 -1
  95. package/dist/strategy/CollateralDeficitStrategy.d.ts.map +1 -1
  96. package/dist/strategy/CollateralDeficitStrategy.js +19 -11
  97. package/dist/strategy/CollateralDeficitStrategy.js.map +1 -1
  98. package/dist/strategy/CollateralDeficitStrategy.test.js +135 -2
  99. package/dist/strategy/CollateralDeficitStrategy.test.js.map +1 -1
  100. package/dist/strategy/CompositeStrategy.test.js +13 -0
  101. package/dist/strategy/CompositeStrategy.test.js.map +1 -1
  102. package/dist/strategy/MinAmountStrategy.test.js +4 -0
  103. package/dist/strategy/MinAmountStrategy.test.js.map +1 -1
  104. package/dist/strategy/StrategyFactory.d.ts +2 -1
  105. package/dist/strategy/StrategyFactory.d.ts.map +1 -1
  106. package/dist/strategy/StrategyFactory.js +24 -8
  107. package/dist/strategy/StrategyFactory.js.map +1 -1
  108. package/dist/strategy/WeightedStrategy.test.js +6 -0
  109. package/dist/strategy/WeightedStrategy.test.js.map +1 -1
  110. package/dist/test/helpers.d.ts +8 -7
  111. package/dist/test/helpers.d.ts.map +1 -1
  112. package/dist/test/helpers.js +23 -5
  113. package/dist/test/helpers.js.map +1 -1
  114. package/dist/test/lifiMocks.d.ts +51 -0
  115. package/dist/test/lifiMocks.d.ts.map +1 -0
  116. package/dist/test/lifiMocks.js +130 -0
  117. package/dist/test/lifiMocks.js.map +1 -0
  118. package/dist/tracking/ActionTracker.d.ts +33 -1
  119. package/dist/tracking/ActionTracker.d.ts.map +1 -1
  120. package/dist/tracking/ActionTracker.js +193 -22
  121. package/dist/tracking/ActionTracker.js.map +1 -1
  122. package/dist/tracking/ActionTracker.test.js +107 -19
  123. package/dist/tracking/ActionTracker.test.js.map +1 -1
  124. package/dist/tracking/IActionTracker.d.ts +47 -3
  125. package/dist/tracking/IActionTracker.d.ts.map +1 -1
  126. package/dist/tracking/InflightContextAdapter.d.ts.map +1 -1
  127. package/dist/tracking/InflightContextAdapter.js +24 -7
  128. package/dist/tracking/InflightContextAdapter.js.map +1 -1
  129. package/dist/tracking/InflightContextAdapter.test.js +7 -4
  130. package/dist/tracking/InflightContextAdapter.test.js.map +1 -1
  131. package/dist/tracking/types.d.ts +31 -2
  132. package/dist/tracking/types.d.ts.map +1 -1
  133. package/dist/utils/ExplorerClient.d.ts +2 -1
  134. package/dist/utils/ExplorerClient.d.ts.map +1 -1
  135. package/dist/utils/ExplorerClient.js +13 -8
  136. package/dist/utils/ExplorerClient.js.map +1 -1
  137. package/dist/utils/bridgeUtils.d.ts +27 -4
  138. package/dist/utils/bridgeUtils.d.ts.map +1 -1
  139. package/dist/utils/bridgeUtils.js +38 -0
  140. package/dist/utils/bridgeUtils.js.map +1 -1
  141. package/dist/utils/bridgeUtils.test.js +9 -0
  142. package/dist/utils/bridgeUtils.test.js.map +1 -1
  143. package/dist/utils/gasEstimation.d.ts +65 -0
  144. package/dist/utils/gasEstimation.d.ts.map +1 -0
  145. package/dist/utils/gasEstimation.js +176 -0
  146. package/dist/utils/gasEstimation.js.map +1 -0
  147. package/dist/utils/tokenUtils.d.ts +9 -1
  148. package/dist/utils/tokenUtils.d.ts.map +1 -1
  149. package/dist/utils/tokenUtils.js +11 -0
  150. package/dist/utils/tokenUtils.js.map +1 -1
  151. package/package.json +9 -7
  152. package/src/bridges/LiFiBridge.ts +538 -0
  153. package/src/config/RebalancerConfig.test.ts +160 -0
  154. package/src/config/RebalancerConfig.ts +14 -3
  155. package/src/config/types.ts +136 -10
  156. package/src/core/InventoryRebalancer.test.ts +1684 -0
  157. package/src/core/InventoryRebalancer.ts +1255 -0
  158. package/src/core/Rebalancer.test.ts +84 -30
  159. package/src/core/Rebalancer.ts +144 -23
  160. package/src/core/RebalancerOrchestrator.test.ts +860 -0
  161. package/src/core/RebalancerOrchestrator.ts +146 -95
  162. package/src/core/RebalancerService.test.ts +80 -123
  163. package/src/core/RebalancerService.ts +67 -33
  164. package/src/e2e/collateral-deficit.e2e-test.ts +2 -4
  165. package/src/e2e/composite.e2e-test.ts +5 -5
  166. package/src/e2e/harness/BridgeSetup.ts +28 -1
  167. package/src/e2e/harness/TestHelpers.ts +1 -4
  168. package/src/e2e/harness/TestRebalancer.ts +7 -7
  169. package/src/e2e/minAmount.e2e-test.ts +1 -2
  170. package/src/e2e/weighted.e2e-test.ts +1 -2
  171. package/src/factories/RebalancerContextFactory.ts +293 -24
  172. package/src/index.ts +20 -5
  173. package/src/interfaces/IExternalBridge.ts +115 -0
  174. package/src/interfaces/IMonitor.ts +1 -0
  175. package/src/interfaces/IRebalancer.ts +45 -29
  176. package/src/interfaces/IStrategy.ts +50 -3
  177. package/src/metrics/PriceGetter.ts +1 -1
  178. package/src/monitor/Monitor.ts +81 -2
  179. package/src/service.ts +59 -18
  180. package/src/strategy/BaseStrategy.ts +77 -24
  181. package/src/strategy/CollateralDeficitStrategy.test.ts +181 -4
  182. package/src/strategy/CollateralDeficitStrategy.ts +42 -15
  183. package/src/strategy/CompositeStrategy.test.ts +13 -0
  184. package/src/strategy/MinAmountStrategy.test.ts +4 -0
  185. package/src/strategy/StrategyFactory.ts +33 -6
  186. package/src/strategy/WeightedStrategy.test.ts +6 -0
  187. package/src/test/helpers.ts +39 -14
  188. package/src/test/lifiMocks.ts +174 -0
  189. package/src/tracking/ActionTracker.test.ts +122 -19
  190. package/src/tracking/ActionTracker.ts +284 -24
  191. package/src/tracking/IActionTracker.ts +58 -3
  192. package/src/tracking/InflightContextAdapter.test.ts +7 -4
  193. package/src/tracking/InflightContextAdapter.ts +42 -9
  194. package/src/tracking/types.ts +43 -2
  195. package/src/utils/ExplorerClient.ts +23 -10
  196. package/src/utils/bridgeUtils.test.ts +9 -0
  197. package/src/utils/bridgeUtils.ts +75 -6
  198. package/src/utils/gasEstimation.ts +272 -0
  199. package/src/utils/tokenUtils.ts +12 -0
  200. package/dist/tracking/index.d.ts +0 -7
  201. package/dist/tracking/index.d.ts.map +0 -1
  202. package/dist/tracking/index.js +0 -6
  203. package/dist/tracking/index.js.map +0 -1
  204. package/dist/utils/index.d.ts +0 -5
  205. package/dist/utils/index.d.ts.map +0 -1
  206. package/dist/utils/index.js +0 -5
  207. package/dist/utils/index.js.map +0 -1
  208. package/src/tracking/index.ts +0 -36
  209. package/src/utils/index.ts +0 -4
@@ -1,6 +1,9 @@
1
1
  import { BigNumber, ethers, providers } from 'ethers';
2
2
 
3
- import { ERC20__factory } from '@hyperlane-xyz/core';
3
+ import {
4
+ ERC20__factory,
5
+ MovableCollateralRouter__factory,
6
+ } from '@hyperlane-xyz/core';
4
7
 
5
8
  export async function setupCollateralBalances(
6
9
  providers: Map<string, providers.JsonRpcProvider>,
@@ -65,3 +68,27 @@ export async function getAllCollateralBalances(
65
68
  }
66
69
  return balances;
67
70
  }
71
+
72
+ export interface AllowedBridgeConfig {
73
+ monitoredRouterAddress: string;
74
+ bridgeAddress: string;
75
+ destinationDomain: number;
76
+ }
77
+
78
+ export async function configureAllowedBridges(
79
+ provider: providers.JsonRpcProvider,
80
+ configs: AllowedBridgeConfig[],
81
+ deployerKey: string,
82
+ ): Promise<void> {
83
+ if (configs.length === 0) return;
84
+
85
+ const deployer = new ethers.Wallet(deployerKey, provider);
86
+ const router = MovableCollateralRouter__factory.connect(
87
+ configs[0].monitoredRouterAddress,
88
+ deployer,
89
+ );
90
+
91
+ for (const config of configs) {
92
+ await router.addBridge(config.destinationDomain, config.bridgeAddress);
93
+ }
94
+ }
@@ -22,9 +22,6 @@ export async function getFirstMonitorEvent(
22
22
  reject(error);
23
23
  });
24
24
 
25
- monitor.start().catch((err: unknown) => {
26
- clearTimeout(timeout);
27
- reject(err instanceof Error ? err : new Error(String(err)));
28
- });
25
+ void monitor.start();
29
26
  });
30
27
  }
@@ -21,7 +21,7 @@ import { RebalancerContextFactory } from '../../factories/RebalancerContextFacto
21
21
  import type { ConfirmedBlockTags } from '../../interfaces/IMonitor.js';
22
22
  import type { IStrategy } from '../../interfaces/IStrategy.js';
23
23
  import type { Monitor } from '../../monitor/Monitor.js';
24
- import type { IActionTracker } from '../../tracking/index.js';
24
+ import type { IActionTracker } from '../../tracking/IActionTracker.js';
25
25
  import type { ExplorerMessage } from '../../utils/ExplorerClient.js';
26
26
  import {
27
27
  ANVIL_TEST_PRIVATE_KEY,
@@ -212,6 +212,7 @@ export class TestRebalancerBuilder {
212
212
  const contextFactory = await RebalancerContextFactory.create(
213
213
  rebalancerConfig,
214
214
  workingMultiProvider,
215
+ undefined,
215
216
  mpp,
216
217
  registry,
217
218
  this.logger,
@@ -225,18 +226,17 @@ export class TestRebalancerBuilder {
225
226
  await tracker.initialize();
226
227
  this.logger.info('ActionTracker initialized with mock explorer');
227
228
 
228
- // In execute mode, create an actual Rebalancer to enable intent creation and execution
229
- const rebalancer =
229
+ // In execute mode, create actual Rebalancers to enable intent creation and execution
230
+ const rebalancers =
230
231
  this.executionMode === 'execute'
231
- ? contextFactory.createRebalancer()
232
- : undefined;
232
+ ? (await contextFactory.createRebalancers(tracker)).rebalancers
233
+ : [];
233
234
 
234
235
  const orchestratorDeps: RebalancerOrchestratorDeps = {
235
236
  strategy,
236
- rebalancer,
237
+ rebalancers,
237
238
  actionTracker: tracker,
238
239
  inflightContextAdapter: adapter,
239
- multiProvider: workingMultiProvider,
240
240
  rebalancerConfig,
241
241
  logger: this.logger,
242
242
  };
@@ -212,7 +212,7 @@ describe('MinAmountStrategy E2E', function () {
212
212
  hyperlaneCore,
213
213
  {
214
214
  dispatchTx: rebalanceTxReceipt,
215
- messageId: actionToArbitrum.messageId,
215
+ messageId: actionToArbitrum.messageId!,
216
216
  origin: 'anvil1',
217
217
  destination: 'anvil2',
218
218
  },
@@ -236,7 +236,6 @@ describe('MinAmountStrategy E2E', function () {
236
236
  activeIntents[0].id,
237
237
  );
238
238
  expect(completedIntent!.status).to.equal('complete');
239
- expect(completedIntent!.fulfilledAmount).to.equal(70000000n);
240
239
  });
241
240
 
242
241
  it('should handle stuck transfer and propose routes to destination', async function () {
@@ -194,7 +194,7 @@ describe('WeightedStrategy E2E', function () {
194
194
  hyperlaneCore,
195
195
  {
196
196
  dispatchTx: rebalanceTxReceipt,
197
- messageId: actionToBase.messageId,
197
+ messageId: actionToBase.messageId!,
198
198
  origin: 'anvil1',
199
199
  destination: 'anvil3',
200
200
  },
@@ -218,7 +218,6 @@ describe('WeightedStrategy E2E', function () {
218
218
  activeIntents[0].id,
219
219
  );
220
220
  expect(completedIntent!.status).to.equal('complete');
221
- expect(completedIntent!.fulfilledAmount).to.equal(1000000000n);
222
221
 
223
222
  // Assert: No more in-progress actions
224
223
  const remainingActions = await context.tracker.getInProgressActions();
@@ -11,35 +11,47 @@ import {
11
11
  WarpCore,
12
12
  type WarpCoreConfig,
13
13
  } from '@hyperlane-xyz/sdk';
14
- import { objMap } from '@hyperlane-xyz/utils';
14
+ import { objMap, toWei } from '@hyperlane-xyz/utils';
15
15
 
16
+ import { LiFiBridge } from '../bridges/LiFiBridge.js';
16
17
  import { type RebalancerConfig } from '../config/RebalancerConfig.js';
17
- import { getAllBridges, getStrategyChainNames } from '../config/types.js';
18
+ import {
19
+ ExecutionType,
20
+ ExternalBridgeType,
21
+ getAllBridges,
22
+ getStrategyChainConfig,
23
+ getStrategyChainNames,
24
+ } from '../config/types.js';
25
+ import { InventoryRebalancer } from '../core/InventoryRebalancer.js';
18
26
  import { Rebalancer } from '../core/Rebalancer.js';
27
+ import { RebalancerOrchestrator } from '../core/RebalancerOrchestrator.js';
28
+ import type { ExternalBridgeRegistry } from '../interfaces/IExternalBridge.js';
19
29
  import type { IRebalancer } from '../interfaces/IRebalancer.js';
20
30
  import type { IStrategy } from '../interfaces/IStrategy.js';
21
31
  import { Metrics } from '../metrics/Metrics.js';
22
32
  import { PriceGetter } from '../metrics/PriceGetter.js';
23
- import { Monitor } from '../monitor/Monitor.js';
33
+ import { type InventoryMonitorConfig, Monitor } from '../monitor/Monitor.js';
24
34
  import { StrategyFactory } from '../strategy/StrategyFactory.js';
25
35
  import {
26
36
  ActionTracker,
27
37
  type ActionTrackerConfig,
28
- type IActionTracker,
29
- InMemoryStore,
30
- InflightContextAdapter,
31
- type RebalanceAction,
32
- type RebalanceActionStatus,
33
- type RebalanceIntent,
34
- type RebalanceIntentStatus,
35
- type Transfer,
36
- type TransferStatus,
37
- } from '../tracking/index.js';
38
+ } from '../tracking/ActionTracker.js';
39
+ import type { IActionTracker } from '../tracking/IActionTracker.js';
40
+ import { InflightContextAdapter } from '../tracking/InflightContextAdapter.js';
41
+ import { InMemoryStore } from '../tracking/store/index.js';
42
+ import type {
43
+ RebalanceAction,
44
+ RebalanceActionStatus,
45
+ RebalanceIntent,
46
+ RebalanceIntentStatus,
47
+ Transfer,
48
+ TransferStatus,
49
+ } from '../tracking/types.js';
38
50
  import {
39
51
  ExplorerClient,
40
52
  type IExplorerClient,
41
53
  } from '../utils/ExplorerClient.js';
42
- import { isCollateralizedTokenEligibleForRebalancing } from '../utils/index.js';
54
+ import { isCollateralizedTokenEligibleForRebalancing } from '../utils/tokenUtils.js';
43
55
 
44
56
  const DEFAULT_EXPLORER_URL =
45
57
  process.env.EXPLORER_API_URL || 'https://explorer4.hasura.app/v1/graphql';
@@ -49,7 +61,8 @@ export class RebalancerContextFactory {
49
61
  * @param config - The rebalancer config
50
62
  * @param warpCore - An instance of `WarpCore` configured for the specified `warpRouteId`.
51
63
  * @param tokensByChainName - A map of chain->token to ease the lookup of token by chain
52
- * @param multiProvider - MultiProvider instance
64
+ * @param multiProvider - MultiProvider instance (for movable collateral operations)
65
+ * @param inventoryMultiProvider - MultiProvider instance for inventory operations (optional)
53
66
  * @param multiProtocolProvider - MultiProtocolProvider instance (with mailbox metadata)
54
67
  * @param registry - IRegistry instance
55
68
  * @param logger - Logger instance
@@ -59,14 +72,24 @@ export class RebalancerContextFactory {
59
72
  private readonly warpCore: WarpCore,
60
73
  private readonly tokensByChainName: ChainMap<Token>,
61
74
  private readonly multiProvider: MultiProvider,
75
+ private readonly inventoryMultiProvider: MultiProvider | undefined,
62
76
  private readonly multiProtocolProvider: MultiProtocolProvider,
63
77
  private readonly registry: IRegistry,
64
78
  private readonly logger: Logger,
65
79
  ) {}
66
80
 
81
+ /**
82
+ * @param config - The rebalancer config
83
+ * @param multiProvider - MultiProvider instance (for movable collateral operations)
84
+ * @param inventoryMultiProvider - MultiProvider instance for inventory operations (optional)
85
+ * @param multiProtocolProvider - MultiProtocolProvider instance (optional, created from multiProvider if not provided)
86
+ * @param registry - IRegistry instance
87
+ * @param logger - Logger instance
88
+ */
67
89
  public static async create(
68
90
  config: RebalancerConfig,
69
91
  multiProvider: MultiProvider,
92
+ inventoryMultiProvider: MultiProvider | undefined,
70
93
  multiProtocolProvider: MultiProtocolProvider | undefined,
71
94
  registry: IRegistry,
72
95
  logger: Logger,
@@ -78,18 +101,15 @@ export class RebalancerContextFactory {
78
101
  },
79
102
  'Creating RebalancerContextFactory',
80
103
  );
104
+
105
+ // TODO: should we pull addressed for chains we care about, i.e those in the warp config
81
106
  const addresses = await registry.getAddresses();
82
107
 
83
108
  // The Sealevel warp adapters require the Mailbox address, so we
84
109
  // get mailboxes for all chains and merge them with the chain metadata.
85
110
  const mailboxes = objMap(addresses, (_, { mailbox }) => ({ mailbox }));
86
111
 
87
- // Create MultiProtocolProvider (convert from MultiProvider if not provided)
88
- const mpp =
89
- multiProtocolProvider ??
90
- MultiProtocolProvider.fromMultiProvider(multiProvider);
91
- const extendedMultiProtocolProvider = mpp.extendChainMetadata(mailboxes);
92
-
112
+ // Fetch warp route config FIRST to get chain list
93
113
  const warpCoreConfig =
94
114
  warpCoreConfigOverride ??
95
115
  (await registry.getWarpRoute(config.warpRouteId));
@@ -98,6 +118,22 @@ export class RebalancerContextFactory {
98
118
  `Warp route config for ${config.warpRouteId} not found in registry`,
99
119
  );
100
120
  }
121
+
122
+ // Force-initialize providers for all warp route chains
123
+ // This ensures fromMultiProvider() snapshots actual provider instances
124
+ const warpChains = [
125
+ ...new Set(warpCoreConfig.tokens.map((t: any) => t.chainName)),
126
+ ];
127
+ for (const chain of warpChains) {
128
+ multiProvider.getProvider(chain);
129
+ }
130
+
131
+ // Create MultiProtocolProvider (convert from MultiProvider if not provided)
132
+ const mpp =
133
+ multiProtocolProvider ??
134
+ MultiProtocolProvider.fromMultiProvider(multiProvider);
135
+ const extendedMultiProtocolProvider = mpp.extendChainMetadata(mailboxes);
136
+
101
137
  const warpCore = WarpCore.FromConfig(
102
138
  extendedMultiProtocolProvider,
103
139
  warpCoreConfig,
@@ -117,6 +153,7 @@ export class RebalancerContextFactory {
117
153
  warpCore,
118
154
  tokensByChainName,
119
155
  multiProvider,
156
+ inventoryMultiProvider,
120
157
  extendedMultiProtocolProvider,
121
158
  registry,
122
159
  logger,
@@ -154,7 +191,10 @@ export class RebalancerContextFactory {
154
191
  );
155
192
  }
156
193
 
157
- public createMonitor(checkFrequency: number): Monitor {
194
+ public createMonitor(
195
+ checkFrequency: number,
196
+ inventoryConfig?: InventoryMonitorConfig,
197
+ ): Monitor {
158
198
  this.logger.debug(
159
199
  {
160
200
  warpRouteId: this.config.warpRouteId,
@@ -162,7 +202,12 @@ export class RebalancerContextFactory {
162
202
  },
163
203
  'Creating Monitor',
164
204
  );
165
- return new Monitor(checkFrequency, this.warpCore, this.logger);
205
+ return new Monitor(
206
+ checkFrequency,
207
+ this.warpCore,
208
+ this.logger,
209
+ inventoryConfig,
210
+ );
166
211
  }
167
212
 
168
213
  public async createStrategy(metrics?: Metrics): Promise<IStrategy> {
@@ -177,16 +222,48 @@ export class RebalancerContextFactory {
177
222
  },
178
223
  'Creating Strategy',
179
224
  );
225
+
226
+ // Build minAmountsByChain from chain configs
227
+ const chainNames = getStrategyChainNames(this.config.strategyConfig);
228
+ const minAmountsByChain: ChainMap<bigint> = {};
229
+
230
+ for (const chainName of chainNames) {
231
+ const chainConfig = getStrategyChainConfig(
232
+ this.config.strategyConfig,
233
+ chainName,
234
+ );
235
+ if (chainConfig?.bridgeMinAcceptedAmount) {
236
+ const token = this.tokensByChainName[chainName];
237
+ const decimals = token?.decimals ?? 18;
238
+ minAmountsByChain[chainName] = BigInt(
239
+ toWei(chainConfig.bridgeMinAcceptedAmount, decimals),
240
+ );
241
+ }
242
+ }
243
+
244
+ this.logger.debug(
245
+ {
246
+ minAmountsByChain: Object.fromEntries(
247
+ Object.entries(minAmountsByChain).map(([k, v]) => [k, v.toString()]),
248
+ ),
249
+ },
250
+ 'Built minimum amounts by chain for strategy',
251
+ );
252
+
180
253
  return StrategyFactory.createStrategy(
181
254
  this.config.strategyConfig,
182
255
  this.tokensByChainName,
183
256
  await this.getInitialTotalCollateral(),
184
257
  this.logger,
185
258
  metrics,
259
+ minAmountsByChain,
186
260
  );
187
261
  }
188
262
 
189
- public createRebalancer(metrics?: Metrics): IRebalancer {
263
+ private createMovableCollateralRebalancer(
264
+ actionTracker: IActionTracker,
265
+ metrics?: Metrics,
266
+ ): IRebalancer {
190
267
  this.logger.debug(
191
268
  { warpRouteId: this.config.warpRouteId },
192
269
  'Creating Rebalancer',
@@ -197,6 +274,7 @@ export class RebalancerContextFactory {
197
274
  this.multiProvider.metadata,
198
275
  this.tokensByChainName,
199
276
  this.multiProvider,
277
+ actionTracker,
200
278
  this.logger,
201
279
  metrics,
202
280
  );
@@ -272,6 +350,7 @@ export class RebalancerContextFactory {
272
350
  routersByDomain,
273
351
  bridges,
274
352
  rebalancerAddress,
353
+ inventorySignerAddress: this.config.inventorySigner,
275
354
  };
276
355
 
277
356
  // 6. Create ActionTracker
@@ -301,6 +380,196 @@ export class RebalancerContextFactory {
301
380
  return { tracker, adapter };
302
381
  }
303
382
 
383
+ /**
384
+ * Creates inventory components for inventory-based rebalancing.
385
+ * Returns null if inventory config is not available.
386
+ *
387
+ * @param actionTracker - ActionTracker instance for tracking inventory actions
388
+ */
389
+ private async createInventoryRebalancerAndConfig(
390
+ actionTracker: IActionTracker,
391
+ ): Promise<{
392
+ inventoryRebalancer: IRebalancer;
393
+ externalBridgeRegistry: Partial<ExternalBridgeRegistry>;
394
+ inventoryConfig: InventoryMonitorConfig;
395
+ } | null> {
396
+ const { inventorySigner, externalBridges } = this.config;
397
+
398
+ if (!inventorySigner) {
399
+ this.logger.debug(
400
+ 'Inventory config not available, skipping inventory components creation',
401
+ );
402
+ return null;
403
+ }
404
+
405
+ this.logger.debug(
406
+ { warpRouteId: this.config.warpRouteId, inventorySigner },
407
+ 'Creating inventory components',
408
+ );
409
+
410
+ const inventoryChains = getStrategyChainNames(
411
+ this.config.strategyConfig,
412
+ ).filter((chainName) => {
413
+ const chainConfig = getStrategyChainConfig(
414
+ this.config.strategyConfig,
415
+ chainName,
416
+ );
417
+ return chainConfig?.executionType === ExecutionType.Inventory;
418
+ });
419
+
420
+ if (inventoryChains.length === 0) {
421
+ this.logger.debug('No inventory chains configured');
422
+ return null;
423
+ }
424
+
425
+ // Build registry dynamically from ExternalBridgeType enum
426
+ const externalBridgeRegistry: Partial<ExternalBridgeRegistry> = {};
427
+
428
+ for (const bridgeType of Object.values(ExternalBridgeType)) {
429
+ switch (bridgeType) {
430
+ case ExternalBridgeType.LiFi: {
431
+ const lifiConfig = externalBridges?.lifi;
432
+ if (lifiConfig?.integrator) {
433
+ externalBridgeRegistry[ExternalBridgeType.LiFi] = new LiFiBridge(
434
+ {
435
+ integrator: lifiConfig.integrator,
436
+ defaultSlippage: lifiConfig.defaultSlippage,
437
+ chainMetadata: this.multiProvider.metadata,
438
+ },
439
+ this.logger,
440
+ );
441
+ }
442
+ break;
443
+ }
444
+ default: {
445
+ // Exhaustive check - TypeScript will error if new enum value added
446
+ const _exhaustive: never = bridgeType;
447
+ throw new Error(`Unknown bridge type: ${_exhaustive}`);
448
+ }
449
+ }
450
+ }
451
+
452
+ if (Object.keys(externalBridgeRegistry).length === 0) {
453
+ this.logger.debug(
454
+ 'No external bridges configured, skipping inventory components',
455
+ );
456
+ return null;
457
+ }
458
+
459
+ // 3. Build inventory config
460
+ const inventoryConfig: InventoryMonitorConfig = {
461
+ inventoryAddress: inventorySigner,
462
+ chains: inventoryChains,
463
+ };
464
+
465
+ // 4. Create InventoryRebalancer
466
+ // Use inventoryMultiProvider for inventory operations if available, otherwise fall back to multiProvider
467
+ const inventoryRebalancer = new InventoryRebalancer(
468
+ {
469
+ inventorySigner,
470
+ inventoryMultiProvider: this.inventoryMultiProvider,
471
+ inventoryChains,
472
+ },
473
+ actionTracker,
474
+ externalBridgeRegistry,
475
+ this.warpCore,
476
+ this.multiProvider,
477
+ this.logger,
478
+ );
479
+
480
+ this.logger.info(
481
+ {
482
+ inventoryChains,
483
+ inventorySigner,
484
+ },
485
+ 'Inventory components created successfully',
486
+ );
487
+
488
+ return { inventoryRebalancer, externalBridgeRegistry, inventoryConfig };
489
+ }
490
+
491
+ /**
492
+ * Creates all rebalancers based on config execution types.
493
+ * Returns an array of rebalancers (movableCollateral and/or inventory)
494
+ * along with metadata needed for monitor and orchestrator.
495
+ */
496
+ public async createRebalancers(
497
+ actionTracker: IActionTracker,
498
+ metrics?: Metrics,
499
+ ): Promise<{
500
+ rebalancers: IRebalancer[];
501
+ externalBridgeRegistry: Partial<ExternalBridgeRegistry>;
502
+ inventoryConfig?: InventoryMonitorConfig;
503
+ }> {
504
+ const rebalancers: IRebalancer[] = [];
505
+ let externalBridgeRegistry: Partial<ExternalBridgeRegistry> = {};
506
+ let inventoryConfig: InventoryMonitorConfig | undefined;
507
+
508
+ // Check if any chains use movableCollateral execution type
509
+ const hasMovableCollateral = this.hasMovableCollateralChains();
510
+ if (hasMovableCollateral) {
511
+ const rebalancer = this.createMovableCollateralRebalancer(
512
+ actionTracker,
513
+ metrics,
514
+ );
515
+ rebalancers.push(rebalancer);
516
+ }
517
+
518
+ // Check if any chains use inventory execution type
519
+ const inventoryComponents =
520
+ await this.createInventoryRebalancerAndConfig(actionTracker);
521
+ if (inventoryComponents) {
522
+ rebalancers.push(inventoryComponents.inventoryRebalancer);
523
+ externalBridgeRegistry = inventoryComponents.externalBridgeRegistry;
524
+ inventoryConfig = inventoryComponents.inventoryConfig;
525
+ }
526
+
527
+ return { rebalancers, externalBridgeRegistry, inventoryConfig };
528
+ }
529
+
530
+ /**
531
+ * Creates a RebalancerOrchestrator with all required dependencies.
532
+ */
533
+ public createOrchestrator(options: {
534
+ strategy: IStrategy;
535
+ actionTracker: IActionTracker;
536
+ inflightContextAdapter: InflightContextAdapter;
537
+ rebalancers: IRebalancer[];
538
+ externalBridgeRegistry: Partial<ExternalBridgeRegistry>;
539
+ metrics?: Metrics;
540
+ }): RebalancerOrchestrator {
541
+ this.logger.debug(
542
+ { warpRouteId: this.config.warpRouteId },
543
+ 'Creating RebalancerOrchestrator',
544
+ );
545
+
546
+ return new RebalancerOrchestrator({
547
+ strategy: options.strategy,
548
+ actionTracker: options.actionTracker,
549
+ inflightContextAdapter: options.inflightContextAdapter,
550
+ rebalancerConfig: this.config,
551
+ logger: this.logger,
552
+ rebalancers: options.rebalancers,
553
+ externalBridgeRegistry: options.externalBridgeRegistry,
554
+ metrics: options.metrics,
555
+ });
556
+ }
557
+
558
+ private hasMovableCollateralChains(): boolean {
559
+ return getStrategyChainNames(this.config.strategyConfig).some(
560
+ (chainName) => {
561
+ const chainConfig = getStrategyChainConfig(
562
+ this.config.strategyConfig,
563
+ chainName,
564
+ );
565
+ return (
566
+ chainConfig?.executionType === ExecutionType.MovableCollateral ||
567
+ chainConfig?.executionType === undefined
568
+ ); // Default is movableCollateral
569
+ },
570
+ );
571
+ }
572
+
304
573
  private async getInitialTotalCollateral(): Promise<bigint> {
305
574
  let initialTotalCollateral = 0n;
306
575
 
package/src/index.ts CHANGED
@@ -57,16 +57,23 @@ export { PriceGetter } from './metrics/PriceGetter.js';
57
57
 
58
58
  // Interfaces
59
59
  export type {
60
+ ExecutionResult,
61
+ IInventoryRebalancer,
62
+ IMovableCollateralRebalancer,
60
63
  IRebalancer,
64
+ InventoryExecutionResult,
65
+ MovableCollateralExecutionResult,
61
66
  PreparedTransaction,
62
- RebalanceRoute,
63
- RebalanceExecutionResult,
67
+ RebalancerType,
64
68
  } from './interfaces/IRebalancer.js';
65
69
  export type {
66
70
  IStrategy,
67
- StrategyRoute,
68
- RawBalances,
69
71
  InflightContext,
72
+ InventoryRoute,
73
+ MovableCollateralRoute,
74
+ RawBalances,
75
+ Route,
76
+ StrategyRoute,
70
77
  } from './interfaces/IStrategy.js';
71
78
  export type {
72
79
  ConfirmedBlockTag,
@@ -82,10 +89,16 @@ export {
82
89
  export type { IMetrics } from './interfaces/IMetrics.js';
83
90
 
84
91
  // Utils
85
- export { getBridgeConfig } from './utils/bridgeUtils.js';
92
+ export {
93
+ getBridgeConfig,
94
+ isMovableCollateralConfig,
95
+ isInventoryConfig,
96
+ } from './utils/bridgeUtils.js';
86
97
  export type {
87
98
  BridgeConfigWithOverride,
88
99
  BridgeConfig,
100
+ MovableCollateralBridgeConfig,
101
+ InventoryBridgeConfig,
89
102
  } from './utils/bridgeUtils.js';
90
103
  export { getRawBalances } from './utils/balanceUtils.js';
91
104
  export { isCollateralizedTokenEligibleForRebalancing } from './utils/tokenUtils.js';
@@ -102,6 +115,8 @@ export type {
102
115
  Transfer,
103
116
  RebalanceIntent,
104
117
  RebalanceAction,
118
+ ActionType,
119
+ PartialInventoryIntent,
105
120
  } from './tracking/types.js';
106
121
 
107
122
  // Factory