@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,20 @@
1
+ import type { Logger } from 'pino';
2
+ import { ChainMetadataManager } from '@hyperlane-xyz/sdk';
3
+ import { RebalancerConfig } from '../config/RebalancerConfig.js';
4
+ import type { IRebalancer } from '../interfaces/IRebalancer.js';
5
+ import type { RebalancingRoute } from '../interfaces/IStrategy.js';
6
+ import { ExplorerClient } from '../utils/ExplorerClient.js';
7
+ /**
8
+ * Prevents rebalancing if there are inflight rebalances for the warp route.
9
+ */
10
+ export declare class WithInflightGuard implements IRebalancer {
11
+ private readonly config;
12
+ private readonly rebalancer;
13
+ private readonly explorer;
14
+ private readonly txSender;
15
+ private readonly chainManager;
16
+ private readonly logger;
17
+ constructor(config: RebalancerConfig, rebalancer: IRebalancer, explorer: ExplorerClient, txSender: string, chainManager: ChainMetadataManager, logger: Logger);
18
+ rebalance(routes: RebalancingRoute[]): Promise<void>;
19
+ }
20
+ //# sourceMappingURL=WithInflightGuard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WithInflightGuard.d.ts","sourceRoot":"","sources":["../../src/core/WithInflightGuard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5D;;GAEG;AACH,qBAAa,iBAAkB,YAAW,WAAW;IAIjD,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAP/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAGb,MAAM,EAAE,gBAAgB,EACxB,UAAU,EAAE,WAAW,EACvB,QAAQ,EAAE,cAAc,EACxB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,oBAAoB,EACnD,MAAM,EAAE,MAAM;IAKV,SAAS,CAAC,MAAM,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAwC3D"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Prevents rebalancing if there are inflight rebalances for the warp route.
3
+ */
4
+ export class WithInflightGuard {
5
+ config;
6
+ rebalancer;
7
+ explorer;
8
+ txSender;
9
+ chainManager;
10
+ logger;
11
+ constructor(config, rebalancer, explorer, txSender, chainManager, logger) {
12
+ this.config = config;
13
+ this.rebalancer = rebalancer;
14
+ this.explorer = explorer;
15
+ this.txSender = txSender;
16
+ this.chainManager = chainManager;
17
+ this.logger = logger.child({ class: WithInflightGuard.name });
18
+ }
19
+ async rebalance(routes) {
20
+ // Always enforce the inflight guard
21
+ if (routes.length === 0) {
22
+ return this.rebalancer.rebalance(routes);
23
+ }
24
+ const chains = Object.keys(this.config.strategyConfig.chains);
25
+ const bridges = chains.map((chain) => this.config.strategyConfig.chains[chain].bridge);
26
+ const domains = chains.map((chain) => this.chainManager.getDomainId(chain));
27
+ let hasInflightRebalances = false;
28
+ try {
29
+ hasInflightRebalances = await this.explorer.hasUndeliveredRebalance({
30
+ bridges,
31
+ domains: Array.from(new Set(domains)),
32
+ txSender: this.txSender,
33
+ limit: 5,
34
+ }, this.logger);
35
+ }
36
+ catch (e) {
37
+ this.logger.error({ status: e.status, body: e.body }, 'Explorer inflight query failed');
38
+ throw e;
39
+ }
40
+ if (hasInflightRebalances) {
41
+ this.logger.info('Inflight rebalance detected via Explorer; skipping this cycle');
42
+ return;
43
+ }
44
+ return this.rebalancer.rebalance(routes);
45
+ }
46
+ }
47
+ //# sourceMappingURL=WithInflightGuard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WithInflightGuard.js","sourceRoot":"","sources":["../../src/core/WithInflightGuard.ts"],"names":[],"mappings":"AASA;;GAEG;AACH,MAAM,OAAO,iBAAiB;IAIT;IACA;IACA;IACA;IACA;IAPF,MAAM,CAAS;IAEhC,YACmB,MAAwB,EACxB,UAAuB,EACvB,QAAwB,EACxB,QAAgB,EAChB,YAAkC,EACnD,MAAc;QALG,WAAM,GAAN,MAAM,CAAkB;QACxB,eAAU,GAAV,UAAU,CAAa;QACvB,aAAQ,GAAR,QAAQ,CAAgB;QACxB,aAAQ,GAAR,QAAQ,CAAQ;QAChB,iBAAY,GAAZ,YAAY,CAAsB;QAGnD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,iBAAiB,CAAC,IAAI,EAAE,CAAC,CAAC;IAChE,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,MAA0B;QACxC,oCAAoC;QACpC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,OAAO,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC3C,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QAC9D,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CACxB,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAC3D,CAAC;QACF,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC;QAE5E,IAAI,qBAAqB,GAAG,KAAK,CAAC;QAClC,IAAI,CAAC;YACH,qBAAqB,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,uBAAuB,CACjE;gBACE,OAAO;gBACP,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;gBACrC,QAAQ,EAAE,IAAI,CAAC,QAAQ;gBACvB,KAAK,EAAE,CAAC;aACT,EACD,IAAI,CAAC,MAAM,CACZ,CAAC;QACJ,CAAC;QAAC,OAAO,CAAM,EAAE,CAAC;YAChB,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAClC,gCAAgC,CACjC,CAAC;YACF,MAAM,CAAC,CAAC;QACV,CAAC;QAED,IAAI,qBAAqB,EAAE,CAAC;YAC1B,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,+DAA+D,CAChE,CAAC;YACF,OAAO;QACT,CAAC;QAED,OAAO,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAC3C,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=WithInflightGuard.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WithInflightGuard.test.d.ts","sourceRoot":"","sources":["../../src/core/WithInflightGuard.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,64 @@
1
+ import chai, { expect } from 'chai';
2
+ import chaiAsPromised from 'chai-as-promised';
3
+ import { ethers } from 'ethers';
4
+ import { pino } from 'pino';
5
+ import Sinon from 'sinon';
6
+ import { chainMetadata } from '@hyperlane-xyz/registry';
7
+ import { ChainMetadataManager } from '@hyperlane-xyz/sdk';
8
+ import { MockRebalancer, buildTestConfig } from '../test/helpers.js';
9
+ import { ExplorerClient } from '../utils/ExplorerClient.js';
10
+ import { WithInflightGuard } from './WithInflightGuard.js';
11
+ chai.use(chaiAsPromised);
12
+ const testLogger = pino({ level: 'silent' });
13
+ describe('WithInflightGuard', () => {
14
+ it('forwards empty routes without calling Explorer', async () => {
15
+ const config = buildTestConfig();
16
+ const rebalancer = new MockRebalancer();
17
+ const rebalanceSpy = Sinon.spy(rebalancer, 'rebalance');
18
+ const explorer = new ExplorerClient('http://localhost');
19
+ const explorerSpy = Sinon.stub(explorer, 'hasUndeliveredRebalance');
20
+ const guard = new WithInflightGuard(config, rebalancer, explorer, ethers.Wallet.createRandom().address, new ChainMetadataManager(chainMetadata), testLogger);
21
+ await guard.rebalance([]);
22
+ expect(explorerSpy.called).to.be.false;
23
+ expect(rebalanceSpy.calledOnce).to.be.true;
24
+ expect(rebalanceSpy.calledWith([])).to.be.true;
25
+ });
26
+ it('calls underlying rebalancer when no inflight is detected', async () => {
27
+ const config = buildTestConfig({}, ['ethereum', 'arbitrum']);
28
+ const routes = [{ origin: 'ethereum' }];
29
+ const rebalancer = new MockRebalancer();
30
+ const rebalanceSpy = Sinon.spy(rebalancer, 'rebalance');
31
+ const explorer = new ExplorerClient('http://localhost');
32
+ const explorerSpy = Sinon.stub(explorer, 'hasUndeliveredRebalance').resolves(false);
33
+ const guard = new WithInflightGuard(config, rebalancer, explorer, ethers.Wallet.createRandom().address, new ChainMetadataManager(chainMetadata), testLogger);
34
+ await guard.rebalance(routes);
35
+ expect(explorerSpy.calledOnce).to.be.true;
36
+ expect(rebalanceSpy.calledOnce).to.be.true;
37
+ expect(rebalanceSpy.calledWith(routes)).to.be.true;
38
+ });
39
+ it('skips rebalancing when inflight is detected', async () => {
40
+ const config = buildTestConfig({}, ['ethereum', 'arbitrum']);
41
+ const routes = [{ origin: 'ethereum' }];
42
+ const rebalancer = new MockRebalancer();
43
+ const rebalanceSpy = Sinon.spy(rebalancer, 'rebalance');
44
+ const explorer = new ExplorerClient('http://localhost');
45
+ const explorerSpy = Sinon.stub(explorer, 'hasUndeliveredRebalance').resolves(true);
46
+ const guard = new WithInflightGuard(config, rebalancer, explorer, ethers.Wallet.createRandom().address, new ChainMetadataManager(chainMetadata), testLogger);
47
+ await guard.rebalance(routes);
48
+ expect(explorerSpy.calledOnce).to.be.true;
49
+ expect(rebalanceSpy.called).to.be.false;
50
+ });
51
+ it('propagates explorer query error', async () => {
52
+ const config = buildTestConfig({}, ['ethereum', 'arbitrum']);
53
+ const routes = [{ origin: 'ethereum' }];
54
+ const rebalancer = new MockRebalancer();
55
+ const rebalanceSpy = Sinon.spy(rebalancer, 'rebalance');
56
+ const explorer = new ExplorerClient('http://localhost');
57
+ const explorerSpy = Sinon.stub(explorer, 'hasUndeliveredRebalance').rejects(new Error('Explorer HTTP 405'));
58
+ const guard = new WithInflightGuard(config, rebalancer, explorer, ethers.Wallet.createRandom().address, new ChainMetadataManager(chainMetadata), testLogger);
59
+ await expect(guard.rebalance(routes)).to.be.rejectedWith('Explorer HTTP 405');
60
+ expect(explorerSpy.calledOnce).to.be.true;
61
+ expect(rebalanceSpy.called).to.be.false;
62
+ });
63
+ });
64
+ //# sourceMappingURL=WithInflightGuard.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WithInflightGuard.test.js","sourceRoot":"","sources":["../../src/core/WithInflightGuard.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { Logger } from 'pino';
2
+ import { RebalancerConfig } from '../config/RebalancerConfig.js';
3
+ import type { IRebalancer } from '../interfaces/IRebalancer.js';
4
+ import type { RebalancingRoute } from '../interfaces/IStrategy.js';
5
+ /**
6
+ * Prevents frequent rebalancing operations while bridges complete.
7
+ */
8
+ export declare class WithSemaphore implements IRebalancer {
9
+ private readonly config;
10
+ private readonly rebalancer;
11
+ private waitUntil;
12
+ private executing;
13
+ private readonly logger;
14
+ constructor(config: RebalancerConfig, rebalancer: IRebalancer, logger: Logger);
15
+ /**
16
+ * Rebalance with timing control
17
+ * @param routes - Routes to process
18
+ */
19
+ rebalance(routes: RebalancingRoute[]): Promise<void>;
20
+ private getHighestLockTime;
21
+ }
22
+ //# sourceMappingURL=WithSemaphore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WithSemaphore.d.ts","sourceRoot":"","sources":["../../src/core/WithSemaphore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAEnE;;GAEG;AACH,qBAAa,aAAc,YAAW,WAAW;IAQ7C,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAP7B,OAAO,CAAC,SAAS,CAAa;IAE9B,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAGb,MAAM,EAAE,gBAAgB,EACxB,UAAU,EAAE,WAAW,EACxC,MAAM,EAAE,MAAM;IAKhB;;;OAGG;IACG,SAAS,CAAC,MAAM,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA+C1D,OAAO,CAAC,kBAAkB;CAgB3B"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Prevents frequent rebalancing operations while bridges complete.
3
+ */
4
+ export class WithSemaphore {
5
+ config;
6
+ rebalancer;
7
+ // Timestamp until which rebalancing should be blocked
8
+ waitUntil = 0;
9
+ // Lock to prevent concurrent rebalance execution
10
+ executing = false;
11
+ logger;
12
+ constructor(config, rebalancer, logger) {
13
+ this.config = config;
14
+ this.rebalancer = rebalancer;
15
+ this.logger = logger.child({ class: WithSemaphore.name });
16
+ }
17
+ /**
18
+ * Rebalance with timing control
19
+ * @param routes - Routes to process
20
+ */
21
+ async rebalance(routes) {
22
+ if (this.executing) {
23
+ this.logger.info('Currently executing rebalance. Skipping.');
24
+ return;
25
+ }
26
+ // No routes mean the system is balanced so we reset the timer to allow new rebalancing
27
+ if (!routes.length) {
28
+ this.logger.info('No routes to execute. Assuming rebalance is complete. Resetting semaphore timer.');
29
+ this.waitUntil = 0;
30
+ return;
31
+ }
32
+ // Skip if still in waiting period
33
+ if (Date.now() < this.waitUntil) {
34
+ this.logger.info('Still in waiting period. Skipping rebalance.');
35
+ return;
36
+ }
37
+ // The wait period will be determined by the bridge with the highest wait tolerance
38
+ const highestTolerance = this.getHighestLockTime(routes);
39
+ try {
40
+ // Execute rebalance
41
+ this.executing = true;
42
+ await this.rebalancer.rebalance(routes);
43
+ }
44
+ finally {
45
+ this.executing = false;
46
+ }
47
+ // Set new waiting period
48
+ this.waitUntil = Date.now() + highestTolerance;
49
+ this.logger.info({
50
+ highestTolerance,
51
+ waitUntil: this.waitUntil,
52
+ }, 'Rebalance semaphore locked');
53
+ }
54
+ getHighestLockTime(routes) {
55
+ return routes.reduce((highest, route) => {
56
+ const origin = this.config.strategyConfig.chains[route.origin];
57
+ if (!origin) {
58
+ this.logger.error({ route }, 'Chain not found in config. Skipping.');
59
+ throw new Error(`Chain ${route.origin} not found in config`);
60
+ }
61
+ const bridgeLockTime = origin.bridgeLockTime;
62
+ const overrideLockTime = origin.override?.[route.destination]?.bridgeLockTime ?? 0;
63
+ return Math.max(highest, bridgeLockTime, overrideLockTime);
64
+ }, 0);
65
+ }
66
+ }
67
+ //# sourceMappingURL=WithSemaphore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WithSemaphore.js","sourceRoot":"","sources":["../../src/core/WithSemaphore.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,MAAM,OAAO,aAAa;IAQL;IACA;IARnB,sDAAsD;IAC9C,SAAS,GAAW,CAAC,CAAC;IAC9B,iDAAiD;IACzC,SAAS,GAAY,KAAK,CAAC;IAClB,MAAM,CAAS;IAEhC,YACmB,MAAwB,EACxB,UAAuB,EACxC,MAAc;QAFG,WAAM,GAAN,MAAM,CAAkB;QACxB,eAAU,GAAV,UAAU,CAAa;QAGxC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,SAAS,CAAC,MAA0B;QACxC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAC;YAE7D,OAAO;QACT,CAAC;QAED,uFAAuF;QACvF,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACnB,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,kFAAkF,CACnF,CAAC;YAEF,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC;YACnB,OAAO;QACT,CAAC;QAED,kCAAkC;QAClC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;YAChC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;YAEjE,OAAO;QACT,CAAC;QAED,mFAAmF;QACnF,MAAM,gBAAgB,GAAG,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAEzD,IAAI,CAAC;YACH,oBAAoB;YACpB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;YACtB,MAAM,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC1C,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;QACzB,CAAC;QAED,yBAAyB;QACzB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,gBAAgB,CAAC;QAE/C,IAAI,CAAC,MAAM,CAAC,IAAI,CACd;YACE,gBAAgB;YAChB,SAAS,EAAE,IAAI,CAAC,SAAS;SAC1B,EACD,4BAA4B,CAC7B,CAAC;IACJ,CAAC;IAEO,kBAAkB,CAAC,MAA0B;QACnD,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;YACtC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAE/D,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,sCAAsC,CAAC,CAAC;gBACrE,MAAM,IAAI,KAAK,CAAC,SAAS,KAAK,CAAC,MAAM,sBAAsB,CAAC,CAAC;YAC/D,CAAC;YAED,MAAM,cAAc,GAAG,MAAM,CAAC,cAAc,CAAC;YAC7C,MAAM,gBAAgB,GACpB,MAAM,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,cAAc,IAAI,CAAC,CAAC;YAE5D,OAAO,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,cAAc,EAAE,gBAAgB,CAAC,CAAC;QAC7D,CAAC,EAAE,CAAC,CAAC,CAAC;IACR,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=WithSemaphore.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WithSemaphore.test.d.ts","sourceRoot":"","sources":["../../src/core/WithSemaphore.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,83 @@
1
+ import chai, { expect } from 'chai';
2
+ import chaiAsPromised from 'chai-as-promised';
3
+ import { pino } from 'pino';
4
+ import Sinon from 'sinon';
5
+ import { RebalancerStrategyOptions } from '@hyperlane-xyz/sdk';
6
+ import { MockRebalancer, buildTestConfig } from '../test/helpers.js';
7
+ import { WithSemaphore } from './WithSemaphore.js';
8
+ chai.use(chaiAsPromised);
9
+ const testLogger = pino({ level: 'silent' });
10
+ describe('WithSemaphore', () => {
11
+ it('should call the underlying rebalancer', async () => {
12
+ const config = buildTestConfig();
13
+ const routes = [
14
+ {
15
+ origin: 'chain1',
16
+ },
17
+ ];
18
+ const rebalancer = new MockRebalancer();
19
+ const rebalanceSpy = Sinon.spy(rebalancer, 'rebalance');
20
+ const withSemaphore = new WithSemaphore(config, rebalancer, testLogger);
21
+ await withSemaphore.rebalance(routes);
22
+ expect(rebalanceSpy.calledOnce).to.be.true;
23
+ expect(rebalanceSpy.calledWith(routes)).to.be.true;
24
+ });
25
+ it('should return early if there are no routes', async () => {
26
+ const config = buildTestConfig();
27
+ const rebalancer = new MockRebalancer();
28
+ const rebalanceSpy = Sinon.spy(rebalancer, 'rebalance');
29
+ const withSemaphore = new WithSemaphore(config, rebalancer, testLogger);
30
+ await withSemaphore.rebalance([]);
31
+ expect(rebalanceSpy.calledOnce).to.be.false;
32
+ });
33
+ it('should return early if rebalance occurs before waitUntil is reached', async () => {
34
+ const config = buildTestConfig();
35
+ const routes = [
36
+ {
37
+ origin: 'chain1',
38
+ },
39
+ ];
40
+ const rebalancer = new MockRebalancer();
41
+ const rebalanceSpy = Sinon.spy(rebalancer, 'rebalance');
42
+ const withSemaphore = new WithSemaphore(config, rebalancer, testLogger);
43
+ await withSemaphore.rebalance(routes);
44
+ expect(rebalanceSpy.calledOnce).to.be.true;
45
+ expect(rebalanceSpy.calledWith(routes)).to.be.true;
46
+ rebalanceSpy.resetHistory();
47
+ await withSemaphore.rebalance(routes);
48
+ expect(rebalanceSpy.calledOnce).to.be.false;
49
+ });
50
+ it('should throw if a chain is missing', async () => {
51
+ const config = buildTestConfig({
52
+ strategyConfig: {
53
+ rebalanceStrategy: RebalancerStrategyOptions.Weighted,
54
+ chains: {},
55
+ },
56
+ });
57
+ const routes = [
58
+ {
59
+ origin: 'chain1',
60
+ },
61
+ ];
62
+ const rebalancer = new MockRebalancer();
63
+ const withSemaphore = new WithSemaphore(config, rebalancer, testLogger);
64
+ await expect(withSemaphore.rebalance(routes)).to.be.rejectedWith(`Chain ${routes[0].origin} not found in config`);
65
+ });
66
+ it('should not execute if another rebalance is currently executing', async () => {
67
+ const config = buildTestConfig();
68
+ const routes = [
69
+ {
70
+ origin: 'chain1',
71
+ },
72
+ ];
73
+ const rebalancer = new MockRebalancer();
74
+ const rebalanceSpy = Sinon.spy(rebalancer, 'rebalance');
75
+ const withSemaphore = new WithSemaphore(config, rebalancer, testLogger);
76
+ const rebalancePromise1 = withSemaphore.rebalance(routes);
77
+ const rebalancePromise2 = withSemaphore.rebalance(routes);
78
+ await rebalancePromise1;
79
+ await rebalancePromise2;
80
+ expect(rebalanceSpy.calledOnce).to.be.true;
81
+ });
82
+ });
83
+ //# sourceMappingURL=WithSemaphore.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WithSemaphore.test.js","sourceRoot":"","sources":["../../src/core/WithSemaphore.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import { Logger } from 'pino';
2
+ import { IRegistry } from '@hyperlane-xyz/registry';
3
+ import { MultiProtocolProvider, MultiProvider, type Token, WarpCore } from '@hyperlane-xyz/sdk';
4
+ import { RebalancerConfig } from '../config/RebalancerConfig.js';
5
+ import type { IRebalancer } from '../interfaces/IRebalancer.js';
6
+ import type { IStrategy } from '../interfaces/IStrategy.js';
7
+ import { Metrics } from '../metrics/Metrics.js';
8
+ import { Monitor } from '../monitor/Monitor.js';
9
+ export declare class RebalancerContextFactory {
10
+ private readonly config;
11
+ private readonly warpCore;
12
+ private readonly tokensByChainName;
13
+ private readonly multiProvider;
14
+ private readonly registry;
15
+ private readonly logger;
16
+ /**
17
+ * @param config - The rebalancer config
18
+ * @param warpCore - An instance of `WarpCore` configured for the specified `warpRouteId`.
19
+ * @param tokensByChainName - A map of chain->token to ease the lookup of token by chain
20
+ * @param multiProvider - MultiProvider instance
21
+ * @param registry - IRegistry instance
22
+ * @param logger - Logger instance
23
+ */
24
+ private constructor();
25
+ /**
26
+ * @param config - The rebalancer config
27
+ * @param multiProvider - MultiProvider instance
28
+ * @param multiProtocolProvider - MultiProtocolProvider instance (optional, created from multiProvider if not provided)
29
+ * @param registry - IRegistry instance
30
+ * @param logger - Logger instance
31
+ */
32
+ static create(config: RebalancerConfig, multiProvider: MultiProvider, multiProtocolProvider: MultiProtocolProvider | undefined, registry: IRegistry, logger: Logger): Promise<RebalancerContextFactory>;
33
+ getWarpCore(): WarpCore;
34
+ getTokenForChain(chainName: string): Token | undefined;
35
+ createMetrics(coingeckoApiKey?: string): Promise<Metrics>;
36
+ createMonitor(checkFrequency: number): Monitor;
37
+ createStrategy(metrics?: Metrics): Promise<IStrategy>;
38
+ createRebalancer(metrics?: Metrics): IRebalancer;
39
+ private getInitialTotalCollateral;
40
+ }
41
+ //# sourceMappingURL=RebalancerContextFactory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RebalancerContextFactory.d.ts","sourceRoot":"","sources":["../../src/factories/RebalancerContextFactory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAE9B,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAEL,qBAAqB,EACrB,aAAa,EACb,KAAK,KAAK,EACV,QAAQ,EACT,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAGjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAEhD,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAIhD,qBAAa,wBAAwB;IAUjC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAdzB;;;;;;;OAOG;IACH,OAAO;IASP;;;;;;OAMG;WACiB,MAAM,CACxB,MAAM,EAAE,gBAAgB,EACxB,aAAa,EAAE,aAAa,EAC5B,qBAAqB,EAAE,qBAAqB,GAAG,SAAS,EACxD,QAAQ,EAAE,SAAS,EACnB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,wBAAwB,CAAC;IA8C7B,WAAW,IAAI,QAAQ;IAIvB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,KAAK,GAAG,SAAS;IAIhD,aAAa,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAuB/D,aAAa,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO;IAWxC,cAAc,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC;IAiB3D,gBAAgB,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,WAAW;YA8BzC,yBAAyB;CAoBxC"}
@@ -0,0 +1,115 @@
1
+ import { MultiProtocolProvider, WarpCore, } from '@hyperlane-xyz/sdk';
2
+ import { objMap } from '@hyperlane-xyz/utils';
3
+ import { Rebalancer } from '../core/Rebalancer.js';
4
+ import { WithSemaphore } from '../core/WithSemaphore.js';
5
+ import { Metrics } from '../metrics/Metrics.js';
6
+ import { PriceGetter } from '../metrics/PriceGetter.js';
7
+ import { Monitor } from '../monitor/Monitor.js';
8
+ import { StrategyFactory } from '../strategy/StrategyFactory.js';
9
+ import { isCollateralizedTokenEligibleForRebalancing } from '../utils/index.js';
10
+ export class RebalancerContextFactory {
11
+ config;
12
+ warpCore;
13
+ tokensByChainName;
14
+ multiProvider;
15
+ registry;
16
+ logger;
17
+ /**
18
+ * @param config - The rebalancer config
19
+ * @param warpCore - An instance of `WarpCore` configured for the specified `warpRouteId`.
20
+ * @param tokensByChainName - A map of chain->token to ease the lookup of token by chain
21
+ * @param multiProvider - MultiProvider instance
22
+ * @param registry - IRegistry instance
23
+ * @param logger - Logger instance
24
+ */
25
+ constructor(config, warpCore, tokensByChainName, multiProvider, registry, logger) {
26
+ this.config = config;
27
+ this.warpCore = warpCore;
28
+ this.tokensByChainName = tokensByChainName;
29
+ this.multiProvider = multiProvider;
30
+ this.registry = registry;
31
+ this.logger = logger;
32
+ }
33
+ /**
34
+ * @param config - The rebalancer config
35
+ * @param multiProvider - MultiProvider instance
36
+ * @param multiProtocolProvider - MultiProtocolProvider instance (optional, created from multiProvider if not provided)
37
+ * @param registry - IRegistry instance
38
+ * @param logger - Logger instance
39
+ */
40
+ static async create(config, multiProvider, multiProtocolProvider, registry, logger) {
41
+ logger.debug({
42
+ warpRouteId: config.warpRouteId,
43
+ }, 'Creating RebalancerContextFactory');
44
+ const addresses = await registry.getAddresses();
45
+ // The Sealevel warp adapters require the Mailbox address, so we
46
+ // get mailboxes for all chains and merge them with the chain metadata.
47
+ const mailboxes = objMap(addresses, (_, { mailbox }) => ({ mailbox }));
48
+ // Create MultiProtocolProvider (convert from MultiProvider if not provided)
49
+ const mpp = multiProtocolProvider ??
50
+ MultiProtocolProvider.fromMultiProvider(multiProvider);
51
+ const provider = mpp.extendChainMetadata(mailboxes);
52
+ const warpCoreConfig = await registry.getWarpRoute(config.warpRouteId);
53
+ if (!warpCoreConfig) {
54
+ throw new Error(`Warp route config for ${config.warpRouteId} not found in registry`);
55
+ }
56
+ const warpCore = WarpCore.FromConfig(provider, warpCoreConfig);
57
+ const tokensByChainName = Object.fromEntries(warpCore.tokens.map((t) => [t.chainName, t]));
58
+ logger.debug({
59
+ warpRouteId: config.warpRouteId,
60
+ }, 'RebalancerContextFactory created successfully');
61
+ return new RebalancerContextFactory(config, warpCore, tokensByChainName, multiProvider, registry, logger);
62
+ }
63
+ getWarpCore() {
64
+ return this.warpCore;
65
+ }
66
+ getTokenForChain(chainName) {
67
+ return this.tokensByChainName[chainName];
68
+ }
69
+ async createMetrics(coingeckoApiKey) {
70
+ this.logger.debug({ warpRouteId: this.config.warpRouteId }, 'Creating Metrics');
71
+ const tokenPriceGetter = PriceGetter.create(this.multiProvider.metadata, this.logger, coingeckoApiKey);
72
+ const warpDeployConfig = await this.registry.getWarpDeployConfig(this.config.warpRouteId);
73
+ return new Metrics(tokenPriceGetter, warpDeployConfig, this.warpCore, this.config.warpRouteId, this.logger);
74
+ }
75
+ createMonitor(checkFrequency) {
76
+ this.logger.debug({
77
+ warpRouteId: this.config.warpRouteId,
78
+ checkFrequency: checkFrequency,
79
+ }, 'Creating Monitor');
80
+ return new Monitor(checkFrequency, this.warpCore, this.logger);
81
+ }
82
+ async createStrategy(metrics) {
83
+ this.logger.debug({
84
+ warpRouteId: this.config.warpRouteId,
85
+ strategyType: this.config.strategyConfig.rebalanceStrategy,
86
+ }, 'Creating Strategy');
87
+ return StrategyFactory.createStrategy(this.config.strategyConfig, this.tokensByChainName, await this.getInitialTotalCollateral(), this.logger, metrics);
88
+ }
89
+ createRebalancer(metrics) {
90
+ this.logger.debug({ warpRouteId: this.config.warpRouteId }, 'Creating Rebalancer');
91
+ const rebalancer = new Rebalancer(objMap(this.config.strategyConfig.chains, (_, v) => ({
92
+ bridge: v.bridge,
93
+ bridgeMinAcceptedAmount: v.bridgeMinAcceptedAmount ?? 0,
94
+ bridgeIsWarp: v.bridgeIsWarp ?? false,
95
+ override: v.override,
96
+ })), this.warpCore, this.multiProvider.metadata, this.tokensByChainName, this.multiProvider, this.logger, metrics);
97
+ // Wrap with semaphore for concurrency control
98
+ const withSemaphore = new WithSemaphore(this.config, rebalancer, this.logger);
99
+ return withSemaphore;
100
+ }
101
+ async getInitialTotalCollateral() {
102
+ let initialTotalCollateral = 0n;
103
+ const chainNames = new Set(Object.keys(this.config.strategyConfig.chains));
104
+ await Promise.all(this.warpCore.tokens.map(async (token) => {
105
+ if (isCollateralizedTokenEligibleForRebalancing(token) &&
106
+ chainNames.has(token.chainName)) {
107
+ const adapter = token.getHypAdapter(this.warpCore.multiProvider);
108
+ const bridgedSupply = await adapter.getBridgedSupply();
109
+ initialTotalCollateral += bridgedSupply ?? 0n;
110
+ }
111
+ }));
112
+ return initialTotalCollateral;
113
+ }
114
+ }
115
+ //# sourceMappingURL=RebalancerContextFactory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RebalancerContextFactory.js","sourceRoot":"","sources":["../../src/factories/RebalancerContextFactory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
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
+ export { RebalancerService } from './core/RebalancerService.js';
10
+ export type { RebalancerServiceConfig, ManualRebalanceRequest, } from './core/RebalancerService.js';
11
+ export { Rebalancer } from './core/Rebalancer.js';
12
+ export { WithInflightGuard } from './core/WithInflightGuard.js';
13
+ export { WithSemaphore } from './core/WithSemaphore.js';
14
+ export { RebalancerConfig } from './config/RebalancerConfig.js';
15
+ export { BaseStrategy } from './strategy/BaseStrategy.js';
16
+ export { WeightedStrategy } from './strategy/WeightedStrategy.js';
17
+ export { MinAmountStrategy } from './strategy/MinAmountStrategy.js';
18
+ export { StrategyFactory } from './strategy/StrategyFactory.js';
19
+ export { Monitor } from './monitor/Monitor.js';
20
+ export { Metrics } from './metrics/Metrics.js';
21
+ export { PriceGetter } from './metrics/PriceGetter.js';
22
+ export type { IRebalancer, PreparedTransaction, } from './interfaces/IRebalancer.js';
23
+ export type { IStrategy, RebalancingRoute, RawBalances, } from './interfaces/IStrategy.js';
24
+ export type { IMonitor } from './interfaces/IMonitor.js';
25
+ export { MonitorEventType, MonitorEvent, MonitorPollingError, MonitorStartError, } from './interfaces/IMonitor.js';
26
+ export type { IMetrics } from './interfaces/IMetrics.js';
27
+ export { getBridgeConfig } from './utils/bridgeUtils.js';
28
+ export type { BridgeConfigWithOverride, BridgeConfig, } from './utils/bridgeUtils.js';
29
+ export { getRawBalances } from './utils/balanceUtils.js';
30
+ export { isCollateralizedTokenEligibleForRebalancing } from './utils/tokenUtils.js';
31
+ export { ExplorerClient } from './utils/ExplorerClient.js';
32
+ export { RebalancerContextFactory } from './factories/RebalancerContextFactory.js';
33
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,YAAY,EACV,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,6BAA6B,CAAC;AAGrC,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAGxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAGhE,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAGhE,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAG/C,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGvD,YAAY,EACV,WAAW,EACX,mBAAmB,GACpB,MAAM,6BAA6B,CAAC;AACrC,YAAY,EACV,SAAS,EACT,gBAAgB,EAChB,WAAW,GACZ,MAAM,2BAA2B,CAAC;AACnC,YAAY,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAGzD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,YAAY,EACV,wBAAwB,EACxB,YAAY,GACb,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,2CAA2C,EAAE,MAAM,uBAAuB,CAAC;AACpF,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAG3D,OAAO,EAAE,wBAAwB,EAAE,MAAM,yCAAyC,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,35 @@
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
+ // Core service
10
+ export { RebalancerService } from './core/RebalancerService.js';
11
+ // Core rebalancing logic
12
+ export { Rebalancer } from './core/Rebalancer.js';
13
+ export { WithInflightGuard } from './core/WithInflightGuard.js';
14
+ export { WithSemaphore } from './core/WithSemaphore.js';
15
+ // Configuration
16
+ export { RebalancerConfig } from './config/RebalancerConfig.js';
17
+ // Strategy
18
+ export { BaseStrategy } from './strategy/BaseStrategy.js';
19
+ export { WeightedStrategy } from './strategy/WeightedStrategy.js';
20
+ export { MinAmountStrategy } from './strategy/MinAmountStrategy.js';
21
+ export { StrategyFactory } from './strategy/StrategyFactory.js';
22
+ // Monitor
23
+ export { Monitor } from './monitor/Monitor.js';
24
+ // Metrics
25
+ export { Metrics } from './metrics/Metrics.js';
26
+ export { PriceGetter } from './metrics/PriceGetter.js';
27
+ export { MonitorEventType, MonitorPollingError, MonitorStartError, } from './interfaces/IMonitor.js';
28
+ // Utils
29
+ export { getBridgeConfig } from './utils/bridgeUtils.js';
30
+ export { getRawBalances } from './utils/balanceUtils.js';
31
+ export { isCollateralizedTokenEligibleForRebalancing } from './utils/tokenUtils.js';
32
+ export { ExplorerClient } from './utils/ExplorerClient.js';
33
+ // Factory
34
+ export { RebalancerContextFactory } from './factories/RebalancerContextFactory.js';
35
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,eAAe;AACf,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAMhE,yBAAyB;AACzB,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAExD,gBAAgB;AAChB,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAEhE,WAAW;AACX,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAEhE,UAAU;AACV,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAE/C,UAAU;AACV,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAavD,OAAO,EACL,gBAAgB,EAEhB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,0BAA0B,CAAC;AAGlC,QAAQ;AACR,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAKzD,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,2CAA2C,EAAE,MAAM,uBAAuB,CAAC;AACpF,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAE3D,UAAU;AACV,OAAO,EAAE,wBAAwB,EAAE,MAAM,yCAAyC,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { MonitorEvent } from './IMonitor.js';
2
+ export interface IMetrics {
3
+ processToken(tokenInfo: MonitorEvent['tokensInfo'][number]): Promise<void>;
4
+ }
5
+ //# sourceMappingURL=IMetrics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IMetrics.d.ts","sourceRoot":"","sources":["../../src/interfaces/IMetrics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,MAAM,WAAW,QAAQ;IACvB,YAAY,CAAC,SAAS,EAAE,YAAY,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5E"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=IMetrics.js.map