@hyperlane-xyz/rebalancer 2.0.0 → 3.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 (213) 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 +8 -2
  6. package/dist/config/RebalancerConfig.d.ts.map +1 -1
  7. package/dist/config/RebalancerConfig.js +9 -4
  8. package/dist/config/RebalancerConfig.js.map +1 -1
  9. package/dist/config/RebalancerConfig.test.js +135 -1
  10. package/dist/config/RebalancerConfig.test.js.map +1 -1
  11. package/dist/config/types.d.ts +1023 -304
  12. package/dist/config/types.d.ts.map +1 -1
  13. package/dist/config/types.js +113 -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 +892 -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 +1382 -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 +719 -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 +74 -110
  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/ForkIndexer.d.ts.map +1 -1
  51. package/dist/e2e/harness/ForkIndexer.js +1 -0
  52. package/dist/e2e/harness/ForkIndexer.js.map +1 -1
  53. package/dist/e2e/harness/TestHelpers.d.ts.map +1 -1
  54. package/dist/e2e/harness/TestHelpers.js +1 -4
  55. package/dist/e2e/harness/TestHelpers.js.map +1 -1
  56. package/dist/e2e/harness/TestRebalancer.d.ts +1 -1
  57. package/dist/e2e/harness/TestRebalancer.d.ts.map +1 -1
  58. package/dist/e2e/harness/TestRebalancer.js +9 -9
  59. package/dist/e2e/harness/TestRebalancer.js.map +1 -1
  60. package/dist/e2e/minAmount.e2e-test.js +0 -1
  61. package/dist/e2e/minAmount.e2e-test.js.map +1 -1
  62. package/dist/e2e/weighted.e2e-test.js +0 -1
  63. package/dist/e2e/weighted.e2e-test.js.map +1 -1
  64. package/dist/factories/RebalancerContextFactory.d.ts +48 -6
  65. package/dist/factories/RebalancerContextFactory.d.ts.map +1 -1
  66. package/dist/factories/RebalancerContextFactory.js +171 -17
  67. package/dist/factories/RebalancerContextFactory.js.map +1 -1
  68. package/dist/index.d.ts +6 -6
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +2 -2
  71. package/dist/index.js.map +1 -1
  72. package/dist/interfaces/IExternalBridge.d.ts +101 -0
  73. package/dist/interfaces/IExternalBridge.d.ts.map +1 -0
  74. package/dist/interfaces/IExternalBridge.js +2 -0
  75. package/dist/interfaces/IExternalBridge.js.map +1 -0
  76. package/dist/interfaces/IMonitor.d.ts +1 -0
  77. package/dist/interfaces/IMonitor.d.ts.map +1 -1
  78. package/dist/interfaces/IRebalancer.d.ts +25 -25
  79. package/dist/interfaces/IRebalancer.d.ts.map +1 -1
  80. package/dist/interfaces/IStrategy.d.ts +36 -3
  81. package/dist/interfaces/IStrategy.d.ts.map +1 -1
  82. package/dist/interfaces/IStrategy.js +12 -1
  83. package/dist/interfaces/IStrategy.js.map +1 -1
  84. package/dist/metrics/PriceGetter.js +1 -1
  85. package/dist/metrics/PriceGetter.js.map +1 -1
  86. package/dist/metrics/scripts/metrics.d.ts +3 -3
  87. package/dist/monitor/Monitor.d.ts +12 -2
  88. package/dist/monitor/Monitor.d.ts.map +1 -1
  89. package/dist/monitor/Monitor.js +46 -1
  90. package/dist/monitor/Monitor.js.map +1 -1
  91. package/dist/service.js +40 -17
  92. package/dist/service.js.map +1 -1
  93. package/dist/strategy/BaseStrategy.d.ts +12 -6
  94. package/dist/strategy/BaseStrategy.d.ts.map +1 -1
  95. package/dist/strategy/BaseStrategy.js +56 -21
  96. package/dist/strategy/BaseStrategy.js.map +1 -1
  97. package/dist/strategy/CollateralDeficitStrategy.d.ts +1 -1
  98. package/dist/strategy/CollateralDeficitStrategy.d.ts.map +1 -1
  99. package/dist/strategy/CollateralDeficitStrategy.js +19 -11
  100. package/dist/strategy/CollateralDeficitStrategy.js.map +1 -1
  101. package/dist/strategy/CollateralDeficitStrategy.test.js +135 -2
  102. package/dist/strategy/CollateralDeficitStrategy.test.js.map +1 -1
  103. package/dist/strategy/CompositeStrategy.test.js +13 -0
  104. package/dist/strategy/CompositeStrategy.test.js.map +1 -1
  105. package/dist/strategy/MinAmountStrategy.test.js +4 -0
  106. package/dist/strategy/MinAmountStrategy.test.js.map +1 -1
  107. package/dist/strategy/StrategyFactory.d.ts +2 -1
  108. package/dist/strategy/StrategyFactory.d.ts.map +1 -1
  109. package/dist/strategy/StrategyFactory.js +24 -8
  110. package/dist/strategy/StrategyFactory.js.map +1 -1
  111. package/dist/strategy/WeightedStrategy.test.js +6 -0
  112. package/dist/strategy/WeightedStrategy.test.js.map +1 -1
  113. package/dist/test/helpers.d.ts +8 -7
  114. package/dist/test/helpers.d.ts.map +1 -1
  115. package/dist/test/helpers.js +23 -5
  116. package/dist/test/helpers.js.map +1 -1
  117. package/dist/test/lifiMocks.d.ts +51 -0
  118. package/dist/test/lifiMocks.d.ts.map +1 -0
  119. package/dist/test/lifiMocks.js +130 -0
  120. package/dist/test/lifiMocks.js.map +1 -0
  121. package/dist/tracking/ActionTracker.d.ts +34 -1
  122. package/dist/tracking/ActionTracker.d.ts.map +1 -1
  123. package/dist/tracking/ActionTracker.js +233 -26
  124. package/dist/tracking/ActionTracker.js.map +1 -1
  125. package/dist/tracking/ActionTracker.test.js +380 -19
  126. package/dist/tracking/ActionTracker.test.js.map +1 -1
  127. package/dist/tracking/IActionTracker.d.ts +48 -3
  128. package/dist/tracking/IActionTracker.d.ts.map +1 -1
  129. package/dist/tracking/InflightContextAdapter.d.ts.map +1 -1
  130. package/dist/tracking/InflightContextAdapter.js +24 -7
  131. package/dist/tracking/InflightContextAdapter.js.map +1 -1
  132. package/dist/tracking/InflightContextAdapter.test.js +7 -4
  133. package/dist/tracking/InflightContextAdapter.test.js.map +1 -1
  134. package/dist/tracking/types.d.ts +33 -2
  135. package/dist/tracking/types.d.ts.map +1 -1
  136. package/dist/utils/ExplorerClient.d.ts +3 -1
  137. package/dist/utils/ExplorerClient.d.ts.map +1 -1
  138. package/dist/utils/ExplorerClient.js +16 -8
  139. package/dist/utils/ExplorerClient.js.map +1 -1
  140. package/dist/utils/bridgeUtils.d.ts +27 -4
  141. package/dist/utils/bridgeUtils.d.ts.map +1 -1
  142. package/dist/utils/bridgeUtils.js +38 -0
  143. package/dist/utils/bridgeUtils.js.map +1 -1
  144. package/dist/utils/bridgeUtils.test.js +9 -0
  145. package/dist/utils/bridgeUtils.test.js.map +1 -1
  146. package/dist/utils/gasEstimation.d.ts +65 -0
  147. package/dist/utils/gasEstimation.d.ts.map +1 -0
  148. package/dist/utils/gasEstimation.js +176 -0
  149. package/dist/utils/gasEstimation.js.map +1 -0
  150. package/dist/utils/tokenUtils.d.ts +9 -1
  151. package/dist/utils/tokenUtils.d.ts.map +1 -1
  152. package/dist/utils/tokenUtils.js +11 -0
  153. package/dist/utils/tokenUtils.js.map +1 -1
  154. package/package.json +9 -7
  155. package/src/bridges/LiFiBridge.ts +538 -0
  156. package/src/config/RebalancerConfig.test.ts +162 -0
  157. package/src/config/RebalancerConfig.ts +21 -3
  158. package/src/config/types.ts +147 -10
  159. package/src/core/InventoryRebalancer.test.ts +1721 -0
  160. package/src/core/InventoryRebalancer.ts +1265 -0
  161. package/src/core/Rebalancer.test.ts +84 -30
  162. package/src/core/Rebalancer.ts +144 -23
  163. package/src/core/RebalancerOrchestrator.test.ts +869 -0
  164. package/src/core/RebalancerOrchestrator.ts +146 -95
  165. package/src/core/RebalancerService.test.ts +86 -124
  166. package/src/core/RebalancerService.ts +67 -33
  167. package/src/e2e/collateral-deficit.e2e-test.ts +2 -4
  168. package/src/e2e/composite.e2e-test.ts +5 -5
  169. package/src/e2e/harness/BridgeSetup.ts +28 -1
  170. package/src/e2e/harness/ForkIndexer.ts +1 -0
  171. package/src/e2e/harness/TestHelpers.ts +1 -4
  172. package/src/e2e/harness/TestRebalancer.ts +10 -7
  173. package/src/e2e/minAmount.e2e-test.ts +1 -2
  174. package/src/e2e/weighted.e2e-test.ts +1 -2
  175. package/src/factories/RebalancerContextFactory.ts +294 -24
  176. package/src/index.ts +22 -5
  177. package/src/interfaces/IExternalBridge.ts +115 -0
  178. package/src/interfaces/IMonitor.ts +1 -0
  179. package/src/interfaces/IRebalancer.ts +45 -29
  180. package/src/interfaces/IStrategy.ts +50 -3
  181. package/src/metrics/PriceGetter.ts +1 -1
  182. package/src/monitor/Monitor.ts +81 -2
  183. package/src/service.ts +59 -18
  184. package/src/strategy/BaseStrategy.ts +77 -24
  185. package/src/strategy/CollateralDeficitStrategy.test.ts +181 -4
  186. package/src/strategy/CollateralDeficitStrategy.ts +42 -15
  187. package/src/strategy/CompositeStrategy.test.ts +13 -0
  188. package/src/strategy/MinAmountStrategy.test.ts +4 -0
  189. package/src/strategy/StrategyFactory.ts +33 -6
  190. package/src/strategy/WeightedStrategy.test.ts +6 -0
  191. package/src/test/helpers.ts +39 -14
  192. package/src/test/lifiMocks.ts +174 -0
  193. package/src/tracking/ActionTracker.test.ts +443 -19
  194. package/src/tracking/ActionTracker.ts +339 -28
  195. package/src/tracking/IActionTracker.ts +59 -3
  196. package/src/tracking/InflightContextAdapter.test.ts +7 -4
  197. package/src/tracking/InflightContextAdapter.ts +42 -9
  198. package/src/tracking/types.ts +45 -2
  199. package/src/utils/ExplorerClient.ts +27 -10
  200. package/src/utils/bridgeUtils.test.ts +9 -0
  201. package/src/utils/bridgeUtils.ts +75 -6
  202. package/src/utils/gasEstimation.ts +272 -0
  203. package/src/utils/tokenUtils.ts +12 -0
  204. package/dist/tracking/index.d.ts +0 -7
  205. package/dist/tracking/index.d.ts.map +0 -1
  206. package/dist/tracking/index.js +0 -6
  207. package/dist/tracking/index.js.map +0 -1
  208. package/dist/utils/index.d.ts +0 -5
  209. package/dist/utils/index.d.ts.map +0 -1
  210. package/dist/utils/index.js +0 -5
  211. package/dist/utils/index.js.map +0 -1
  212. package/src/tracking/index.ts +0 -36
  213. package/src/utils/index.ts +0 -4
