@hyperlane-xyz/rebalancer 0.1.0-beta.5a8bd28ab

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 (202) hide show
  1. package/README.md +178 -0
  2. package/dist/config/RebalancerConfig.d.ts +12 -0
  3. package/dist/config/RebalancerConfig.d.ts.map +1 -0
  4. package/dist/config/RebalancerConfig.js +29 -0
  5. package/dist/config/RebalancerConfig.js.map +1 -0
  6. package/dist/config/RebalancerConfig.test.d.ts +2 -0
  7. package/dist/config/RebalancerConfig.test.d.ts.map +1 -0
  8. package/dist/config/RebalancerConfig.test.js +325 -0
  9. package/dist/config/RebalancerConfig.test.js.map +1 -0
  10. package/dist/core/Rebalancer.d.ts +23 -0
  11. package/dist/core/Rebalancer.d.ts.map +1 -0
  12. package/dist/core/Rebalancer.js +290 -0
  13. package/dist/core/Rebalancer.js.map +1 -0
  14. package/dist/core/RebalancerService.d.ts +115 -0
  15. package/dist/core/RebalancerService.d.ts.map +1 -0
  16. package/dist/core/RebalancerService.js +227 -0
  17. package/dist/core/RebalancerService.js.map +1 -0
  18. package/dist/core/WithInflightGuard.d.ts +20 -0
  19. package/dist/core/WithInflightGuard.d.ts.map +1 -0
  20. package/dist/core/WithInflightGuard.js +47 -0
  21. package/dist/core/WithInflightGuard.js.map +1 -0
  22. package/dist/core/WithInflightGuard.test.d.ts +2 -0
  23. package/dist/core/WithInflightGuard.test.d.ts.map +1 -0
  24. package/dist/core/WithInflightGuard.test.js +64 -0
  25. package/dist/core/WithInflightGuard.test.js.map +1 -0
  26. package/dist/core/WithSemaphore.d.ts +22 -0
  27. package/dist/core/WithSemaphore.d.ts.map +1 -0
  28. package/dist/core/WithSemaphore.js +67 -0
  29. package/dist/core/WithSemaphore.js.map +1 -0
  30. package/dist/core/WithSemaphore.test.d.ts +2 -0
  31. package/dist/core/WithSemaphore.test.d.ts.map +1 -0
  32. package/dist/core/WithSemaphore.test.js +83 -0
  33. package/dist/core/WithSemaphore.test.js.map +1 -0
  34. package/dist/factories/RebalancerContextFactory.d.ts +41 -0
  35. package/dist/factories/RebalancerContextFactory.d.ts.map +1 -0
  36. package/dist/factories/RebalancerContextFactory.js +115 -0
  37. package/dist/factories/RebalancerContextFactory.js.map +1 -0
  38. package/dist/index.d.ts +33 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +35 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/interfaces/IMetrics.d.ts +5 -0
  43. package/dist/interfaces/IMetrics.d.ts.map +1 -0
  44. package/dist/interfaces/IMetrics.js +2 -0
  45. package/dist/interfaces/IMetrics.js.map +1 -0
  46. package/dist/interfaces/IMonitor.d.ts +51 -0
  47. package/dist/interfaces/IMonitor.d.ts.map +1 -0
  48. package/dist/interfaces/IMonitor.js +14 -0
  49. package/dist/interfaces/IMonitor.js.map +1 -0
  50. package/dist/interfaces/IRebalancer.d.ts +15 -0
  51. package/dist/interfaces/IRebalancer.d.ts.map +1 -0
  52. package/dist/interfaces/IRebalancer.js +2 -0
  53. package/dist/interfaces/IRebalancer.js.map +1 -0
  54. package/dist/interfaces/IStrategy.d.ts +11 -0
  55. package/dist/interfaces/IStrategy.d.ts.map +1 -0
  56. package/dist/interfaces/IStrategy.js +2 -0
  57. package/dist/interfaces/IStrategy.js.map +1 -0
  58. package/dist/metrics/Metrics.d.ts +31 -0
  59. package/dist/metrics/Metrics.d.ts.map +1 -0
  60. package/dist/metrics/Metrics.js +302 -0
  61. package/dist/metrics/Metrics.js.map +1 -0
  62. package/dist/metrics/PriceGetter.d.ts +10 -0
  63. package/dist/metrics/PriceGetter.d.ts.map +1 -0
  64. package/dist/metrics/PriceGetter.js +41 -0
  65. package/dist/metrics/PriceGetter.js.map +1 -0
  66. package/dist/metrics/scripts/metrics.d.ts +14 -0
  67. package/dist/metrics/scripts/metrics.d.ts.map +1 -0
  68. package/dist/metrics/scripts/metrics.js +198 -0
  69. package/dist/metrics/scripts/metrics.js.map +1 -0
  70. package/dist/metrics/types.d.ts +24 -0
  71. package/dist/metrics/types.d.ts.map +1 -0
  72. package/dist/metrics/types.js +2 -0
  73. package/dist/metrics/types.js.map +1 -0
  74. package/dist/metrics/utils/metrics.d.ts +12 -0
  75. package/dist/metrics/utils/metrics.d.ts.map +1 -0
  76. package/dist/metrics/utils/metrics.js +28 -0
  77. package/dist/metrics/utils/metrics.js.map +1 -0
  78. package/dist/monitor/Monitor.d.ts +26 -0
  79. package/dist/monitor/Monitor.d.ts.map +1 -0
  80. package/dist/monitor/Monitor.js +116 -0
  81. package/dist/monitor/Monitor.js.map +1 -0
  82. package/dist/service.d.ts +3 -0
  83. package/dist/service.d.ts.map +1 -0
  84. package/dist/service.js +125 -0
  85. package/dist/service.js.map +1 -0
  86. package/dist/strategy/BaseStrategy.d.ts +34 -0
  87. package/dist/strategy/BaseStrategy.d.ts.map +1 -0
  88. package/dist/strategy/BaseStrategy.js +127 -0
  89. package/dist/strategy/BaseStrategy.js.map +1 -0
  90. package/dist/strategy/MinAmountStrategy.d.ts +27 -0
  91. package/dist/strategy/MinAmountStrategy.d.ts.map +1 -0
  92. package/dist/strategy/MinAmountStrategy.js +103 -0
  93. package/dist/strategy/MinAmountStrategy.js.map +1 -0
  94. package/dist/strategy/MinAmountStrategy.test.d.ts +2 -0
  95. package/dist/strategy/MinAmountStrategy.test.d.ts.map +1 -0
  96. package/dist/strategy/MinAmountStrategy.test.js +472 -0
  97. package/dist/strategy/MinAmountStrategy.test.js.map +1 -0
  98. package/dist/strategy/StrategyFactory.d.ts +16 -0
  99. package/dist/strategy/StrategyFactory.d.ts.map +1 -0
  100. package/dist/strategy/StrategyFactory.js +25 -0
  101. package/dist/strategy/StrategyFactory.js.map +1 -0
  102. package/dist/strategy/StrategyFactory.test.d.ts +2 -0
  103. package/dist/strategy/StrategyFactory.test.d.ts.map +1 -0
  104. package/dist/strategy/StrategyFactory.test.js +80 -0
  105. package/dist/strategy/StrategyFactory.test.js.map +1 -0
  106. package/dist/strategy/WeightedStrategy.d.ts +23 -0
  107. package/dist/strategy/WeightedStrategy.d.ts.map +1 -0
  108. package/dist/strategy/WeightedStrategy.js +61 -0
  109. package/dist/strategy/WeightedStrategy.js.map +1 -0
  110. package/dist/strategy/WeightedStrategy.test.d.ts +2 -0
  111. package/dist/strategy/WeightedStrategy.test.d.ts.map +1 -0
  112. package/dist/strategy/WeightedStrategy.test.js +307 -0
  113. package/dist/strategy/WeightedStrategy.test.js.map +1 -0
  114. package/dist/strategy/index.d.ts +5 -0
  115. package/dist/strategy/index.d.ts.map +1 -0
  116. package/dist/strategy/index.js +5 -0
  117. package/dist/strategy/index.js.map +1 -0
  118. package/dist/test/helpers.d.ts +8 -0
  119. package/dist/test/helpers.d.ts.map +1 -0
  120. package/dist/test/helpers.js +33 -0
  121. package/dist/test/helpers.js.map +1 -0
  122. package/dist/utils/ExplorerClient.d.ts +14 -0
  123. package/dist/utils/ExplorerClient.d.ts.map +1 -0
  124. package/dist/utils/ExplorerClient.js +82 -0
  125. package/dist/utils/ExplorerClient.js.map +1 -0
  126. package/dist/utils/balanceUtils.d.ts +13 -0
  127. package/dist/utils/balanceUtils.d.ts.map +1 -0
  128. package/dist/utils/balanceUtils.js +43 -0
  129. package/dist/utils/balanceUtils.js.map +1 -0
  130. package/dist/utils/balanceUtils.test.d.ts +2 -0
  131. package/dist/utils/balanceUtils.test.d.ts.map +1 -0
  132. package/dist/utils/balanceUtils.test.js +54 -0
  133. package/dist/utils/balanceUtils.test.js.map +1 -0
  134. package/dist/utils/bridgeUtils.d.ts +19 -0
  135. package/dist/utils/bridgeUtils.d.ts.map +1 -0
  136. package/dist/utils/bridgeUtils.js +20 -0
  137. package/dist/utils/bridgeUtils.js.map +1 -0
  138. package/dist/utils/bridgeUtils.test.d.ts +2 -0
  139. package/dist/utils/bridgeUtils.test.d.ts.map +1 -0
  140. package/dist/utils/bridgeUtils.test.js +77 -0
  141. package/dist/utils/bridgeUtils.test.js.map +1 -0
  142. package/dist/utils/errors.d.ts +4 -0
  143. package/dist/utils/errors.d.ts.map +1 -0
  144. package/dist/utils/errors.js +6 -0
  145. package/dist/utils/errors.js.map +1 -0
  146. package/dist/utils/files.d.ts +35 -0
  147. package/dist/utils/files.d.ts.map +1 -0
  148. package/dist/utils/files.js +190 -0
  149. package/dist/utils/files.js.map +1 -0
  150. package/dist/utils/generalUtils.d.ts +3 -0
  151. package/dist/utils/generalUtils.d.ts.map +1 -0
  152. package/dist/utils/generalUtils.js +9 -0
  153. package/dist/utils/generalUtils.js.map +1 -0
  154. package/dist/utils/index.d.ts +5 -0
  155. package/dist/utils/index.d.ts.map +1 -0
  156. package/dist/utils/index.js +5 -0
  157. package/dist/utils/index.js.map +1 -0
  158. package/dist/utils/tokenUtils.d.ts +14 -0
  159. package/dist/utils/tokenUtils.d.ts.map +1 -0
  160. package/dist/utils/tokenUtils.js +21 -0
  161. package/dist/utils/tokenUtils.js.map +1 -0
  162. package/package.json +70 -0
  163. package/src/config/RebalancerConfig.test.ts +388 -0
  164. package/src/config/RebalancerConfig.ts +39 -0
  165. package/src/core/Rebalancer.ts +471 -0
  166. package/src/core/RebalancerService.ts +333 -0
  167. package/src/core/WithInflightGuard.test.ts +131 -0
  168. package/src/core/WithInflightGuard.ts +67 -0
  169. package/src/core/WithSemaphore.test.ts +112 -0
  170. package/src/core/WithSemaphore.ts +92 -0
  171. package/src/factories/RebalancerContextFactory.ts +210 -0
  172. package/src/index.ts +68 -0
  173. package/src/interfaces/IMetrics.ts +5 -0
  174. package/src/interfaces/IMonitor.ts +63 -0
  175. package/src/interfaces/IRebalancer.ts +20 -0
  176. package/src/interfaces/IStrategy.ts +13 -0
  177. package/src/metrics/Metrics.ts +558 -0
  178. package/src/metrics/PriceGetter.ts +74 -0
  179. package/src/metrics/scripts/metrics.ts +298 -0
  180. package/src/metrics/types.ts +27 -0
  181. package/src/metrics/utils/metrics.ts +33 -0
  182. package/src/monitor/Monitor.ts +174 -0
  183. package/src/service.ts +154 -0
  184. package/src/strategy/BaseStrategy.ts +210 -0
  185. package/src/strategy/MinAmountStrategy.test.ts +625 -0
  186. package/src/strategy/MinAmountStrategy.ts +170 -0
  187. package/src/strategy/StrategyFactory.test.ts +109 -0
  188. package/src/strategy/StrategyFactory.ts +48 -0
  189. package/src/strategy/WeightedStrategy.test.ts +408 -0
  190. package/src/strategy/WeightedStrategy.ts +93 -0
  191. package/src/strategy/index.ts +4 -0
  192. package/src/test/helpers.ts +46 -0
  193. package/src/utils/ExplorerClient.ts +99 -0
  194. package/src/utils/balanceUtils.test.ts +74 -0
  195. package/src/utils/balanceUtils.ts +69 -0
  196. package/src/utils/bridgeUtils.test.ts +92 -0
  197. package/src/utils/bridgeUtils.ts +42 -0
  198. package/src/utils/errors.ts +5 -0
  199. package/src/utils/files.ts +276 -0
  200. package/src/utils/generalUtils.ts +13 -0
  201. package/src/utils/index.ts +4 -0
  202. package/src/utils/tokenUtils.ts +26 -0
