@hyperlane-xyz/rebalancer 0.1.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +134 -14
- package/dist/config/RebalancerConfig.d.ts +2 -2
- package/dist/config/RebalancerConfig.d.ts.map +1 -1
- package/dist/config/RebalancerConfig.js +4 -3
- package/dist/config/RebalancerConfig.js.map +1 -1
- package/dist/config/RebalancerConfig.test.js +434 -163
- package/dist/config/RebalancerConfig.test.js.map +1 -1
- package/dist/config/types.d.ts +1650 -290
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +124 -46
- package/dist/config/types.js.map +1 -1
- package/dist/core/Rebalancer.d.ts +14 -7
- package/dist/core/Rebalancer.d.ts.map +1 -1
- package/dist/core/Rebalancer.js +168 -99
- package/dist/core/Rebalancer.js.map +1 -1
- package/dist/core/Rebalancer.test.d.ts +2 -0
- package/dist/core/Rebalancer.test.d.ts.map +1 -0
- package/dist/core/Rebalancer.test.js +391 -0
- package/dist/core/Rebalancer.test.js.map +1 -0
- package/dist/core/RebalancerService.d.ts +16 -2
- package/dist/core/RebalancerService.d.ts.map +1 -1
- package/dist/core/RebalancerService.js +164 -21
- package/dist/core/RebalancerService.js.map +1 -1
- package/dist/core/RebalancerService.test.d.ts +2 -0
- package/dist/core/RebalancerService.test.d.ts.map +1 -0
- package/dist/core/RebalancerService.test.js +809 -0
- package/dist/core/RebalancerService.test.js.map +1 -0
- package/dist/factories/RebalancerContextFactory.d.ts +11 -0
- package/dist/factories/RebalancerContextFactory.d.ts.map +1 -1
- package/dist/factories/RebalancerContextFactory.js +60 -13
- package/dist/factories/RebalancerContextFactory.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/interfaces/IMonitor.d.ts +6 -8
- package/dist/interfaces/IMonitor.d.ts.map +1 -1
- package/dist/interfaces/IMonitor.js.map +1 -1
- package/dist/interfaces/IRebalancer.d.ts +20 -4
- package/dist/interfaces/IRebalancer.d.ts.map +1 -1
- package/dist/interfaces/IStrategy.d.ts +18 -2
- package/dist/interfaces/IStrategy.d.ts.map +1 -1
- package/dist/metrics/Metrics.d.ts +4 -2
- package/dist/metrics/Metrics.d.ts.map +1 -1
- package/dist/metrics/Metrics.js +21 -1
- package/dist/metrics/Metrics.js.map +1 -1
- package/dist/metrics/scripts/metrics.d.ts +2 -0
- package/dist/metrics/scripts/metrics.d.ts.map +1 -1
- package/dist/metrics/scripts/metrics.js +12 -0
- package/dist/metrics/scripts/metrics.js.map +1 -1
- package/dist/monitor/Monitor.d.ts +8 -3
- package/dist/monitor/Monitor.d.ts.map +1 -1
- package/dist/monitor/Monitor.js +75 -15
- package/dist/monitor/Monitor.js.map +1 -1
- package/dist/strategy/BaseStrategy.d.ts +51 -5
- package/dist/strategy/BaseStrategy.d.ts.map +1 -1
- package/dist/strategy/BaseStrategy.js +199 -19
- package/dist/strategy/BaseStrategy.js.map +1 -1
- package/dist/strategy/CollateralDeficitStrategy.d.ts +65 -0
- package/dist/strategy/CollateralDeficitStrategy.d.ts.map +1 -0
- package/dist/strategy/CollateralDeficitStrategy.js +245 -0
- package/dist/strategy/CollateralDeficitStrategy.js.map +1 -0
- package/dist/strategy/CollateralDeficitStrategy.test.d.ts +2 -0
- package/dist/strategy/CollateralDeficitStrategy.test.d.ts.map +1 -0
- package/dist/strategy/CollateralDeficitStrategy.test.js +364 -0
- package/dist/strategy/CollateralDeficitStrategy.test.js.map +1 -0
- package/dist/strategy/CompositeStrategy.d.ts +18 -0
- package/dist/strategy/CompositeStrategy.d.ts.map +1 -0
- package/dist/strategy/CompositeStrategy.js +63 -0
- package/dist/strategy/CompositeStrategy.js.map +1 -0
- package/dist/strategy/CompositeStrategy.test.d.ts +2 -0
- package/dist/strategy/CompositeStrategy.test.d.ts.map +1 -0
- package/dist/strategy/CompositeStrategy.test.js +265 -0
- package/dist/strategy/CompositeStrategy.test.js.map +1 -0
- package/dist/strategy/MinAmountStrategy.d.ts +12 -5
- package/dist/strategy/MinAmountStrategy.d.ts.map +1 -1
- package/dist/strategy/MinAmountStrategy.js +23 -14
- package/dist/strategy/MinAmountStrategy.js.map +1 -1
- package/dist/strategy/MinAmountStrategy.test.js +88 -20
- package/dist/strategy/MinAmountStrategy.test.js.map +1 -1
- package/dist/strategy/StrategyFactory.d.ts +15 -6
- package/dist/strategy/StrategyFactory.d.ts.map +1 -1
- package/dist/strategy/StrategyFactory.js +48 -10
- package/dist/strategy/StrategyFactory.js.map +1 -1
- package/dist/strategy/StrategyFactory.test.js +2 -2
- package/dist/strategy/StrategyFactory.test.js.map +1 -1
- package/dist/strategy/WeightedStrategy.d.ts +13 -4
- package/dist/strategy/WeightedStrategy.d.ts.map +1 -1
- package/dist/strategy/WeightedStrategy.js +18 -6
- package/dist/strategy/WeightedStrategy.js.map +1 -1
- package/dist/strategy/WeightedStrategy.test.js +108 -18
- package/dist/strategy/WeightedStrategy.test.js.map +1 -1
- package/dist/strategy/index.d.ts +2 -0
- package/dist/strategy/index.d.ts.map +1 -1
- package/dist/strategy/index.js +2 -0
- package/dist/strategy/index.js.map +1 -1
- package/dist/test/helpers.d.ts +93 -3
- package/dist/test/helpers.d.ts.map +1 -1
- package/dist/test/helpers.js +267 -10
- package/dist/test/helpers.js.map +1 -1
- package/dist/tracking/ActionTracker.d.ts +49 -0
- package/dist/tracking/ActionTracker.d.ts.map +1 -0
- package/dist/tracking/ActionTracker.js +422 -0
- package/dist/tracking/ActionTracker.js.map +1 -0
- package/dist/tracking/ActionTracker.test.d.ts +2 -0
- package/dist/tracking/ActionTracker.test.d.ts.map +1 -0
- package/dist/tracking/ActionTracker.test.js +637 -0
- package/dist/tracking/ActionTracker.test.js.map +1 -0
- package/dist/tracking/IActionTracker.d.ts +101 -0
- package/dist/tracking/IActionTracker.d.ts.map +1 -0
- package/dist/tracking/IActionTracker.js +2 -0
- package/dist/tracking/IActionTracker.js.map +1 -0
- package/dist/tracking/InflightContextAdapter.d.ts +18 -0
- package/dist/tracking/InflightContextAdapter.d.ts.map +1 -0
- package/dist/tracking/InflightContextAdapter.js +35 -0
- package/dist/tracking/InflightContextAdapter.js.map +1 -0
- package/dist/tracking/InflightContextAdapter.test.d.ts +2 -0
- package/dist/tracking/InflightContextAdapter.test.d.ts.map +1 -0
- package/dist/tracking/InflightContextAdapter.test.js +172 -0
- package/dist/tracking/InflightContextAdapter.test.js.map +1 -0
- package/dist/tracking/index.d.ts +7 -0
- package/dist/tracking/index.d.ts.map +1 -0
- package/dist/tracking/index.js +6 -0
- package/dist/tracking/index.js.map +1 -0
- package/dist/tracking/store/IStore.d.ts +41 -0
- package/dist/tracking/store/IStore.d.ts.map +1 -0
- package/dist/tracking/store/IStore.js +2 -0
- package/dist/tracking/store/IStore.js.map +1 -0
- package/dist/tracking/store/InMemoryStore.d.ts +21 -0
- package/dist/tracking/store/InMemoryStore.d.ts.map +1 -0
- package/dist/tracking/store/InMemoryStore.js +40 -0
- package/dist/tracking/store/InMemoryStore.js.map +1 -0
- package/dist/tracking/store/InMemoryStore.test.d.ts +2 -0
- package/dist/tracking/store/InMemoryStore.test.d.ts.map +1 -0
- package/dist/tracking/store/InMemoryStore.test.js +290 -0
- package/dist/tracking/store/InMemoryStore.test.js.map +1 -0
- package/dist/tracking/store/index.d.ts +3 -0
- package/dist/tracking/store/index.d.ts.map +1 -0
- package/dist/tracking/store/index.js +2 -0
- package/dist/tracking/store/index.js.map +1 -0
- package/dist/tracking/types.d.ts +43 -0
- package/dist/tracking/types.d.ts.map +1 -0
- package/dist/tracking/types.js +2 -0
- package/dist/tracking/types.js.map +1 -0
- package/dist/utils/ExplorerClient.d.ts +39 -1
- package/dist/utils/ExplorerClient.d.ts.map +1 -1
- package/dist/utils/ExplorerClient.js +205 -2
- package/dist/utils/ExplorerClient.js.map +1 -1
- package/dist/utils/balanceUtils.js +2 -2
- package/dist/utils/balanceUtils.js.map +1 -1
- package/dist/utils/balanceUtils.test.js +1 -0
- package/dist/utils/balanceUtils.test.js.map +1 -1
- package/dist/utils/bridgeUtils.d.ts +1 -3
- package/dist/utils/bridgeUtils.d.ts.map +1 -1
- package/dist/utils/bridgeUtils.js +1 -5
- package/dist/utils/bridgeUtils.js.map +1 -1
- package/dist/utils/bridgeUtils.test.js +3 -14
- package/dist/utils/bridgeUtils.test.js.map +1 -1
- package/package.json +11 -9
- package/src/config/RebalancerConfig.test.ts +459 -163
- package/src/config/RebalancerConfig.ts +5 -3
- package/src/config/types.ts +159 -52
- package/src/core/Rebalancer.test.ts +632 -0
- package/src/core/Rebalancer.ts +247 -157
- package/src/core/RebalancerService.test.ts +1144 -0
- package/src/core/RebalancerService.ts +245 -23
- package/src/factories/RebalancerContextFactory.ts +115 -14
- package/src/index.ts +16 -4
- package/src/interfaces/IMonitor.ts +15 -8
- package/src/interfaces/IRebalancer.ts +22 -4
- package/src/interfaces/IStrategy.ts +23 -2
- package/src/metrics/Metrics.ts +26 -5
- package/src/metrics/scripts/metrics.ts +14 -0
- package/src/monitor/Monitor.ts +109 -22
- package/src/strategy/BaseStrategy.ts +316 -26
- package/src/strategy/CollateralDeficitStrategy.test.ts +551 -0
- package/src/strategy/CollateralDeficitStrategy.ts +390 -0
- package/src/strategy/CompositeStrategy.test.ts +405 -0
- package/src/strategy/CompositeStrategy.ts +102 -0
- package/src/strategy/MinAmountStrategy.test.ts +189 -88
- package/src/strategy/MinAmountStrategy.ts +44 -13
- package/src/strategy/StrategyFactory.test.ts +2 -2
- package/src/strategy/StrategyFactory.ts +91 -8
- package/src/strategy/WeightedStrategy.test.ts +187 -72
- package/src/strategy/WeightedStrategy.ts +41 -7
- package/src/strategy/index.ts +2 -0
- package/src/test/helpers.ts +418 -14
- package/src/tracking/ActionTracker.test.ts +783 -0
- package/src/tracking/ActionTracker.ts +647 -0
- package/src/tracking/IActionTracker.ts +140 -0
- package/src/tracking/InflightContextAdapter.test.ts +203 -0
- package/src/tracking/InflightContextAdapter.ts +42 -0
- package/src/tracking/index.ts +36 -0
- package/src/tracking/store/IStore.ts +48 -0
- package/src/tracking/store/InMemoryStore.test.ts +338 -0
- package/src/tracking/store/InMemoryStore.ts +58 -0
- package/src/tracking/store/index.ts +2 -0
- package/src/tracking/types.ts +74 -0
- package/src/utils/ExplorerClient.ts +266 -3
- package/src/utils/balanceUtils.test.ts +1 -0
- package/src/utils/balanceUtils.ts +2 -2
- package/src/utils/bridgeUtils.test.ts +3 -15
- package/src/utils/bridgeUtils.ts +0 -10
- package/dist/core/WithInflightGuard.d.ts +0 -20
- package/dist/core/WithInflightGuard.d.ts.map +0 -1
- package/dist/core/WithInflightGuard.js +0 -47
- package/dist/core/WithInflightGuard.js.map +0 -1
- package/dist/core/WithInflightGuard.test.d.ts +0 -2
- package/dist/core/WithInflightGuard.test.d.ts.map +0 -1
- package/dist/core/WithInflightGuard.test.js +0 -64
- package/dist/core/WithInflightGuard.test.js.map +0 -1
- package/dist/core/WithSemaphore.d.ts +0 -22
- package/dist/core/WithSemaphore.d.ts.map +0 -1
- package/dist/core/WithSemaphore.js +0 -67
- package/dist/core/WithSemaphore.js.map +0 -1
- package/dist/core/WithSemaphore.test.d.ts +0 -2
- package/dist/core/WithSemaphore.test.d.ts.map +0 -1
- package/dist/core/WithSemaphore.test.js +0 -83
- package/dist/core/WithSemaphore.test.js.map +0 -1
- package/src/core/WithInflightGuard.test.ts +0 -131
- package/src/core/WithInflightGuard.ts +0 -67
- package/src/core/WithSemaphore.test.ts +0 -111
- package/src/core/WithSemaphore.ts +0 -92
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { randomUUID } from 'crypto';
|
|
1
2
|
import { Logger } from 'pino';
|
|
2
3
|
|
|
3
4
|
import { IRegistry } from '@hyperlane-xyz/registry';
|
|
@@ -9,17 +10,34 @@ import {
|
|
|
9
10
|
import { assert, toWei } from '@hyperlane-xyz/utils';
|
|
10
11
|
|
|
11
12
|
import { RebalancerConfig } from '../config/RebalancerConfig.js';
|
|
13
|
+
import {
|
|
14
|
+
getStrategyChainConfig,
|
|
15
|
+
getStrategyChainNames,
|
|
16
|
+
} from '../config/types.js';
|
|
12
17
|
import { RebalancerContextFactory } from '../factories/RebalancerContextFactory.js';
|
|
13
18
|
import {
|
|
19
|
+
type ConfirmedBlockTags,
|
|
14
20
|
MonitorEvent,
|
|
15
21
|
MonitorEventType,
|
|
16
22
|
MonitorPollingError,
|
|
17
23
|
MonitorStartError,
|
|
18
24
|
} from '../interfaces/IMonitor.js';
|
|
19
|
-
import type {
|
|
20
|
-
|
|
25
|
+
import type {
|
|
26
|
+
IRebalancer,
|
|
27
|
+
RebalanceExecutionResult,
|
|
28
|
+
RebalanceRoute,
|
|
29
|
+
} from '../interfaces/IRebalancer.js';
|
|
30
|
+
import type {
|
|
31
|
+
IStrategy,
|
|
32
|
+
InflightContext,
|
|
33
|
+
StrategyRoute,
|
|
34
|
+
} from '../interfaces/IStrategy.js';
|
|
21
35
|
import { Metrics } from '../metrics/Metrics.js';
|
|
22
36
|
import { Monitor } from '../monitor/Monitor.js';
|
|
37
|
+
import {
|
|
38
|
+
type IActionTracker,
|
|
39
|
+
InflightContextAdapter,
|
|
40
|
+
} from '../tracking/index.js';
|
|
23
41
|
import { getRawBalances } from '../utils/balanceUtils.js';
|
|
24
42
|
|
|
25
43
|
export interface RebalancerServiceConfig {
|
|
@@ -101,6 +119,8 @@ export class RebalancerService {
|
|
|
101
119
|
private rebalancer?: IRebalancer;
|
|
102
120
|
private metrics?: Metrics;
|
|
103
121
|
private mode: 'manual' | 'daemon';
|
|
122
|
+
private actionTracker?: IActionTracker;
|
|
123
|
+
private inflightContextAdapter?: InflightContextAdapter;
|
|
104
124
|
|
|
105
125
|
constructor(
|
|
106
126
|
private readonly multiProvider: MultiProvider,
|
|
@@ -159,7 +179,24 @@ export class RebalancerService {
|
|
|
159
179
|
);
|
|
160
180
|
}
|
|
161
181
|
|
|
162
|
-
|
|
182
|
+
// Create ActionTracker for tracking inflight actions
|
|
183
|
+
const { tracker, adapter } =
|
|
184
|
+
await this.contextFactory.createActionTracker();
|
|
185
|
+
this.actionTracker = tracker;
|
|
186
|
+
this.inflightContextAdapter = adapter;
|
|
187
|
+
await this.actionTracker.initialize();
|
|
188
|
+
this.logger.info('ActionTracker initialized');
|
|
189
|
+
|
|
190
|
+
this.logger.info(
|
|
191
|
+
{
|
|
192
|
+
warpRouteId: this.rebalancerConfig.warpRouteId,
|
|
193
|
+
strategyTypes: this.rebalancerConfig.strategyConfig.map(
|
|
194
|
+
(s) => s.rebalanceStrategy,
|
|
195
|
+
),
|
|
196
|
+
chains: getStrategyChainNames(this.rebalancerConfig.strategyConfig),
|
|
197
|
+
},
|
|
198
|
+
'RebalancerService initialized',
|
|
199
|
+
);
|
|
163
200
|
}
|
|
164
201
|
|
|
165
202
|
/**
|
|
@@ -195,14 +232,28 @@ export class RebalancerService {
|
|
|
195
232
|
assert(!isNaN(amountNum), 'Amount must be a valid number');
|
|
196
233
|
assert(amountNum > 0, 'Amount must be greater than 0');
|
|
197
234
|
|
|
235
|
+
const originConfig = getStrategyChainConfig(
|
|
236
|
+
this.rebalancerConfig.strategyConfig,
|
|
237
|
+
origin,
|
|
238
|
+
);
|
|
239
|
+
assert(
|
|
240
|
+
originConfig?.bridge,
|
|
241
|
+
`No bridge configured for origin chain ${origin}`,
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
// Use destination-specific bridge override if configured, otherwise use default
|
|
245
|
+
const bridge =
|
|
246
|
+
originConfig.override?.[destination]?.bridge ?? originConfig.bridge;
|
|
247
|
+
|
|
198
248
|
try {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
249
|
+
const route: RebalanceRoute = {
|
|
250
|
+
intentId: randomUUID(),
|
|
251
|
+
origin,
|
|
252
|
+
destination,
|
|
253
|
+
amount: BigInt(toWei(amount, originToken.decimals)),
|
|
254
|
+
bridge,
|
|
255
|
+
};
|
|
256
|
+
await this.rebalancer.rebalance([route]);
|
|
206
257
|
this.logger.info(
|
|
207
258
|
`✅ Manual rebalance from ${origin} to ${destination} for amount ${amount} submitted successfully.`,
|
|
208
259
|
);
|
|
@@ -274,10 +325,9 @@ export class RebalancerService {
|
|
|
274
325
|
process.exit(0);
|
|
275
326
|
}
|
|
276
327
|
|
|
277
|
-
/**
|
|
278
|
-
* Event handler for token info updates from monitor
|
|
279
|
-
*/
|
|
280
328
|
private async onTokenInfo(event: MonitorEvent): Promise<void> {
|
|
329
|
+
this.logger.info('Polling cycle started');
|
|
330
|
+
|
|
281
331
|
if (this.metrics) {
|
|
282
332
|
await Promise.all(
|
|
283
333
|
event.tokensInfo.map((tokenInfo) =>
|
|
@@ -286,24 +336,196 @@ export class RebalancerService {
|
|
|
286
336
|
);
|
|
287
337
|
}
|
|
288
338
|
|
|
339
|
+
await this.syncActionTracker(event.confirmedBlockTags);
|
|
340
|
+
|
|
289
341
|
const rawBalances = getRawBalances(
|
|
290
|
-
|
|
342
|
+
getStrategyChainNames(this.rebalancerConfig.strategyConfig),
|
|
291
343
|
event,
|
|
292
344
|
this.logger,
|
|
293
345
|
);
|
|
294
346
|
|
|
295
|
-
|
|
347
|
+
this.logger.info(
|
|
348
|
+
{
|
|
349
|
+
balances: Object.entries(rawBalances).map(([chain, balance]) => ({
|
|
350
|
+
chain,
|
|
351
|
+
balance: balance.toString(),
|
|
352
|
+
})),
|
|
353
|
+
},
|
|
354
|
+
'Router balances',
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
// Get inflight context for strategy decision-making
|
|
358
|
+
const inflightContext = await this.getInflightContext();
|
|
296
359
|
|
|
297
|
-
this.
|
|
298
|
-
|
|
299
|
-
|
|
360
|
+
const strategyRoutes = this.strategy!.getRebalancingRoutes(
|
|
361
|
+
rawBalances,
|
|
362
|
+
inflightContext,
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
if (strategyRoutes.length > 0) {
|
|
366
|
+
this.logger.info(
|
|
367
|
+
{
|
|
368
|
+
routes: strategyRoutes.map((r) => ({
|
|
369
|
+
from: r.origin,
|
|
370
|
+
to: r.destination,
|
|
371
|
+
amount: r.amount.toString(),
|
|
372
|
+
})),
|
|
373
|
+
},
|
|
374
|
+
'Routes proposed',
|
|
375
|
+
);
|
|
376
|
+
if (this.rebalancer) {
|
|
377
|
+
await this.executeWithTracking(strategyRoutes);
|
|
378
|
+
}
|
|
379
|
+
} else {
|
|
380
|
+
this.logger.info('No rebalancing needed');
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
this.logger.info('Polling cycle completed');
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
private async syncActionTracker(
|
|
387
|
+
confirmedBlockTags?: ConfirmedBlockTags,
|
|
388
|
+
): Promise<void> {
|
|
389
|
+
if (!this.actionTracker) return;
|
|
390
|
+
|
|
391
|
+
try {
|
|
392
|
+
await Promise.all([
|
|
393
|
+
this.actionTracker.syncTransfers(confirmedBlockTags),
|
|
394
|
+
this.actionTracker.syncRebalanceIntents(),
|
|
395
|
+
this.actionTracker.syncRebalanceActions(confirmedBlockTags),
|
|
396
|
+
]);
|
|
397
|
+
|
|
398
|
+
await this.actionTracker.logStoreContents();
|
|
399
|
+
} catch (error) {
|
|
400
|
+
this.logger.warn(
|
|
401
|
+
{ error },
|
|
402
|
+
'ActionTracker sync failed, using stale data',
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Get inflight context for strategy decision-making
|
|
409
|
+
*/
|
|
410
|
+
private async getInflightContext(): Promise<InflightContext> {
|
|
411
|
+
if (!this.inflightContextAdapter) {
|
|
412
|
+
return { pendingRebalances: [], pendingTransfers: [] };
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return this.inflightContextAdapter.getInflightContext();
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Execute rebalancing with intent tracking.
|
|
420
|
+
* Creates intents and assigns IDs to routes before execution, then processes results by ID.
|
|
421
|
+
*/
|
|
422
|
+
private async executeWithTracking(
|
|
423
|
+
strategyRoutes: StrategyRoute[],
|
|
424
|
+
): Promise<void> {
|
|
425
|
+
if (!this.rebalancer || !this.actionTracker) {
|
|
426
|
+
this.logger.warn('Rebalancer or ActionTracker not available, skipping');
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// 1. Convert strategy routes to rebalance routes with IDs and create intents
|
|
431
|
+
// The route ID is used as the intent ID for direct matching
|
|
432
|
+
const rebalanceRoutes: RebalanceRoute[] = [];
|
|
433
|
+
const intentIds: string[] = [];
|
|
434
|
+
|
|
435
|
+
for (const route of strategyRoutes) {
|
|
436
|
+
const intent = await this.actionTracker.createRebalanceIntent({
|
|
437
|
+
origin: this.multiProvider.getDomainId(route.origin),
|
|
438
|
+
destination: this.multiProvider.getDomainId(route.destination),
|
|
439
|
+
amount: route.amount,
|
|
440
|
+
bridge: route.bridge,
|
|
441
|
+
});
|
|
442
|
+
intentIds.push(intent.id);
|
|
443
|
+
rebalanceRoutes.push({
|
|
444
|
+
...route,
|
|
445
|
+
intentId: intent.id,
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
this.logger.debug(
|
|
450
|
+
{ intentCount: rebalanceRoutes.length },
|
|
451
|
+
'Created rebalance intents',
|
|
452
|
+
);
|
|
453
|
+
|
|
454
|
+
// 2. Execute rebalance with routes that have IDs
|
|
455
|
+
let results: RebalanceExecutionResult[];
|
|
456
|
+
try {
|
|
457
|
+
results = await this.rebalancer.rebalance(rebalanceRoutes);
|
|
458
|
+
const failedResults = results.filter((r) => !r.success);
|
|
459
|
+
if (failedResults.length > 0) {
|
|
460
|
+
this.metrics?.recordRebalancerFailure();
|
|
461
|
+
this.logger.warn(
|
|
462
|
+
{ failureCount: failedResults.length, total: results.length },
|
|
463
|
+
'Rebalancer cycle completed with failures',
|
|
464
|
+
);
|
|
465
|
+
} else {
|
|
300
466
|
this.metrics?.recordRebalancerSuccess();
|
|
301
467
|
this.logger.info('Rebalancer completed a cycle successfully');
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
468
|
+
}
|
|
469
|
+
} catch (error: any) {
|
|
470
|
+
this.metrics?.recordRebalancerFailure();
|
|
471
|
+
this.logger.error({ error }, 'Error while rebalancing');
|
|
472
|
+
|
|
473
|
+
// Mark all intents as failed
|
|
474
|
+
await Promise.all(
|
|
475
|
+
intentIds.map((id) => this.actionTracker!.failRebalanceIntent(id)),
|
|
476
|
+
);
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// 3. Process results - results have IDs that match intents directly
|
|
481
|
+
await this.processExecutionResults(results);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Process execution results and update tracking state.
|
|
486
|
+
* Results are matched to intents by the route ID (which equals the intent ID).
|
|
487
|
+
*/
|
|
488
|
+
private async processExecutionResults(
|
|
489
|
+
results: RebalanceExecutionResult[],
|
|
490
|
+
): Promise<void> {
|
|
491
|
+
for (const result of results) {
|
|
492
|
+
const intentId = result.route.intentId;
|
|
493
|
+
|
|
494
|
+
if (result.success && result.messageId) {
|
|
495
|
+
await this.actionTracker!.createRebalanceAction({
|
|
496
|
+
intentId,
|
|
497
|
+
origin: this.multiProvider.getDomainId(result.route.origin),
|
|
498
|
+
destination: this.multiProvider.getDomainId(result.route.destination),
|
|
499
|
+
amount: result.route.amount,
|
|
500
|
+
messageId: result.messageId,
|
|
501
|
+
txHash: result.txHash,
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
this.logger.info(
|
|
505
|
+
{
|
|
506
|
+
intentId,
|
|
507
|
+
messageId: result.messageId,
|
|
508
|
+
txHash: result.txHash,
|
|
509
|
+
origin: result.route.origin,
|
|
510
|
+
destination: result.route.destination,
|
|
511
|
+
},
|
|
512
|
+
'Rebalance action created successfully',
|
|
513
|
+
);
|
|
514
|
+
} else {
|
|
515
|
+
await this.actionTracker!.failRebalanceIntent(intentId);
|
|
516
|
+
|
|
517
|
+
this.logger.warn(
|
|
518
|
+
{
|
|
519
|
+
intentId,
|
|
520
|
+
success: result.success,
|
|
521
|
+
error: result.error,
|
|
522
|
+
origin: result.route.origin,
|
|
523
|
+
destination: result.route.destination,
|
|
524
|
+
},
|
|
525
|
+
'Rebalance intent marked as failed',
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
307
529
|
}
|
|
308
530
|
|
|
309
531
|
/**
|
|
@@ -3,6 +3,7 @@ import { type Logger } from 'pino';
|
|
|
3
3
|
import { IRegistry } from '@hyperlane-xyz/registry';
|
|
4
4
|
import {
|
|
5
5
|
type ChainMap,
|
|
6
|
+
HyperlaneCore,
|
|
6
7
|
MultiProtocolProvider,
|
|
7
8
|
MultiProvider,
|
|
8
9
|
type Token,
|
|
@@ -11,16 +12,33 @@ import {
|
|
|
11
12
|
import { objMap } from '@hyperlane-xyz/utils';
|
|
12
13
|
|
|
13
14
|
import { type RebalancerConfig } from '../config/RebalancerConfig.js';
|
|
15
|
+
import { getAllBridges, getStrategyChainNames } from '../config/types.js';
|
|
14
16
|
import { Rebalancer } from '../core/Rebalancer.js';
|
|
15
|
-
import { WithSemaphore } from '../core/WithSemaphore.js';
|
|
16
17
|
import type { IRebalancer } from '../interfaces/IRebalancer.js';
|
|
17
18
|
import type { IStrategy } from '../interfaces/IStrategy.js';
|
|
18
19
|
import { Metrics } from '../metrics/Metrics.js';
|
|
19
20
|
import { PriceGetter } from '../metrics/PriceGetter.js';
|
|
20
21
|
import { Monitor } from '../monitor/Monitor.js';
|
|
21
22
|
import { StrategyFactory } from '../strategy/StrategyFactory.js';
|
|
23
|
+
import {
|
|
24
|
+
ActionTracker,
|
|
25
|
+
type ActionTrackerConfig,
|
|
26
|
+
type IActionTracker,
|
|
27
|
+
InMemoryStore,
|
|
28
|
+
InflightContextAdapter,
|
|
29
|
+
type RebalanceAction,
|
|
30
|
+
type RebalanceActionStatus,
|
|
31
|
+
type RebalanceIntent,
|
|
32
|
+
type RebalanceIntentStatus,
|
|
33
|
+
type Transfer,
|
|
34
|
+
type TransferStatus,
|
|
35
|
+
} from '../tracking/index.js';
|
|
36
|
+
import { ExplorerClient } from '../utils/ExplorerClient.js';
|
|
22
37
|
import { isCollateralizedTokenEligibleForRebalancing } from '../utils/index.js';
|
|
23
38
|
|
|
39
|
+
const DEFAULT_EXPLORER_URL =
|
|
40
|
+
process.env.EXPLORER_API_URL || 'https://explorer4.hasura.app/v1/graphql';
|
|
41
|
+
|
|
24
42
|
export class RebalancerContextFactory {
|
|
25
43
|
/**
|
|
26
44
|
* @param config - The rebalancer config
|
|
@@ -141,10 +159,14 @@ export class RebalancerContextFactory {
|
|
|
141
159
|
}
|
|
142
160
|
|
|
143
161
|
public async createStrategy(metrics?: Metrics): Promise<IStrategy> {
|
|
162
|
+
const strategyTypes = this.config.strategyConfig.map(
|
|
163
|
+
(s) => s.rebalanceStrategy,
|
|
164
|
+
);
|
|
144
165
|
this.logger.debug(
|
|
145
166
|
{
|
|
146
167
|
warpRouteId: this.config.warpRouteId,
|
|
147
|
-
|
|
168
|
+
strategyTypes,
|
|
169
|
+
strategyCount: this.config.strategyConfig.length,
|
|
148
170
|
},
|
|
149
171
|
'Creating Strategy',
|
|
150
172
|
);
|
|
@@ -162,13 +184,8 @@ export class RebalancerContextFactory {
|
|
|
162
184
|
{ warpRouteId: this.config.warpRouteId },
|
|
163
185
|
'Creating Rebalancer',
|
|
164
186
|
);
|
|
187
|
+
|
|
165
188
|
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
189
|
this.warpCore,
|
|
173
190
|
this.multiProvider.metadata,
|
|
174
191
|
this.tokensByChainName,
|
|
@@ -177,20 +194,104 @@ export class RebalancerContextFactory {
|
|
|
177
194
|
metrics,
|
|
178
195
|
);
|
|
179
196
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
197
|
+
return rebalancer;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Creates an ActionTracker for tracking inflight rebalance actions and user transfers.
|
|
202
|
+
* Returns both the tracker and adapter for use by RebalancerService.
|
|
203
|
+
*
|
|
204
|
+
* @param explorerUrl - Optional explorer URL (defaults to production Hyperlane Explorer)
|
|
205
|
+
*/
|
|
206
|
+
public async createActionTracker(
|
|
207
|
+
explorerUrl: string = DEFAULT_EXPLORER_URL,
|
|
208
|
+
): Promise<{
|
|
209
|
+
tracker: IActionTracker;
|
|
210
|
+
adapter: InflightContextAdapter;
|
|
211
|
+
}> {
|
|
212
|
+
this.logger.debug(
|
|
213
|
+
{ warpRouteId: this.config.warpRouteId },
|
|
214
|
+
'Creating ActionTracker',
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
// 1. Create in-memory stores
|
|
218
|
+
const transferStore = new InMemoryStore<Transfer, TransferStatus>();
|
|
219
|
+
const intentStore = new InMemoryStore<
|
|
220
|
+
RebalanceIntent,
|
|
221
|
+
RebalanceIntentStatus
|
|
222
|
+
>();
|
|
223
|
+
const actionStore = new InMemoryStore<
|
|
224
|
+
RebalanceAction,
|
|
225
|
+
RebalanceActionStatus
|
|
226
|
+
>();
|
|
227
|
+
|
|
228
|
+
// 2. Create ExplorerClient
|
|
229
|
+
const explorerClient = new ExplorerClient(explorerUrl);
|
|
230
|
+
|
|
231
|
+
// 3. Get HyperlaneCore from registry
|
|
232
|
+
const addresses = await this.registry.getAddresses();
|
|
233
|
+
const hyperlaneCore = HyperlaneCore.fromAddressesMap(
|
|
234
|
+
addresses,
|
|
235
|
+
this.multiProvider,
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
// 4. Get rebalancer address from signer
|
|
239
|
+
// Use the first chain in the strategy to get the signer address
|
|
240
|
+
const chainNames = getStrategyChainNames(this.config.strategyConfig);
|
|
241
|
+
if (chainNames.length === 0) {
|
|
242
|
+
throw new Error('No chains configured in strategy');
|
|
243
|
+
}
|
|
244
|
+
const signer = this.multiProvider.getSigner(chainNames[0]);
|
|
245
|
+
const rebalancerAddress = await signer.getAddress();
|
|
246
|
+
|
|
247
|
+
const bridges = getAllBridges(this.config.strategyConfig);
|
|
248
|
+
|
|
249
|
+
// Build router→domain mapping (source of truth for routers and domains)
|
|
250
|
+
const routersByDomain: Record<number, string> = {};
|
|
251
|
+
for (const token of this.warpCore.tokens) {
|
|
252
|
+
const domain = this.multiProvider.getDomainId(token.chainName);
|
|
253
|
+
routersByDomain[domain] = token.addressOrDenom;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const trackerConfig: ActionTrackerConfig = {
|
|
257
|
+
routersByDomain,
|
|
258
|
+
bridges,
|
|
259
|
+
rebalancerAddress,
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
// 6. Create ActionTracker
|
|
263
|
+
const tracker = new ActionTracker(
|
|
264
|
+
transferStore,
|
|
265
|
+
intentStore,
|
|
266
|
+
actionStore,
|
|
267
|
+
explorerClient,
|
|
268
|
+
hyperlaneCore,
|
|
269
|
+
trackerConfig,
|
|
184
270
|
this.logger,
|
|
185
271
|
);
|
|
186
272
|
|
|
187
|
-
|
|
273
|
+
// 7. Create InflightContextAdapter
|
|
274
|
+
const adapter = new InflightContextAdapter(tracker, this.multiProvider);
|
|
275
|
+
|
|
276
|
+
this.logger.debug(
|
|
277
|
+
{
|
|
278
|
+
warpRouteId: this.config.warpRouteId,
|
|
279
|
+
routerCount: Object.keys(routersByDomain).length,
|
|
280
|
+
bridgeCount: bridges.length,
|
|
281
|
+
domainCount: Object.keys(routersByDomain).length,
|
|
282
|
+
},
|
|
283
|
+
'ActionTracker created successfully',
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
return { tracker, adapter };
|
|
188
287
|
}
|
|
189
288
|
|
|
190
289
|
private async getInitialTotalCollateral(): Promise<bigint> {
|
|
191
290
|
let initialTotalCollateral = 0n;
|
|
192
291
|
|
|
193
|
-
const chainNames = new Set(
|
|
292
|
+
const chainNames = new Set(
|
|
293
|
+
getStrategyChainNames(this.config.strategyConfig),
|
|
294
|
+
);
|
|
194
295
|
|
|
195
296
|
await Promise.all(
|
|
196
297
|
this.warpCore.tokens.map(async (token) => {
|
package/src/index.ts
CHANGED
|
@@ -16,17 +16,18 @@ export type {
|
|
|
16
16
|
|
|
17
17
|
// Core rebalancing logic
|
|
18
18
|
export { Rebalancer } from './core/Rebalancer.js';
|
|
19
|
-
export { WithInflightGuard } from './core/WithInflightGuard.js';
|
|
20
|
-
export { WithSemaphore } from './core/WithSemaphore.js';
|
|
21
19
|
|
|
22
20
|
// Configuration
|
|
23
21
|
export { RebalancerConfig } from './config/RebalancerConfig.js';
|
|
24
22
|
export {
|
|
23
|
+
getStrategyChainConfig,
|
|
24
|
+
getStrategyChainNames,
|
|
25
25
|
RebalancerBaseChainConfigSchema,
|
|
26
26
|
RebalancerConfigSchema,
|
|
27
27
|
RebalancerMinAmountConfigSchema,
|
|
28
28
|
RebalancerMinAmountType,
|
|
29
29
|
RebalancerStrategyOptions,
|
|
30
|
+
RebalancerStrategySchema,
|
|
30
31
|
RebalancerWeightedChainConfigSchema,
|
|
31
32
|
StrategyConfigSchema,
|
|
32
33
|
} from './config/types.js';
|
|
@@ -42,6 +43,7 @@ export type {
|
|
|
42
43
|
|
|
43
44
|
// Strategy
|
|
44
45
|
export { BaseStrategy } from './strategy/BaseStrategy.js';
|
|
46
|
+
export { CompositeStrategy } from './strategy/CompositeStrategy.js';
|
|
45
47
|
export { WeightedStrategy } from './strategy/WeightedStrategy.js';
|
|
46
48
|
export { MinAmountStrategy } from './strategy/MinAmountStrategy.js';
|
|
47
49
|
export { StrategyFactory } from './strategy/StrategyFactory.js';
|
|
@@ -57,13 +59,20 @@ export { PriceGetter } from './metrics/PriceGetter.js';
|
|
|
57
59
|
export type {
|
|
58
60
|
IRebalancer,
|
|
59
61
|
PreparedTransaction,
|
|
62
|
+
RebalanceRoute,
|
|
63
|
+
RebalanceExecutionResult,
|
|
60
64
|
} from './interfaces/IRebalancer.js';
|
|
61
65
|
export type {
|
|
62
66
|
IStrategy,
|
|
63
|
-
|
|
67
|
+
StrategyRoute,
|
|
64
68
|
RawBalances,
|
|
69
|
+
InflightContext,
|
|
65
70
|
} from './interfaces/IStrategy.js';
|
|
66
|
-
export type {
|
|
71
|
+
export type {
|
|
72
|
+
ConfirmedBlockTag,
|
|
73
|
+
ConfirmedBlockTags,
|
|
74
|
+
IMonitor,
|
|
75
|
+
} from './interfaces/IMonitor.js';
|
|
67
76
|
export {
|
|
68
77
|
MonitorEventType,
|
|
69
78
|
MonitorEvent,
|
|
@@ -82,5 +91,8 @@ export { getRawBalances } from './utils/balanceUtils.js';
|
|
|
82
91
|
export { isCollateralizedTokenEligibleForRebalancing } from './utils/tokenUtils.js';
|
|
83
92
|
export { ExplorerClient } from './utils/ExplorerClient.js';
|
|
84
93
|
|
|
94
|
+
// Tracking
|
|
95
|
+
export { InflightContextAdapter } from './tracking/InflightContextAdapter.js';
|
|
96
|
+
|
|
85
97
|
// Factory
|
|
86
98
|
export { RebalancerContextFactory } from './factories/RebalancerContextFactory.js';
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type ChainMap,
|
|
3
|
+
EthJsonRpcBlockParameterTag,
|
|
4
|
+
type Token,
|
|
5
|
+
} from '@hyperlane-xyz/sdk';
|
|
2
6
|
|
|
3
7
|
import { WrappedError } from '../utils/errors.js';
|
|
4
8
|
|
|
@@ -16,17 +20,19 @@ export enum MonitorEventType {
|
|
|
16
20
|
Start = 'Start',
|
|
17
21
|
}
|
|
18
22
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
export type ConfirmedBlockTag =
|
|
24
|
+
| number
|
|
25
|
+
| EthJsonRpcBlockParameterTag
|
|
26
|
+
| undefined;
|
|
27
|
+
|
|
28
|
+
export type ConfirmedBlockTags = ChainMap<ConfirmedBlockTag>;
|
|
29
|
+
|
|
22
30
|
export type MonitorEvent = {
|
|
23
|
-
/**
|
|
24
|
-
* Collection of objects containing the information retrieved by the Monitor.
|
|
25
|
-
*/
|
|
26
31
|
tokensInfo: {
|
|
27
32
|
token: Token;
|
|
28
33
|
bridgedSupply?: bigint;
|
|
29
34
|
}[];
|
|
35
|
+
confirmedBlockTags: ConfirmedBlockTags;
|
|
30
36
|
};
|
|
31
37
|
|
|
32
38
|
/**
|
|
@@ -35,10 +41,11 @@ export type MonitorEvent = {
|
|
|
35
41
|
export interface IMonitor {
|
|
36
42
|
/**
|
|
37
43
|
* Allows subscribers to listen to hyperlane's tokens info.
|
|
44
|
+
* Handler can be async - Monitor will await it before starting next cycle.
|
|
38
45
|
*/
|
|
39
46
|
on(
|
|
40
47
|
eventName: MonitorEventType.TokenInfo,
|
|
41
|
-
fn: (event: MonitorEvent) => void
|
|
48
|
+
fn: (event: MonitorEvent) => void | Promise<void>,
|
|
42
49
|
): this;
|
|
43
50
|
|
|
44
51
|
/**
|
|
@@ -3,21 +3,39 @@ import {
|
|
|
3
3
|
type TokenAmount,
|
|
4
4
|
} from '@hyperlane-xyz/sdk';
|
|
5
5
|
|
|
6
|
-
import {
|
|
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
|
+
};
|
|
7
17
|
|
|
8
18
|
export type PreparedTransaction = {
|
|
9
19
|
populatedTx: Awaited<
|
|
10
20
|
ReturnType<EvmMovableCollateralAdapter['populateRebalanceTx']>
|
|
11
21
|
>;
|
|
12
|
-
route:
|
|
22
|
+
route: RebalanceRoute;
|
|
13
23
|
originTokenAmount: TokenAmount;
|
|
14
24
|
};
|
|
15
25
|
|
|
16
26
|
export type RebalanceMetrics = {
|
|
17
|
-
route:
|
|
27
|
+
route: RebalanceRoute;
|
|
18
28
|
originTokenAmount: TokenAmount;
|
|
19
29
|
};
|
|
20
30
|
|
|
31
|
+
export type RebalanceExecutionResult = {
|
|
32
|
+
route: RebalanceRoute;
|
|
33
|
+
success: boolean;
|
|
34
|
+
messageId?: string;
|
|
35
|
+
txHash?: string;
|
|
36
|
+
error?: string;
|
|
37
|
+
};
|
|
38
|
+
|
|
21
39
|
export interface IRebalancer {
|
|
22
|
-
rebalance(routes:
|
|
40
|
+
rebalance(routes: RebalanceRoute[]): Promise<RebalanceExecutionResult[]>;
|
|
23
41
|
}
|
|
@@ -1,13 +1,34 @@
|
|
|
1
1
|
import { type ChainMap, type ChainName } from '@hyperlane-xyz/sdk';
|
|
2
|
+
import type { Address } from '@hyperlane-xyz/utils';
|
|
2
3
|
|
|
3
4
|
export type RawBalances = ChainMap<bigint>;
|
|
4
5
|
|
|
5
|
-
export
|
|
6
|
+
export interface Route {
|
|
6
7
|
origin: ChainName;
|
|
7
8
|
destination: ChainName;
|
|
8
9
|
amount: bigint;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface StrategyRoute extends Route {
|
|
13
|
+
bridge: Address;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type InflightContext = {
|
|
17
|
+
/**
|
|
18
|
+
* In-flight rebalances from ActionTracker.
|
|
19
|
+
* Uses Route[] because recovered intents (from Explorer startup recovery)
|
|
20
|
+
* don't have bridge information. Some routes may have bridge at runtime.
|
|
21
|
+
*/
|
|
22
|
+
pendingRebalances: Route[];
|
|
23
|
+
pendingTransfers: Route[];
|
|
24
|
+
/** Routes from earlier strategies - always have bridge */
|
|
25
|
+
proposedRebalances?: StrategyRoute[];
|
|
9
26
|
};
|
|
10
27
|
|
|
11
28
|
export interface IStrategy {
|
|
12
|
-
|
|
29
|
+
readonly name: string;
|
|
30
|
+
getRebalancingRoutes(
|
|
31
|
+
rawBalances: RawBalances,
|
|
32
|
+
inflightContext?: InflightContext,
|
|
33
|
+
): StrategyRoute[];
|
|
13
34
|
}
|