package/src/index.ts CHANGED
@@ -20,6 +20,8 @@ export { Rebalancer } from './core/Rebalancer.js';
20
20
  // Configuration
21
21
  export { RebalancerConfig } from './config/RebalancerConfig.js';
22
22
  export {
23
+ DEFAULT_INTENT_TTL_MS,
24
+ DEFAULT_INTENT_TTL_S,
23
25
  getStrategyChainConfig,
24
26
  getStrategyChainNames,
25
27
  RebalancerBaseChainConfigSchema,
@@ -57,16 +59,23 @@ export { PriceGetter } from './metrics/PriceGetter.js';
57
59
 
58
60
  // Interfaces
59
61
  export type {
62
+ ExecutionResult,
63
+ IInventoryRebalancer,
64
+ IMovableCollateralRebalancer,
60
65
  IRebalancer,
66
+ InventoryExecutionResult,
67
+ MovableCollateralExecutionResult,
61
68
  PreparedTransaction,
62
- RebalanceRoute,
63
- RebalanceExecutionResult,
69
+ RebalancerType,
64
70
  } from './interfaces/IRebalancer.js';
65
71
  export type {
66
72
  IStrategy,
67
- StrategyRoute,
68
- RawBalances,
69
73
  InflightContext,
74
+ InventoryRoute,
75
+ MovableCollateralRoute,
76
+ RawBalances,
77
+ Route,
78
+ StrategyRoute,
70
79
  } from './interfaces/IStrategy.js';
71
80
  export type {
72
81
  ConfirmedBlockTag,
@@ -82,10 +91,16 @@ export {
82
91
  export type { IMetrics } from './interfaces/IMetrics.js';
83
92
 
84
93
  // Utils
85
- export { getBridgeConfig } from './utils/bridgeUtils.js';
94
+ export {
95
+ getBridgeConfig,
96
+ isMovableCollateralConfig,
97
+ isInventoryConfig,
98
+ } from './utils/bridgeUtils.js';
86
99
  export type {
87
100
  BridgeConfigWithOverride,
88
101
  BridgeConfig,
102
+ MovableCollateralBridgeConfig,
103
+ InventoryBridgeConfig,
89
104
  } from './utils/bridgeUtils.js';
90
105
  export { getRawBalances } from './utils/balanceUtils.js';
91
106
  export { isCollateralizedTokenEligibleForRebalancing } from './utils/tokenUtils.js';
@@ -102,6 +117,8 @@ export type {
102
117
  Transfer,
103
118
  RebalanceIntent,
104
119
  RebalanceAction,
120
+ ActionType,
121
+ PartialInventoryIntent,
105
122
  } from './tracking/types.js';
106
123
 
107
124
  // Factory
@@ -0,0 +1,115 @@
1
+ import type { Logger } from 'pino';
2
+
3
+ import type { ChainMap, ChainMetadata } from '@hyperlane-xyz/sdk';
4
+
5
+ import type { ExternalBridgeType } from '../config/types.js';
6
+
7
+ /**
8
+ * Configuration for an external bridge.
9
+ */
10
+ export interface ExternalBridgeConfig {
11
+ integrator: string; // Required: dApp/company name for bridge integration
12
+ apiKey?: string; // Optional: API key for higher rate limits
13
+ defaultSlippage?: number; // Default slippage tolerance (e.g., 0.005 = 0.5%)
14
+ chainMetadata?: ChainMap<ChainMetadata>; // Optional: chain metadata for resolving RPC URLs by chainId
15
+ }
16
+
17
+ /**
18
+ * Parameters for requesting a bridge quote.
19
+ * Either fromAmount OR toAmount must be provided (mutually exclusive).
20
+ * - fromAmount: "I'm sending X, what do I get?" (standard quote)
21
+ * - toAmount: "I want X on destination, how much do I send?" (reverse quote)
22
+ */
23
+ export interface BridgeQuoteParams {
24
+ fromChain: number; // Source chain ID
25
+ toChain: number; // Destination chain ID
26
+ fromToken: string; // Source token address
27
+ toToken: string; // Destination token address
28
+ fromAmount?: bigint; // Amount to send (in token decimals) - use for "I'm sending X"
29
+ toAmount?: bigint; // Amount to receive (in token decimals) - use for "I want X"
30
+ fromAddress: string; // Sender address
31
+ toAddress?: string; // Recipient address (defaults to fromAddress)
32
+ slippage?: number; // Slippage tolerance (overrides default)
33
+ }
34
+
35
+ /**
36
+ * Quote response from a bridge.
37
+ */
38
+ export interface BridgeQuote {
39
+ id: string; // Unique quote identifier
40
+ tool: string; // Bridge/DEX tool used (e.g., 'stargate', 'across')
41
+ fromAmount: bigint; // Amount being sent (input required)
42
+ toAmount: bigint; // Expected amount to receive
43
+ toAmountMin: bigint; // Minimum amount to receive (after slippage)
44
+ executionDuration: number; // Estimated execution time in seconds
45
+ gasCosts: bigint; // Sum of gas costs for the bridge operation
46
+ feeCosts: bigint; // Sum of non-included fee costs (protocol fees, etc.)
47
+ route: unknown; // Bridge-specific route data for execution
48
+ }
49
+
50
+ /**
51
+ * Result of executing a bridge transfer.
52
+ */
53
+ export interface BridgeTransferResult {
54
+ txHash: string; // Origin chain transaction hash
55
+ fromChain: number; // Source chain ID
56
+ toChain: number; // Destination chain ID
57
+ transferId?: string; // Bridge-specific transfer identifier
58
+ }
59
+
60
+ /**
61
+ * Status of a bridge transfer.
62
+ */
63
+ export type BridgeTransferStatus =
64
+ | { status: 'pending'; substatus?: string }
65
+ | { status: 'complete'; receivingTxHash: string; receivedAmount: bigint }
66
+ | { status: 'failed'; error?: string }
67
+ | { status: 'not_found' };
68
+
69
+ /**
70
+ * Interface for external bridge implementations (e.g., LiFi, Socket).
71
+ *
72
+ * External bridges are used for inventory rebalancing when chains don't support
73
+ * MovableCollateralRouter. The flow is:
74
+ * 1. quote() - Get a quote for bridging tokens
75
+ * 2. execute() - Execute the bridge transfer
76
+ * 3. getStatus() - Poll for transfer completion
77
+ */
78
+ export interface IExternalBridge {
79
+ readonly externalBridgeId: string;
80
+ readonly logger: Logger;
81
+
82
+ getNativeTokenAddress?(): string;
83
+
84
+ quote(params: BridgeQuoteParams): Promise<BridgeQuote>;
85
+
86
+ /**
87
+ * Execute a bridge transfer using a previously obtained quote.
88
+ * @param quote - Quote obtained from quote()
89
+ * @param privateKey - Private key hex string (0x-prefixed) for signing the transaction
90
+ */
91
+ execute(
92
+ quote: BridgeQuote,
93
+ privateKey: string,
94
+ ): Promise<BridgeTransferResult>;
95
+
96
+ /**
97
+ * Get the status of a bridge transfer.
98
+ * @param txHash - Origin chain transaction hash
99
+ * @param fromChain - Source chain ID
100
+ * @param toChain - Destination chain ID
101
+ */
102
+ getStatus(
103
+ txHash: string,
104
+ fromChain: number,
105
+ toChain: number,
106
+ ): Promise<BridgeTransferStatus>;
107
+ }
108
+
109
+ /**
110
+ * Registry mapping external bridge types to their implementations.
111
+ */
112
+ export type ExternalBridgeRegistry = Record<
113
+ ExternalBridgeType,
114
+ IExternalBridge
115
+ >;
@@ -33,6 +33,7 @@ export type MonitorEvent = {
33
33
  bridgedSupply?: bigint;
34
34
  }[];
35
35
  confirmedBlockTags: ConfirmedBlockTags;
36
+ inventoryBalances?: ChainMap<bigint>;
36
37
  };
37
38
 
38
39
  /**
@@ -3,39 +3,55 @@ import {
3
3
  type TokenAmount,
4
4
  } from '@hyperlane-xyz/sdk';
5
5
 
6
- import type { StrategyRoute } from './IStrategy.js';
7
-
8
- /**
9
- * RebalanceRoute extends StrategyRoute with a required intentId for tracking.
10
- * The intentId is assigned by RebalancerService before execution and links
11
- * to the corresponding RebalanceIntent in the tracking system.
12
- */
13
- export type RebalanceRoute = StrategyRoute & {
14
- /** Links to the RebalanceIntent that this route fulfills */
15
- intentId: string;
16
- };
6
+ import {
7
+ type InventoryRoute,
8
+ type MovableCollateralRoute,
9
+ type Route,
10
+ } from './IStrategy.js';
11
+
12
+ export type RebalancerType = 'movableCollateral' | 'inventory';
13
+
14
+ export interface ExecutionResult<R extends Route = Route> {
15
+ route: R;
16
+ success: boolean;
17
+ error?: string;
18
+ // messageId?: string;
19
+ txHash?: string;
20
+ // amountSent?: bigint;
21
+ reason?: string;
22
+ }
23
+
24
+ export interface MovableCollateralExecutionResult extends ExecutionResult<MovableCollateralRoute> {
25
+ messageId: string;
26
+ }
27
+
28
+ export interface InventoryExecutionResult extends ExecutionResult<InventoryRoute> {
29
+ messageId?: string;
30
+ amountSent?: bigint;
31
+ }
32
+
33
+ export interface IRebalancer<
34
+ R extends Route = Route,
35
+ E extends ExecutionResult<R> = ExecutionResult<R>,
36
+ > {
37
+ readonly rebalancerType: RebalancerType;
38
+ rebalance(routes: R[]): Promise<E[]>;
39
+ }
40
+
41
+ export type IMovableCollateralRebalancer = IRebalancer<
42
+ MovableCollateralRoute,
43
+ MovableCollateralExecutionResult
44
+ >;
45
+
46
+ export type IInventoryRebalancer = IRebalancer<
47
+ InventoryRoute,
48
+ InventoryExecutionResult
49
+ >;
17
50
 
18
51
  export type PreparedTransaction = {
19
52
  populatedTx: Awaited<
20
53
  ReturnType<EvmMovableCollateralAdapter['populateRebalanceTx']>
21
54
  >;
22
- route: RebalanceRoute;
55
+ route: MovableCollateralRoute & { intentId: string };
23
56
  originTokenAmount: TokenAmount;
24
57
  };
25
-
26
- export type RebalanceMetrics = {
27
- route: RebalanceRoute;
28
- originTokenAmount: TokenAmount;
29
- };
30
-
31
- export type RebalanceExecutionResult = {
32
- route: RebalanceRoute;
33
- success: boolean;
34
- messageId?: string;
35
- txHash?: string;
36
- error?: string;
37
- };
38
-
39
- export interface IRebalancer {
40
- rebalance(routes: RebalanceRoute[]): Promise<RebalanceExecutionResult[]>;
41
- }
@@ -1,26 +1,73 @@
1
1
  import { type ChainMap, type ChainName } from '@hyperlane-xyz/sdk';
2
2
  import type { Address } from '@hyperlane-xyz/utils';
3
3
 
4
+ import { ExternalBridgeType } from '../config/types.js';
5
+ import { ExecutionMethod } from '../tracking/types.js';
6
+
4
7
  export type RawBalances = ChainMap<bigint>;
5
8
 
6
9
  export interface Route {
7
10
  origin: ChainName;
8
11
  destination: ChainName;
9
12
  amount: bigint;
13
+ /** Execution method used for this rebalance */
14
+ executionMethod?: ExecutionMethod;
15
+ }
16
+
17
+ export interface RouteWithContext extends Route {
18
+ /** For inventory intents: sum of complete inventory_deposit actions (message delivered) */
19
+ deliveredAmount?: bigint;
20
+ /** For inventory intents: sum of in_progress inventory_deposit actions (tx confirmed, message pending) */
21
+ awaitingDeliveryAmount?: bigint;
10
22
  }
11
23
 
12
- export interface StrategyRoute extends Route {
24
+ /**
25
+ * Route using movable collateral execution (on-chain rebalance via bridge)
26
+ */
27
+ export interface MovableCollateralRoute extends Route {
28
+ executionType: 'movableCollateral';
13
29
  bridge: Address;
14
30
  }
15
31
 
32
+ /**
33
+ * Route using inventory execution (external bridge + transferRemote)
34
+ */
35
+ export interface InventoryRoute extends Route {
36
+ executionType: 'inventory';
37
+ externalBridge: ExternalBridgeType;
38
+ }
39
+
40
+ /**
41
+ * Discriminated union of route types by executionType
42
+ */
43
+ export type StrategyRoute = MovableCollateralRoute | InventoryRoute;
44
+
45
+ /**
46
+ * Type guard to check if a route is a MovableCollateralRoute
47
+ */
48
+ export function isMovableCollateralRoute(
49
+ route: StrategyRoute,
50
+ ): route is MovableCollateralRoute {
51
+ return route.executionType === 'movableCollateral';
52
+ }
53
+
54
+ /**
55
+ * Type guard to check if a route is an InventoryRoute
56
+ */
57
+ export function isInventoryRoute(
58
+ route: StrategyRoute,
59
+ ): route is InventoryRoute {
60
+ return route.executionType === 'inventory';
61
+ }
62
+
16
63
  export type InflightContext = {
17
64
  /**
18
65
  * In-flight rebalances from ActionTracker.
19
66
  * Uses Route[] because recovered intents (from Explorer startup recovery)
20
67
  * don't have bridge information. Some routes may have bridge at runtime.
21
68
  */
22
- pendingRebalances: Route[];
23
- pendingTransfers: Route[];
69
+ pendingRebalances: RouteWithContext[];
70
+ pendingTransfers: RouteWithContext[];
24
71
  /** Routes from earlier strategies - always have bridge */
25
72
  proposedRebalances?: StrategyRoute[];
26
73
  };
@@ -52,7 +52,7 @@ export class PriceGetter extends CoinGeckoTokenPriceGetter {
52
52
  const coinGeckoId = token.coinGeckoId;
53
53
 
54
54
  if (!coinGeckoId) {
55
- this.logger.warn(
55
+ this.logger.debug(
56
56
  {
57
57
  tokenSymbol: token.symbol,
58
58
  chain: token.chainName,
@@ -1,7 +1,12 @@
1
1
  import { type Logger } from 'pino';
2
2
 
3
- import { type Token, type WarpCore } from '@hyperlane-xyz/sdk';
4
- import { sleep } from '@hyperlane-xyz/utils';
3
+ import {
4
+ type ChainMap,
5
+ type ChainName,
6
+ type Token,
7
+ type WarpCore,
8
+ } from '@hyperlane-xyz/sdk';
9
+ import { Address, sleep } from '@hyperlane-xyz/utils';
5
10
 
6
11
  import {
7
12
  type ConfirmedBlockTag,
@@ -14,6 +19,14 @@ import {
14
19
  } from '../interfaces/IMonitor.js';
15
20
  import { getConfirmedBlockTag } from '../utils/blockTag.js';
16
21
 
22
+ /**
23
+ * Configuration for the Monitor's inventory tracking.
24
+ */
25
+ export interface InventoryMonitorConfig {
26
+ inventoryAddress: Address;
27
+ chains: ChainName[];
28
+ }
29
+
17
30
  /**
18
31
  * Simple monitor implementation that polls warp route collateral balances and emits them as MonitorEvent.
19
32
  * Awaits the TokenInfo handler before starting the next cycle to prevent race conditions.
@@ -33,6 +46,7 @@ export class Monitor implements IMonitor {
33
46
  private readonly checkFrequency: number,
34
47
  private readonly warpCore: WarpCore,
35
48
  private readonly logger: Logger,
49
+ private readonly inventoryConfig?: InventoryMonitorConfig,
36
50
  ) {}
37
51
 
38
52
  private async computeConfirmedBlockTags(): Promise<ConfirmedBlockTags> {
@@ -123,6 +137,24 @@ export class Monitor implements IMonitor {
123
137
  });
124
138
  }
125
139
 
140
+ const inventoryBalances = await this.fetchInventoryBalances();
141
+ if (Object.keys(inventoryBalances).length > 0) {
142
+ event.inventoryBalances = inventoryBalances;
143
+ this.logger.info(
144
+ {
145
+ chainsMonitored: Object.keys(inventoryBalances).length,
146
+ balances: Object.entries(inventoryBalances).map(
147
+ ([chain, balance]) => ({
148
+ chain,
149
+ balance: balance.toString(),
150
+ balanceEth: (Number(balance) / 1e18).toFixed(6),
151
+ }),
152
+ ),
153
+ },
154
+ 'Inventory balances fetched',
155
+ );
156
+ }
157
+
126
158
  if (this.tokenInfoHandler) {
127
159
  await this.tokenInfoHandler(event);
128
160
  }
@@ -218,6 +250,53 @@ export class Monitor implements IMonitor {
218
250
  return bridgedSupply;
219
251
  }
220
252
 
253
+ private async fetchInventoryBalances(): Promise<ChainMap<bigint>> {
254
+ if (!this.inventoryConfig) return {};
255
+
256
+ const balances: ChainMap<bigint> = {};
257
+
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',
264
+ );
265
+ return { chainName, balance: 0n };
266
+ }
267
+
268
+ try {
269
+ const adapter = token.getAdapter(this.warpCore.multiProvider);
270
+ const balance = await adapter.getBalance(
271
+ this.inventoryConfig!.inventoryAddress,
272
+ );
273
+ this.logger.debug(
274
+ {
275
+ chain: chainName,
276
+ token: token.addressOrDenom,
277
+ balance: balance.toString(),
278
+ },
279
+ 'Read inventory balance',
280
+ );
281
+ return { chainName, balance };
282
+ } catch (error) {
283
+ this.logger.error(
284
+ { chain: chainName, error: (error as Error).message },
285
+ 'Failed to read inventory balance',
286
+ );
287
+ return { chainName, balance: 0n };
288
+ }
289
+ });
290
+
291
+ const results = await Promise.all(readPromises);
292
+
293
+ for (const { chainName, balance } of results) {
294
+ balances[chainName] = balance;
295
+ }
296
+
297
+ return balances;
298
+ }
299
+
221
300
  stop(): Promise<void> {
222
301
  if (!this.isMonitorRunning) return Promise.resolve();
223
302
 
package/src/service.ts CHANGED
@@ -8,7 +8,9 @@
8
8
  *
9
9
  * Environment Variables:
10
10
  * - REBALANCER_CONFIG_FILE: Path to the rebalancer configuration YAML file (required)
11
- * - HYP_KEY: Private key for signing transactions (required)
11
+ * - HYP_REBALANCER_KEY: Private key for movable collateral rebalancing operations (preferred)
12
+ * - HYP_KEY: Fallback private key for HYP_REBALANCER_KEY (optional)
13
+ * - HYP_INVENTORY_KEY: Private key for inventory operations - LiFi bridges and transferRemote (optional)
12
14
  * - COINGECKO_API_KEY: API key for CoinGecko price fetching (optional, for metrics)
13
15
  * - CHECK_FREQUENCY: Balance check frequency in ms (default: 60000)
14
16
  * - WITH_METRICS: Enable Prometheus metrics (default: "true")
@@ -19,13 +21,13 @@
19
21
  *
20
22
  * Usage:
21
23
  * node dist/service.js
22
- * REBALANCER_CONFIG_FILE=/config/rebalancer.yaml HYP_KEY=0x... node dist/service.js
24
+ * REBALANCER_CONFIG_FILE=/config/rebalancer.yaml HYP_REBALANCER_KEY=0x... HYP_INVENTORY_KEY=0x... node dist/service.js
23
25
  */
24
26
  import { Wallet } from 'ethers';
25
27
 
26
28
  import { DEFAULT_GITHUB_REGISTRY } from '@hyperlane-xyz/registry';
27
29
  import { getRegistry } from '@hyperlane-xyz/registry/fs';
28
- import { MultiProtocolProvider, MultiProvider } from '@hyperlane-xyz/sdk';
30
+ import { MultiProvider } from '@hyperlane-xyz/sdk';
29
31
  import {
30
32
  applyRpcUrlOverridesFromEnv,
31
33
  createServiceLogger,
@@ -44,12 +46,18 @@ async function main(): Promise<void> {
44
46
  process.exit(1);
45
47
  }
46
48
 
47
- const privateKey = process.env.HYP_KEY;
48
- if (!privateKey) {
49
- rootLogger.error('HYP_KEY environment variable is required');
49
+ const rebalancerPrivateKey =
50
+ process.env.HYP_REBALANCER_KEY ?? process.env.HYP_KEY;
51
+ if (!rebalancerPrivateKey) {
52
+ rootLogger.error(
53
+ 'HYP_REBALANCER_KEY (or HYP_KEY) environment variable is required',
54
+ );
50
55
  process.exit(1);
51
56
  }
52
57
 
58
+ // Optional: inventory key for inventory-based operations (LiFi bridges, transferRemote)
59
+ const inventoryPrivateKey = process.env.HYP_INVENTORY_KEY;
60
+
53
61
  // Parse optional environment variables
54
62
  let checkFrequency = 60_000;
55
63
  if (process.env.CHECK_FREQUENCY) {
@@ -106,27 +114,60 @@ async function main(): Promise<void> {
106
114
  );
107
115
 
108
116
  // Apply RPC URL overrides from environment variables
109
- const overriddenChains = applyRpcUrlOverridesFromEnv(chainMetadata);
110
- if (overriddenChains.length > 0) {
117
+ applyRpcUrlOverridesFromEnv(chainMetadata);
118
+
119
+ // Create MultiProvider with signer
120
+ const multiProvider = new MultiProvider(chainMetadata);
121
+ const rebalancerSigner = new Wallet(rebalancerPrivateKey);
122
+ multiProvider.setSharedSigner(rebalancerSigner);
123
+ logger.info(
124
+ { rebalancerAddress: rebalancerSigner.address },
125
+ '✅ Initialized MultiProvider with rebalancer signer',
126
+ );
127
+
128
+ // Create inventory MultiProvider if inventory key is provided
129
+ let inventoryMultiProvider: MultiProvider | undefined;
130
+ if (inventoryPrivateKey) {
131
+ inventoryMultiProvider = new MultiProvider(chainMetadata, {
132
+ providers: multiProvider.providers,
133
+ });
134
+ const inventorySigner = new Wallet(inventoryPrivateKey);
135
+ inventoryMultiProvider.setSharedSigner(inventorySigner);
136
+
137
+ // Validate against config.inventorySigner if present
138
+ const inventoryAddress = inventorySigner.address;
139
+ if (
140
+ rebalancerConfig.inventorySigner &&
141
+ rebalancerConfig.inventorySigner.toLowerCase() !==
142
+ inventoryAddress.toLowerCase()
143
+ ) {
144
+ throw new Error(
145
+ `inventorySigner mismatch: config has ${rebalancerConfig.inventorySigner} but HYP_INVENTORY_KEY derives to ${inventoryAddress}`,
146
+ );
147
+ }
111
148
  logger.info(
112
- { chains: overriddenChains, count: overriddenChains.length },
113
- 'Applied RPC overrides from environment variables',
149
+ { inventoryAddress },
150
+ ' Initialized inventory MultiProvider',
114
151
  );
115
152
  }
116
153
 
117
- // Create MultiProvider with signer
118
- const multiProvider = new MultiProvider(chainMetadata);
119
- const signer = new Wallet(privateKey);
120
- multiProvider.setSharedSigner(signer);
121
- logger.info('✅ Initialized MultiProvider with signer');
154
+ // Fail fast if config references inventorySigner but no HYP_INVENTORY_KEY is provided
155
+ // Without the matching key, inventory operations would silently use the wrong signer
156
+ if (rebalancerConfig.inventorySigner && !inventoryPrivateKey) {
157
+ logger.error(
158
+ { inventorySigner: rebalancerConfig.inventorySigner },
159
+ 'Config specifies inventorySigner but HYP_INVENTORY_KEY is not set. Provide the key or remove inventorySigner from config.',
160
+ );
161
+ process.exit(1);
162
+ }
122
163
 
123
- // Create MultiProtocolProvider
124
- const multiProtocolProvider = new MultiProtocolProvider(chainMetadata);
125
- logger.info('✅ Initialized MultiProtocolProvider');
164
+ // MultiProtocolProvider will be derived from multiProvider in factory
165
+ const multiProtocolProvider = undefined;
126
166
 
127
167
  // Create the rebalancer service
128
168
  const service = new RebalancerService(
129
169
  multiProvider,
170
+ inventoryMultiProvider,
130
171
  multiProtocolProvider,
131
172
  registry,
132
173
  rebalancerConfig,