@@ -0,0 +1,92 @@
1
+ import type { Logger } from 'pino';
2
+
3
+ import { RebalancerConfig } from '../config/RebalancerConfig.js';
4
+ import type { IRebalancer } from '../interfaces/IRebalancer.js';
5
+ import type { RebalancingRoute } from '../interfaces/IStrategy.js';
6
+
7
+ /**
8
+ * Prevents frequent rebalancing operations while bridges complete.
9
+ */
10
+ export class WithSemaphore implements IRebalancer {
11
+ // Timestamp until which rebalancing should be blocked
12
+ private waitUntil: number = 0;
13
+ // Lock to prevent concurrent rebalance execution
14
+ private executing: boolean = false;
15
+ private readonly logger: Logger;
16
+
17
+ constructor(
18
+ private readonly config: RebalancerConfig,
19
+ private readonly rebalancer: IRebalancer,
20
+ logger: Logger,
21
+ ) {
22
+ this.logger = logger.child({ class: WithSemaphore.name });
23
+ }
24
+
25
+ /**
26
+ * Rebalance with timing control
27
+ * @param routes - Routes to process
28
+ */
29
+ async rebalance(routes: RebalancingRoute[]): Promise<void> {
30
+ if (this.executing) {
31
+ this.logger.info('Currently executing rebalance. Skipping.');
32
+
33
+ return;
34
+ }
35
+
36
+ // No routes mean the system is balanced so we reset the timer to allow new rebalancing
37
+ if (!routes.length) {
38
+ this.logger.info(
39
+ 'No routes to execute. Assuming rebalance is complete. Resetting semaphore timer.',
40
+ );
41
+
42
+ this.waitUntil = 0;
43
+ return;
44
+ }
45
+
46
+ // Skip if still in waiting period
47
+ if (Date.now() < this.waitUntil) {
48
+ this.logger.info('Still in waiting period. Skipping rebalance.');
49
+
50
+ return;
51
+ }
52
+
53
+ // The wait period will be determined by the bridge with the highest wait tolerance
54
+ const highestTolerance = this.getHighestLockTime(routes);
55
+
56
+ try {
57
+ // Execute rebalance
58
+ this.executing = true;
59
+ await this.rebalancer.rebalance(routes);
60
+ } finally {
61
+ this.executing = false;
62
+ }
63
+
64
+ // Set new waiting period
65
+ this.waitUntil = Date.now() + highestTolerance;
66
+
67
+ this.logger.info(
68
+ {
69
+ highestTolerance,
70
+ waitUntil: this.waitUntil,
71
+ },
72
+ 'Rebalance semaphore locked',
73
+ );
74
+ }
75
+
76
+ private getHighestLockTime(routes: RebalancingRoute[]) {
77
+ return routes.reduce((highest, route) => {
78
+ const origin = this.config.strategyConfig.chains[route.origin];
79
+
80
+ if (!origin) {
81
+ this.logger.error({ route }, 'Chain not found in config. Skipping.');
82
+ throw new Error(`Chain ${route.origin} not found in config`);
83
+ }
84
+
85
+ const bridgeLockTime = origin.bridgeLockTime;
86
+ const overrideLockTime =
87
+ origin.override?.[route.destination]?.bridgeLockTime ?? 0;
88
+
89
+ return Math.max(highest, bridgeLockTime, overrideLockTime);
90
+ }, 0);
91
+ }
92
+ }
@@ -0,0 +1,210 @@
1
+ import { Logger } from 'pino';
2
+
3
+ import { IRegistry } from '@hyperlane-xyz/registry';
4
+ import {
5
+ type ChainMap,
6
+ MultiProtocolProvider,
7
+ MultiProvider,
8
+ type Token,
9
+ WarpCore,
10
+ } from '@hyperlane-xyz/sdk';
11
+ import { objMap } from '@hyperlane-xyz/utils';
12
+
13
+ import { RebalancerConfig } from '../config/RebalancerConfig.js';
14
+ import { Rebalancer } from '../core/Rebalancer.js';
15
+ import { WithSemaphore } from '../core/WithSemaphore.js';
16
+ import type { IRebalancer } from '../interfaces/IRebalancer.js';
17
+ import type { IStrategy } from '../interfaces/IStrategy.js';
18
+ import { Metrics } from '../metrics/Metrics.js';
19
+ import { PriceGetter } from '../metrics/PriceGetter.js';
20
+ import { Monitor } from '../monitor/Monitor.js';
21
+ import { StrategyFactory } from '../strategy/StrategyFactory.js';
22
+ import { isCollateralizedTokenEligibleForRebalancing } from '../utils/index.js';
23
+
24
+ export class RebalancerContextFactory {
25
+ /**
26
+ * @param config - The rebalancer config
27
+ * @param warpCore - An instance of `WarpCore` configured for the specified `warpRouteId`.
28
+ * @param tokensByChainName - A map of chain->token to ease the lookup of token by chain
29
+ * @param multiProvider - MultiProvider instance
30
+ * @param registry - IRegistry instance
31
+ * @param logger - Logger instance
32
+ */
33
+ private constructor(
34
+ private readonly config: RebalancerConfig,
35
+ private readonly warpCore: WarpCore,
36
+ private readonly tokensByChainName: ChainMap<Token>,
37
+ private readonly multiProvider: MultiProvider,
38
+ private readonly registry: IRegistry,
39
+ private readonly logger: Logger,
40
+ ) {}
41
+
42
+ /**
43
+ * @param config - The rebalancer config
44
+ * @param multiProvider - MultiProvider instance
45
+ * @param multiProtocolProvider - MultiProtocolProvider instance (optional, created from multiProvider if not provided)
46
+ * @param registry - IRegistry instance
47
+ * @param logger - Logger instance
48
+ */
49
+ public static async create(
50
+ config: RebalancerConfig,
51
+ multiProvider: MultiProvider,
52
+ multiProtocolProvider: MultiProtocolProvider | undefined,
53
+ registry: IRegistry,
54
+ logger: Logger,
55
+ ): Promise<RebalancerContextFactory> {
56
+ logger.debug(
57
+ {
58
+ warpRouteId: config.warpRouteId,
59
+ },
60
+ 'Creating RebalancerContextFactory',
61
+ );
62
+ const addresses = await registry.getAddresses();
63
+
64
+ // The Sealevel warp adapters require the Mailbox address, so we
65
+ // get mailboxes for all chains and merge them with the chain metadata.
66
+ const mailboxes = objMap(addresses, (_, { mailbox }) => ({ mailbox }));
67
+
68
+ // Create MultiProtocolProvider (convert from MultiProvider if not provided)
69
+ const mpp =
70
+ multiProtocolProvider ??
71
+ MultiProtocolProvider.fromMultiProvider(multiProvider);
72
+ const provider = mpp.extendChainMetadata(mailboxes);
73
+
74
+ const warpCoreConfig = await registry.getWarpRoute(config.warpRouteId);
75
+ if (!warpCoreConfig) {
76
+ throw new Error(
77
+ `Warp route config for ${config.warpRouteId} not found in registry`,
78
+ );
79
+ }
80
+ const warpCore = WarpCore.FromConfig(provider, warpCoreConfig);
81
+ const tokensByChainName = Object.fromEntries(
82
+ warpCore.tokens.map((t) => [t.chainName, t]),
83
+ );
84
+
85
+ logger.debug(
86
+ {
87
+ warpRouteId: config.warpRouteId,
88
+ },
89
+ 'RebalancerContextFactory created successfully',
90
+ );
91
+ return new RebalancerContextFactory(
92
+ config,
93
+ warpCore,
94
+ tokensByChainName,
95
+ multiProvider,
96
+ registry,
97
+ logger,
98
+ );
99
+ }
100
+
101
+ public getWarpCore(): WarpCore {
102
+ return this.warpCore;
103
+ }
104
+
105
+ public getTokenForChain(chainName: string): Token | undefined {
106
+ return this.tokensByChainName[chainName];
107
+ }
108
+
109
+ public async createMetrics(coingeckoApiKey?: string): Promise<Metrics> {
110
+ this.logger.debug(
111
+ { warpRouteId: this.config.warpRouteId },
112
+ 'Creating Metrics',
113
+ );
114
+ const tokenPriceGetter = PriceGetter.create(
115
+ this.multiProvider.metadata,
116
+ this.logger,
117
+ coingeckoApiKey,
118
+ );
119
+ const warpDeployConfig = await this.registry.getWarpDeployConfig(
120
+ this.config.warpRouteId,
121
+ );
122
+
123
+ return new Metrics(
124
+ tokenPriceGetter,
125
+ warpDeployConfig,
126
+ this.warpCore,
127
+ this.config.warpRouteId,
128
+ this.logger,
129
+ );
130
+ }
131
+
132
+ public createMonitor(checkFrequency: number): Monitor {
133
+ this.logger.debug(
134
+ {
135
+ warpRouteId: this.config.warpRouteId,
136
+ checkFrequency: checkFrequency,
137
+ },
138
+ 'Creating Monitor',
139
+ );
140
+ return new Monitor(checkFrequency, this.warpCore, this.logger);
141
+ }
142
+
143
+ public async createStrategy(metrics?: Metrics): Promise<IStrategy> {
144
+ this.logger.debug(
145
+ {
146
+ warpRouteId: this.config.warpRouteId,
147
+ strategyType: this.config.strategyConfig.rebalanceStrategy,
148
+ },
149
+ 'Creating Strategy',
150
+ );
151
+ return StrategyFactory.createStrategy(
152
+ this.config.strategyConfig,
153
+ this.tokensByChainName,
154
+ await this.getInitialTotalCollateral(),
155
+ this.logger,
156
+ metrics,
157
+ );
158
+ }
159
+
160
+ public createRebalancer(metrics?: Metrics): IRebalancer {
161
+ this.logger.debug(
162
+ { warpRouteId: this.config.warpRouteId },
163
+ 'Creating Rebalancer',
164
+ );
165
+ const rebalancer = new Rebalancer(
166
+ objMap(this.config.strategyConfig.chains, (_, v) => ({
167
+ bridge: v.bridge,
168
+ bridgeMinAcceptedAmount: v.bridgeMinAcceptedAmount ?? 0,
169
+ bridgeIsWarp: v.bridgeIsWarp ?? false,
170
+ override: v.override,
171
+ })),
172
+ this.warpCore,
173
+ this.multiProvider.metadata,
174
+ this.tokensByChainName,
175
+ this.multiProvider,
176
+ this.logger,
177
+ metrics,
178
+ );
179
+
180
+ // Wrap with semaphore for concurrency control
181
+ const withSemaphore = new WithSemaphore(
182
+ this.config,
183
+ rebalancer,
184
+ this.logger,
185
+ );
186
+
187
+ return withSemaphore;
188
+ }
189
+
190
+ private async getInitialTotalCollateral(): Promise<bigint> {
191
+ let initialTotalCollateral = 0n;
192
+
193
+ const chainNames = new Set(Object.keys(this.config.strategyConfig.chains));
194
+
195
+ await Promise.all(
196
+ this.warpCore.tokens.map(async (token) => {
197
+ if (
198
+ isCollateralizedTokenEligibleForRebalancing(token) &&
199
+ chainNames.has(token.chainName)
200
+ ) {
201
+ const adapter = token.getHypAdapter(this.warpCore.multiProvider);
202
+ const bridgedSupply = await adapter.getBridgedSupply();
203
+ initialTotalCollateral += bridgedSupply ?? 0n;
204
+ }
205
+ }),
206
+ );
207
+
208
+ return initialTotalCollateral;
209
+ }
210
+ }
package/src/index.ts ADDED
@@ -0,0 +1,68 @@
1
+ /**
2
+ * @hyperlane-xyz/rebalancer
3
+ *
4
+ * Hyperlane Warp Route Collateral Rebalancer
5
+ *
6
+ * This package provides functionality for automatically rebalancing collateral
7
+ * across Hyperlane warp routes to maintain optimal token distribution.
8
+ */
9
+
10
+ // Core service
11
+ export { RebalancerService } from './core/RebalancerService.js';
12
+ export type {
13
+ RebalancerServiceConfig,
14
+ ManualRebalanceRequest,
15
+ } from './core/RebalancerService.js';
16
+
17
+ // Core rebalancing logic
18
+ export { Rebalancer } from './core/Rebalancer.js';
19
+ export { WithInflightGuard } from './core/WithInflightGuard.js';
20
+ export { WithSemaphore } from './core/WithSemaphore.js';
21
+
22
+ // Configuration
23
+ export { RebalancerConfig } from './config/RebalancerConfig.js';
24
+
25
+ // Strategy
26
+ export { BaseStrategy } from './strategy/BaseStrategy.js';
27
+ export { WeightedStrategy } from './strategy/WeightedStrategy.js';
28
+ export { MinAmountStrategy } from './strategy/MinAmountStrategy.js';
29
+ export { StrategyFactory } from './strategy/StrategyFactory.js';
30
+
31
+ // Monitor
32
+ export { Monitor } from './monitor/Monitor.js';
33
+
34
+ // Metrics
35
+ export { Metrics } from './metrics/Metrics.js';
36
+ export { PriceGetter } from './metrics/PriceGetter.js';
37
+
38
+ // Interfaces
39
+ export type {
40
+ IRebalancer,
41
+ PreparedTransaction,
42
+ } from './interfaces/IRebalancer.js';
43
+ export type {
44
+ IStrategy,
45
+ RebalancingRoute,
46
+ RawBalances,
47
+ } from './interfaces/IStrategy.js';
48
+ export type { IMonitor } from './interfaces/IMonitor.js';
49
+ export {
50
+ MonitorEventType,
51
+ MonitorEvent,
52
+ MonitorPollingError,
53
+ MonitorStartError,
54
+ } from './interfaces/IMonitor.js';
55
+ export type { IMetrics } from './interfaces/IMetrics.js';
56
+
57
+ // Utils
58
+ export { getBridgeConfig } from './utils/bridgeUtils.js';
59
+ export type {
60
+ BridgeConfigWithOverride,
61
+ BridgeConfig,
62
+ } from './utils/bridgeUtils.js';
63
+ export { getRawBalances } from './utils/balanceUtils.js';
64
+ export { isCollateralizedTokenEligibleForRebalancing } from './utils/tokenUtils.js';
65
+ export { ExplorerClient } from './utils/ExplorerClient.js';
66
+
67
+ // Factory
68
+ export { RebalancerContextFactory } from './factories/RebalancerContextFactory.js';
@@ -0,0 +1,5 @@
1
+ import { MonitorEvent } from './IMonitor.js';
2
+
3
+ export interface IMetrics {
4
+ processToken(tokenInfo: MonitorEvent['tokensInfo'][number]): Promise<void>;
5
+ }
@@ -0,0 +1,63 @@
1
+ import { Token } from '@hyperlane-xyz/sdk';
2
+
3
+ import { WrappedError } from '../utils/errors.js';
4
+
5
+ export class MonitorStartError extends WrappedError {
6
+ name = 'MonitorStartError';
7
+ }
8
+
9
+ export class MonitorPollingError extends WrappedError {
10
+ name = 'MonitorPollingError';
11
+ }
12
+
13
+ export enum MonitorEventType {
14
+ TokenInfo = 'TokenInfo',
15
+ Error = 'Error',
16
+ Start = 'Start',
17
+ }
18
+
19
+ /**
20
+ * Represents an event emitted by the monitor containing bridgedSupply and token information.
21
+ */
22
+ export type MonitorEvent = {
23
+ /**
24
+ * Collection of objects containing the information retrieved by the Monitor.
25
+ */
26
+ tokensInfo: {
27
+ token: Token;
28
+ bridgedSupply?: bigint;
29
+ }[];
30
+ };
31
+
32
+ /**
33
+ * Interface for a monitoring service that tracks token information across different chains.
34
+ */
35
+ export interface IMonitor {
36
+ /**
37
+ * Allows subscribers to listen to hyperlane's tokens info.
38
+ */
39
+ on(
40
+ eventName: MonitorEventType.TokenInfo,
41
+ fn: (event: MonitorEvent) => void,
42
+ ): this;
43
+
44
+ /**
45
+ * Allows subscribers to listen to error events.
46
+ */
47
+ on(eventName: MonitorEventType.Error, fn: (event: Error) => void): this;
48
+
49
+ /**
50
+ * Allows subscribers to listen to start events.
51
+ */
52
+ on(eventName: MonitorEventType.Start, fn: () => void): this;
53
+
54
+ /**
55
+ * Starts the monitoring long-running process.
56
+ */
57
+ start(): Promise<void>;
58
+
59
+ /**
60
+ * Stops the monitoring long-running process.
61
+ */
62
+ stop(): void;
63
+ }
@@ -0,0 +1,20 @@
1
+ import { EvmMovableCollateralAdapter, TokenAmount } from '@hyperlane-xyz/sdk';
2
+
3
+ import { RebalancingRoute } from './IStrategy.js';
4
+
5
+ export type PreparedTransaction = {
6
+ populatedTx: Awaited<
7
+ ReturnType<EvmMovableCollateralAdapter['populateRebalanceTx']>
8
+ >;
9
+ route: RebalancingRoute;
10
+ originTokenAmount: TokenAmount;
11
+ };
12
+
13
+ export type RebalanceMetrics = {
14
+ route: RebalancingRoute;
15
+ originTokenAmount: TokenAmount;
16
+ };
17
+
18
+ export interface IRebalancer {
19
+ rebalance(routes: RebalancingRoute[]): Promise<void>;
20
+ }
@@ -0,0 +1,13 @@
1
+ import { ChainMap, ChainName } from '@hyperlane-xyz/sdk';
2
+
3
+ export type RawBalances = ChainMap<bigint>;
4
+
5
+ export type RebalancingRoute = {
6
+ origin: ChainName;
7
+ destination: ChainName;
8
+ amount: bigint;
9
+ };
10
+
11
+ export interface IStrategy {
12
+ getRebalancingRoutes(rawBalances: RawBalances): RebalancingRoute[];
13
+ }