@hyperlane-xyz/rebalancer 2.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bridges/LiFiBridge.d.ts +67 -0
- package/dist/bridges/LiFiBridge.d.ts.map +1 -0
- package/dist/bridges/LiFiBridge.js +386 -0
- package/dist/bridges/LiFiBridge.js.map +1 -0
- package/dist/config/RebalancerConfig.d.ts +8 -2
- package/dist/config/RebalancerConfig.d.ts.map +1 -1
- package/dist/config/RebalancerConfig.js +9 -4
- package/dist/config/RebalancerConfig.js.map +1 -1
- package/dist/config/RebalancerConfig.test.js +135 -1
- package/dist/config/RebalancerConfig.test.js.map +1 -1
- package/dist/config/types.d.ts +1023 -304
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +113 -10
- package/dist/config/types.js.map +1 -1
- package/dist/core/InventoryRebalancer.d.ts +190 -0
- package/dist/core/InventoryRebalancer.d.ts.map +1 -0
- package/dist/core/InventoryRebalancer.js +892 -0
- package/dist/core/InventoryRebalancer.js.map +1 -0
- package/dist/core/InventoryRebalancer.test.d.ts +2 -0
- package/dist/core/InventoryRebalancer.test.d.ts.map +1 -0
- package/dist/core/InventoryRebalancer.test.js +1382 -0
- package/dist/core/InventoryRebalancer.test.js.map +1 -0
- package/dist/core/Rebalancer.d.ts +11 -4
- package/dist/core/Rebalancer.d.ts.map +1 -1
- package/dist/core/Rebalancer.js +92 -9
- package/dist/core/Rebalancer.js.map +1 -1
- package/dist/core/Rebalancer.test.js +82 -49
- package/dist/core/Rebalancer.test.js.map +1 -1
- package/dist/core/RebalancerOrchestrator.d.ts +30 -9
- package/dist/core/RebalancerOrchestrator.d.ts.map +1 -1
- package/dist/core/RebalancerOrchestrator.js +79 -71
- package/dist/core/RebalancerOrchestrator.js.map +1 -1
- package/dist/core/RebalancerOrchestrator.test.d.ts +2 -0
- package/dist/core/RebalancerOrchestrator.test.d.ts.map +1 -0
- package/dist/core/RebalancerOrchestrator.test.js +719 -0
- package/dist/core/RebalancerOrchestrator.test.js.map +1 -0
- package/dist/core/RebalancerService.d.ts +7 -3
- package/dist/core/RebalancerService.d.ts.map +1 -1
- package/dist/core/RebalancerService.js +44 -24
- package/dist/core/RebalancerService.js.map +1 -1
- package/dist/core/RebalancerService.test.js +74 -110
- package/dist/core/RebalancerService.test.js.map +1 -1
- package/dist/e2e/collateral-deficit.e2e-test.js +1 -3
- package/dist/e2e/collateral-deficit.e2e-test.js.map +1 -1
- package/dist/e2e/composite.e2e-test.js.map +1 -1
- package/dist/e2e/harness/BridgeSetup.d.ts +6 -0
- package/dist/e2e/harness/BridgeSetup.d.ts.map +1 -1
- package/dist/e2e/harness/BridgeSetup.js +10 -1
- package/dist/e2e/harness/BridgeSetup.js.map +1 -1
- package/dist/e2e/harness/ForkIndexer.d.ts.map +1 -1
- package/dist/e2e/harness/ForkIndexer.js +1 -0
- package/dist/e2e/harness/ForkIndexer.js.map +1 -1
- package/dist/e2e/harness/TestHelpers.d.ts.map +1 -1
- package/dist/e2e/harness/TestHelpers.js +1 -4
- package/dist/e2e/harness/TestHelpers.js.map +1 -1
- package/dist/e2e/harness/TestRebalancer.d.ts +1 -1
- package/dist/e2e/harness/TestRebalancer.d.ts.map +1 -1
- package/dist/e2e/harness/TestRebalancer.js +9 -9
- package/dist/e2e/harness/TestRebalancer.js.map +1 -1
- package/dist/e2e/minAmount.e2e-test.js +0 -1
- package/dist/e2e/minAmount.e2e-test.js.map +1 -1
- package/dist/e2e/weighted.e2e-test.js +0 -1
- package/dist/e2e/weighted.e2e-test.js.map +1 -1
- package/dist/factories/RebalancerContextFactory.d.ts +48 -6
- package/dist/factories/RebalancerContextFactory.d.ts.map +1 -1
- package/dist/factories/RebalancerContextFactory.js +171 -17
- 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 +2 -2
- package/dist/index.js.map +1 -1
- package/dist/interfaces/IExternalBridge.d.ts +101 -0
- package/dist/interfaces/IExternalBridge.d.ts.map +1 -0
- package/dist/interfaces/IExternalBridge.js +2 -0
- package/dist/interfaces/IExternalBridge.js.map +1 -0
- package/dist/interfaces/IMonitor.d.ts +1 -0
- package/dist/interfaces/IMonitor.d.ts.map +1 -1
- package/dist/interfaces/IRebalancer.d.ts +25 -25
- package/dist/interfaces/IRebalancer.d.ts.map +1 -1
- package/dist/interfaces/IStrategy.d.ts +36 -3
- package/dist/interfaces/IStrategy.d.ts.map +1 -1
- package/dist/interfaces/IStrategy.js +12 -1
- package/dist/interfaces/IStrategy.js.map +1 -1
- package/dist/metrics/PriceGetter.js +1 -1
- package/dist/metrics/PriceGetter.js.map +1 -1
- package/dist/metrics/scripts/metrics.d.ts +3 -3
- package/dist/monitor/Monitor.d.ts +12 -2
- package/dist/monitor/Monitor.d.ts.map +1 -1
- package/dist/monitor/Monitor.js +46 -1
- package/dist/monitor/Monitor.js.map +1 -1
- package/dist/service.js +40 -17
- package/dist/service.js.map +1 -1
- package/dist/strategy/BaseStrategy.d.ts +12 -6
- package/dist/strategy/BaseStrategy.d.ts.map +1 -1
- package/dist/strategy/BaseStrategy.js +56 -21
- package/dist/strategy/BaseStrategy.js.map +1 -1
- package/dist/strategy/CollateralDeficitStrategy.d.ts +1 -1
- package/dist/strategy/CollateralDeficitStrategy.d.ts.map +1 -1
- package/dist/strategy/CollateralDeficitStrategy.js +19 -11
- package/dist/strategy/CollateralDeficitStrategy.js.map +1 -1
- package/dist/strategy/CollateralDeficitStrategy.test.js +135 -2
- package/dist/strategy/CollateralDeficitStrategy.test.js.map +1 -1
- package/dist/strategy/CompositeStrategy.test.js +13 -0
- package/dist/strategy/CompositeStrategy.test.js.map +1 -1
- package/dist/strategy/MinAmountStrategy.test.js +4 -0
- package/dist/strategy/MinAmountStrategy.test.js.map +1 -1
- package/dist/strategy/StrategyFactory.d.ts +2 -1
- package/dist/strategy/StrategyFactory.d.ts.map +1 -1
- package/dist/strategy/StrategyFactory.js +24 -8
- package/dist/strategy/StrategyFactory.js.map +1 -1
- package/dist/strategy/WeightedStrategy.test.js +6 -0
- package/dist/strategy/WeightedStrategy.test.js.map +1 -1
- package/dist/test/helpers.d.ts +8 -7
- package/dist/test/helpers.d.ts.map +1 -1
- package/dist/test/helpers.js +23 -5
- package/dist/test/helpers.js.map +1 -1
- package/dist/test/lifiMocks.d.ts +51 -0
- package/dist/test/lifiMocks.d.ts.map +1 -0
- package/dist/test/lifiMocks.js +130 -0
- package/dist/test/lifiMocks.js.map +1 -0
- package/dist/tracking/ActionTracker.d.ts +34 -1
- package/dist/tracking/ActionTracker.d.ts.map +1 -1
- package/dist/tracking/ActionTracker.js +233 -26
- package/dist/tracking/ActionTracker.js.map +1 -1
- package/dist/tracking/ActionTracker.test.js +380 -19
- package/dist/tracking/ActionTracker.test.js.map +1 -1
- package/dist/tracking/IActionTracker.d.ts +48 -3
- package/dist/tracking/IActionTracker.d.ts.map +1 -1
- package/dist/tracking/InflightContextAdapter.d.ts.map +1 -1
- package/dist/tracking/InflightContextAdapter.js +24 -7
- package/dist/tracking/InflightContextAdapter.js.map +1 -1
- package/dist/tracking/InflightContextAdapter.test.js +7 -4
- package/dist/tracking/InflightContextAdapter.test.js.map +1 -1
- package/dist/tracking/types.d.ts +33 -2
- package/dist/tracking/types.d.ts.map +1 -1
- package/dist/utils/ExplorerClient.d.ts +3 -1
- package/dist/utils/ExplorerClient.d.ts.map +1 -1
- package/dist/utils/ExplorerClient.js +16 -8
- package/dist/utils/ExplorerClient.js.map +1 -1
- package/dist/utils/bridgeUtils.d.ts +27 -4
- package/dist/utils/bridgeUtils.d.ts.map +1 -1
- package/dist/utils/bridgeUtils.js +38 -0
- package/dist/utils/bridgeUtils.js.map +1 -1
- package/dist/utils/bridgeUtils.test.js +9 -0
- package/dist/utils/bridgeUtils.test.js.map +1 -1
- package/dist/utils/gasEstimation.d.ts +65 -0
- package/dist/utils/gasEstimation.d.ts.map +1 -0
- package/dist/utils/gasEstimation.js +176 -0
- package/dist/utils/gasEstimation.js.map +1 -0
- package/dist/utils/tokenUtils.d.ts +9 -1
- package/dist/utils/tokenUtils.d.ts.map +1 -1
- package/dist/utils/tokenUtils.js +11 -0
- package/dist/utils/tokenUtils.js.map +1 -1
- package/package.json +9 -7
- package/src/bridges/LiFiBridge.ts +538 -0
- package/src/config/RebalancerConfig.test.ts +162 -0
- package/src/config/RebalancerConfig.ts +21 -3
- package/src/config/types.ts +147 -10
- package/src/core/InventoryRebalancer.test.ts +1721 -0
- package/src/core/InventoryRebalancer.ts +1265 -0
- package/src/core/Rebalancer.test.ts +84 -30
- package/src/core/Rebalancer.ts +144 -23
- package/src/core/RebalancerOrchestrator.test.ts +869 -0
- package/src/core/RebalancerOrchestrator.ts +146 -95
- package/src/core/RebalancerService.test.ts +86 -124
- package/src/core/RebalancerService.ts +67 -33
- package/src/e2e/collateral-deficit.e2e-test.ts +2 -4
- package/src/e2e/composite.e2e-test.ts +5 -5
- package/src/e2e/harness/BridgeSetup.ts +28 -1
- package/src/e2e/harness/ForkIndexer.ts +1 -0
- package/src/e2e/harness/TestHelpers.ts +1 -4
- package/src/e2e/harness/TestRebalancer.ts +10 -7
- package/src/e2e/minAmount.e2e-test.ts +1 -2
- package/src/e2e/weighted.e2e-test.ts +1 -2
- package/src/factories/RebalancerContextFactory.ts +294 -24
- package/src/index.ts +22 -5
- package/src/interfaces/IExternalBridge.ts +115 -0
- package/src/interfaces/IMonitor.ts +1 -0
- package/src/interfaces/IRebalancer.ts +45 -29
- package/src/interfaces/IStrategy.ts +50 -3
- package/src/metrics/PriceGetter.ts +1 -1
- package/src/monitor/Monitor.ts +81 -2
- package/src/service.ts +59 -18
- package/src/strategy/BaseStrategy.ts +77 -24
- package/src/strategy/CollateralDeficitStrategy.test.ts +181 -4
- package/src/strategy/CollateralDeficitStrategy.ts +42 -15
- package/src/strategy/CompositeStrategy.test.ts +13 -0
- package/src/strategy/MinAmountStrategy.test.ts +4 -0
- package/src/strategy/StrategyFactory.ts +33 -6
- package/src/strategy/WeightedStrategy.test.ts +6 -0
- package/src/test/helpers.ts +39 -14
- package/src/test/lifiMocks.ts +174 -0
- package/src/tracking/ActionTracker.test.ts +443 -19
- package/src/tracking/ActionTracker.ts +339 -28
- package/src/tracking/IActionTracker.ts +59 -3
- package/src/tracking/InflightContextAdapter.test.ts +7 -4
- package/src/tracking/InflightContextAdapter.ts +42 -9
- package/src/tracking/types.ts +45 -2
- package/src/utils/ExplorerClient.ts +27 -10
- package/src/utils/bridgeUtils.test.ts +9 -0
- package/src/utils/bridgeUtils.ts +75 -6
- package/src/utils/gasEstimation.ts +272 -0
- package/src/utils/tokenUtils.ts +12 -0
- package/dist/tracking/index.d.ts +0 -7
- package/dist/tracking/index.d.ts.map +0 -1
- package/dist/tracking/index.js +0 -6
- package/dist/tracking/index.js.map +0 -1
- package/dist/utils/index.d.ts +0 -5
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js +0 -5
- package/dist/utils/index.js.map +0 -1
- package/src/tracking/index.ts +0 -36
- package/src/utils/index.ts +0 -4
|
@@ -8,12 +8,14 @@ import type {
|
|
|
8
8
|
InflightContext,
|
|
9
9
|
RawBalances,
|
|
10
10
|
Route,
|
|
11
|
+
RouteWithContext,
|
|
11
12
|
StrategyRoute,
|
|
12
13
|
} from '../interfaces/IStrategy.js';
|
|
13
14
|
import { type Metrics } from '../metrics/Metrics.js';
|
|
14
15
|
import {
|
|
15
16
|
type BridgeConfig,
|
|
16
17
|
type BridgeConfigWithOverride,
|
|
18
|
+
createStrategyRoute,
|
|
17
19
|
getBridgeConfig,
|
|
18
20
|
} from '../utils/bridgeUtils.js';
|
|
19
21
|
|
|
@@ -193,13 +195,15 @@ export abstract class BaseStrategy implements IStrategy {
|
|
|
193
195
|
deficit.chain,
|
|
194
196
|
);
|
|
195
197
|
|
|
196
|
-
//
|
|
197
|
-
routes.push(
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
198
|
+
// Create appropriate route type based on execution type
|
|
199
|
+
routes.push(
|
|
200
|
+
createStrategyRoute(
|
|
201
|
+
bridgeConfig,
|
|
202
|
+
surplus.chain,
|
|
203
|
+
deficit.chain,
|
|
204
|
+
transferAmount,
|
|
205
|
+
),
|
|
206
|
+
);
|
|
203
207
|
}
|
|
204
208
|
|
|
205
209
|
// Decreases the amounts for the following iterations
|
|
@@ -342,11 +346,17 @@ export abstract class BaseStrategy implements IStrategy {
|
|
|
342
346
|
}
|
|
343
347
|
|
|
344
348
|
/**
|
|
345
|
-
* Simulate pending rebalances
|
|
349
|
+
* Simulate pending rebalances based on execution method.
|
|
350
|
+
*
|
|
351
|
+
* For **movable_collateral** (default):
|
|
352
|
+
* - Add full amount to destination (origin already deducted on-chain)
|
|
346
353
|
*
|
|
347
|
-
*
|
|
348
|
-
* -
|
|
349
|
-
* -
|
|
354
|
+
* For **inventory**:
|
|
355
|
+
* - Simulate the eventual final state as if the entire intent will be fulfilled
|
|
356
|
+
* - Destination: add unfulfilled amount (total - delivered - awaiting)
|
|
357
|
+
* This is what's NOT yet reflected in on-chain balance
|
|
358
|
+
* - Origin: subtract pending amount (total - delivered)
|
|
359
|
+
* This is what WILL decrease when all messages deliver
|
|
350
360
|
*
|
|
351
361
|
* @param rawBalances - Current balances (may already have collateral reserved)
|
|
352
362
|
* @param pendingRebalances - In-flight rebalance operations (in_progress only)
|
|
@@ -354,7 +364,7 @@ export abstract class BaseStrategy implements IStrategy {
|
|
|
354
364
|
*/
|
|
355
365
|
protected simulatePendingRebalances(
|
|
356
366
|
rawBalances: RawBalances,
|
|
357
|
-
pendingRebalances:
|
|
367
|
+
pendingRebalances: RouteWithContext[],
|
|
358
368
|
): RawBalances {
|
|
359
369
|
if (pendingRebalances.length === 0) {
|
|
360
370
|
return rawBalances;
|
|
@@ -363,19 +373,59 @@ export abstract class BaseStrategy implements IStrategy {
|
|
|
363
373
|
const simulated = { ...rawBalances };
|
|
364
374
|
|
|
365
375
|
for (const rebalance of pendingRebalances) {
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
376
|
+
if (rebalance.executionMethod === 'inventory') {
|
|
377
|
+
// Inventory: simulate eventual final state
|
|
378
|
+
const total = rebalance.amount;
|
|
379
|
+
const delivered = rebalance.deliveredAmount ?? 0n;
|
|
380
|
+
const awaiting = rebalance.awaitingDeliveryAmount ?? 0n;
|
|
381
|
+
|
|
382
|
+
// Destination: add unfulfilled amount (total - delivered - awaiting)
|
|
383
|
+
// This is what's NOT yet reflected in on-chain balance
|
|
384
|
+
const destinationAdjustment = total - delivered - awaiting;
|
|
385
|
+
if (destinationAdjustment > 0n) {
|
|
386
|
+
simulated[rebalance.destination] =
|
|
387
|
+
(simulated[rebalance.destination] ?? 0n) + destinationAdjustment;
|
|
388
|
+
|
|
389
|
+
this.logger.debug(
|
|
390
|
+
{
|
|
391
|
+
context: this.constructor.name,
|
|
392
|
+
destination: rebalance.destination,
|
|
393
|
+
destinationAdjustment: destinationAdjustment.toString(),
|
|
394
|
+
},
|
|
395
|
+
'Simulated inventory rebalance (destination increase for unfulfilled)',
|
|
396
|
+
);
|
|
397
|
+
}
|
|
370
398
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
399
|
+
// Origin: subtract pending amount (total - delivered)
|
|
400
|
+
// This is what WILL decrease when all messages deliver
|
|
401
|
+
const originAdjustment = total - delivered;
|
|
402
|
+
if (originAdjustment > 0n) {
|
|
403
|
+
simulated[rebalance.origin] =
|
|
404
|
+
(simulated[rebalance.origin] ?? 0n) - originAdjustment;
|
|
405
|
+
|
|
406
|
+
this.logger.debug(
|
|
407
|
+
{
|
|
408
|
+
context: this.constructor.name,
|
|
409
|
+
origin: rebalance.origin,
|
|
410
|
+
originAdjustment: originAdjustment.toString(),
|
|
411
|
+
},
|
|
412
|
+
'Simulated inventory rebalance (origin decrease for pending)',
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
} else {
|
|
416
|
+
// Movable collateral: origin already deducted on-chain, add to destination
|
|
417
|
+
simulated[rebalance.destination] =
|
|
418
|
+
(simulated[rebalance.destination] ?? 0n) + rebalance.amount;
|
|
419
|
+
|
|
420
|
+
this.logger.debug(
|
|
421
|
+
{
|
|
422
|
+
context: this.constructor.name,
|
|
423
|
+
destination: rebalance.destination,
|
|
424
|
+
amount: rebalance.amount.toString(),
|
|
425
|
+
},
|
|
426
|
+
'Simulated movable collateral rebalance (destination increase)',
|
|
427
|
+
);
|
|
428
|
+
}
|
|
379
429
|
}
|
|
380
430
|
|
|
381
431
|
this.logger.info(
|
|
@@ -384,6 +434,9 @@ export abstract class BaseStrategy implements IStrategy {
|
|
|
384
434
|
from: r.origin,
|
|
385
435
|
to: r.destination,
|
|
386
436
|
amount: r.amount.toString(),
|
|
437
|
+
executionMethod: r.executionMethod ?? 'movable_collateral',
|
|
438
|
+
deliveredAmount: r.deliveredAmount?.toString() ?? '0',
|
|
439
|
+
awaitingDeliveryAmount: r.awaitingDeliveryAmount?.toString() ?? '0',
|
|
387
440
|
})),
|
|
388
441
|
},
|
|
389
442
|
'Simulated pending rebalances',
|
|
@@ -9,12 +9,15 @@ import {
|
|
|
9
9
|
} from '@hyperlane-xyz/sdk';
|
|
10
10
|
import type { Address } from '@hyperlane-xyz/utils';
|
|
11
11
|
|
|
12
|
+
import { ExternalBridgeType } from '../config/types.js';
|
|
12
13
|
import type {
|
|
14
|
+
InventoryRoute,
|
|
13
15
|
RawBalances,
|
|
14
16
|
Route,
|
|
15
17
|
StrategyRoute,
|
|
16
18
|
} from '../interfaces/IStrategy.js';
|
|
17
19
|
import { extractBridgeConfigs } from '../test/helpers.js';
|
|
20
|
+
import type { BridgeConfigWithOverride } from '../utils/bridgeUtils.js';
|
|
18
21
|
|
|
19
22
|
import { CollateralDeficitStrategy } from './CollateralDeficitStrategy.js';
|
|
20
23
|
|
|
@@ -183,6 +186,7 @@ describe('CollateralDeficitStrategy', () => {
|
|
|
183
186
|
origin: chain2,
|
|
184
187
|
destination: chain1,
|
|
185
188
|
amount: 5_000_000n, // 5 USDC pending to chain1
|
|
189
|
+
executionType: 'movableCollateral',
|
|
186
190
|
bridge: BRIDGE2, // Matches chain2's bridge for chain2->chain1 route
|
|
187
191
|
},
|
|
188
192
|
];
|
|
@@ -229,6 +233,7 @@ describe('CollateralDeficitStrategy', () => {
|
|
|
229
233
|
origin: chain2,
|
|
230
234
|
destination: chain1,
|
|
231
235
|
amount: 5_000_000n,
|
|
236
|
+
executionType: 'movableCollateral',
|
|
232
237
|
bridge: OTHER_BRIDGE, // Does NOT match chain2's configured bridge for chain2->chain1
|
|
233
238
|
},
|
|
234
239
|
];
|
|
@@ -271,6 +276,7 @@ describe('CollateralDeficitStrategy', () => {
|
|
|
271
276
|
origin: chain2,
|
|
272
277
|
destination: chain1,
|
|
273
278
|
amount: 10_000_000n, // 10 USDC pending - more than enough
|
|
279
|
+
executionType: 'movableCollateral',
|
|
274
280
|
bridge: BRIDGE2, // Matches chain2's configured bridge for chain2->chain1
|
|
275
281
|
},
|
|
276
282
|
];
|
|
@@ -383,7 +389,9 @@ describe('CollateralDeficitStrategy', () => {
|
|
|
383
389
|
expect(routes).to.have.lengthOf(1);
|
|
384
390
|
expect(routes[0].origin).to.equal(chain2);
|
|
385
391
|
expect(routes[0].destination).to.equal(chain1);
|
|
386
|
-
|
|
392
|
+
if (routes[0].executionType === 'movableCollateral') {
|
|
393
|
+
expect(routes[0].bridge).to.equal(BRIDGE2); // Uses chain2's (origin) bridge
|
|
394
|
+
}
|
|
387
395
|
});
|
|
388
396
|
|
|
389
397
|
it('should generate routes from surplus to deficit chains', () => {
|
|
@@ -473,8 +481,11 @@ describe('CollateralDeficitStrategy', () => {
|
|
|
473
481
|
const filtered = strategy['filterByConfiguredBridges'](pendingRebalances);
|
|
474
482
|
|
|
475
483
|
expect(filtered).to.have.lengthOf(2);
|
|
476
|
-
expect((filtered[0] as
|
|
477
|
-
|
|
484
|
+
expect((filtered[0] as Route & { bridge?: Address }).bridge).to.equal(
|
|
485
|
+
BRIDGE2,
|
|
486
|
+
);
|
|
487
|
+
expect((filtered[1] as Route & { bridge?: Address }).bridge).to.be
|
|
488
|
+
.undefined;
|
|
478
489
|
});
|
|
479
490
|
|
|
480
491
|
it('should include rebalance when bridge matches configured bridge for the route', () => {
|
|
@@ -502,7 +513,9 @@ describe('CollateralDeficitStrategy', () => {
|
|
|
502
513
|
|
|
503
514
|
const filtered = strategy['filterByConfiguredBridges'](pendingRebalances);
|
|
504
515
|
expect(filtered).to.have.lengthOf(1);
|
|
505
|
-
expect((filtered[0] as
|
|
516
|
+
expect((filtered[0] as Route & { bridge?: Address }).bridge).to.equal(
|
|
517
|
+
BRIDGE2,
|
|
518
|
+
);
|
|
506
519
|
});
|
|
507
520
|
|
|
508
521
|
it('should exclude rebalance when bridge does not match configured bridge for the route', () => {
|
|
@@ -525,6 +538,7 @@ describe('CollateralDeficitStrategy', () => {
|
|
|
525
538
|
origin: chain2,
|
|
526
539
|
destination: chain1,
|
|
527
540
|
amount: 5_000_000n,
|
|
541
|
+
executionType: 'movableCollateral',
|
|
528
542
|
bridge: BRIDGE1, // Does NOT match configured bridge for chain2->chain1 (should be BRIDGE2)
|
|
529
543
|
},
|
|
530
544
|
];
|
|
@@ -548,4 +562,167 @@ describe('CollateralDeficitStrategy', () => {
|
|
|
548
562
|
expect(filtered).to.have.lengthOf(0);
|
|
549
563
|
});
|
|
550
564
|
});
|
|
565
|
+
|
|
566
|
+
describe('inventory execution type', () => {
|
|
567
|
+
it('should create inventory route when config is inventory type', () => {
|
|
568
|
+
const config = {
|
|
569
|
+
[chain1]: {
|
|
570
|
+
executionType: 'inventory',
|
|
571
|
+
externalBridge: ExternalBridgeType.LiFi,
|
|
572
|
+
buffer: '1000',
|
|
573
|
+
},
|
|
574
|
+
[chain2]: {
|
|
575
|
+
executionType: 'inventory',
|
|
576
|
+
externalBridge: ExternalBridgeType.LiFi,
|
|
577
|
+
buffer: '500',
|
|
578
|
+
},
|
|
579
|
+
};
|
|
580
|
+
const bridgeConfigs: ChainMap<BridgeConfigWithOverride> = {
|
|
581
|
+
[chain1]: {
|
|
582
|
+
executionType: 'inventory',
|
|
583
|
+
externalBridge: ExternalBridgeType.LiFi,
|
|
584
|
+
},
|
|
585
|
+
[chain2]: {
|
|
586
|
+
executionType: 'inventory',
|
|
587
|
+
externalBridge: ExternalBridgeType.LiFi,
|
|
588
|
+
},
|
|
589
|
+
};
|
|
590
|
+
|
|
591
|
+
const strategy = new CollateralDeficitStrategy(
|
|
592
|
+
config as any,
|
|
593
|
+
tokensByChainName,
|
|
594
|
+
testLogger,
|
|
595
|
+
bridgeConfigs,
|
|
596
|
+
);
|
|
597
|
+
|
|
598
|
+
// Start with positive balances
|
|
599
|
+
const rawBalances: RawBalances = {
|
|
600
|
+
[chain1]: 2_000_000n, // 2 USDC
|
|
601
|
+
[chain2]: 20_000_000n, // 20 USDC
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
// Pending transfer will drain chain1 to create deficit
|
|
605
|
+
const inflightContext = {
|
|
606
|
+
pendingTransfers: [
|
|
607
|
+
{
|
|
608
|
+
origin: chain2,
|
|
609
|
+
destination: chain1,
|
|
610
|
+
amount: 7_000_000n, // 7 USDC pending to chain1
|
|
611
|
+
},
|
|
612
|
+
],
|
|
613
|
+
pendingRebalances: [] as StrategyRoute[],
|
|
614
|
+
};
|
|
615
|
+
|
|
616
|
+
// After reserveCollateral: chain1 = 2 - 7 = -5 USDC (deficit)
|
|
617
|
+
const routes = strategy.getRebalancingRoutes(
|
|
618
|
+
rawBalances,
|
|
619
|
+
inflightContext,
|
|
620
|
+
);
|
|
621
|
+
|
|
622
|
+
expect(routes).to.have.lengthOf(1);
|
|
623
|
+
expect(routes[0].origin).to.equal(chain2);
|
|
624
|
+
expect(routes[0].destination).to.equal(chain1);
|
|
625
|
+
expect(routes[0].executionType).to.equal('inventory');
|
|
626
|
+
expect((routes[0] as InventoryRoute).externalBridge).to.equal(
|
|
627
|
+
ExternalBridgeType.LiFi,
|
|
628
|
+
);
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
it('should include pending inventory rebalances with matching externalBridge in filter', () => {
|
|
632
|
+
const bridgeConfigs: ChainMap<BridgeConfigWithOverride> = {
|
|
633
|
+
[chain1]: {
|
|
634
|
+
executionType: 'inventory',
|
|
635
|
+
externalBridge: ExternalBridgeType.LiFi,
|
|
636
|
+
},
|
|
637
|
+
[chain2]: {
|
|
638
|
+
executionType: 'inventory',
|
|
639
|
+
externalBridge: ExternalBridgeType.LiFi,
|
|
640
|
+
},
|
|
641
|
+
};
|
|
642
|
+
|
|
643
|
+
const config = {
|
|
644
|
+
[chain1]: {
|
|
645
|
+
executionType: 'inventory',
|
|
646
|
+
externalBridge: ExternalBridgeType.LiFi,
|
|
647
|
+
buffer: '1000',
|
|
648
|
+
},
|
|
649
|
+
[chain2]: {
|
|
650
|
+
executionType: 'inventory',
|
|
651
|
+
externalBridge: ExternalBridgeType.LiFi,
|
|
652
|
+
buffer: '500',
|
|
653
|
+
},
|
|
654
|
+
};
|
|
655
|
+
|
|
656
|
+
const strategy = new CollateralDeficitStrategy(
|
|
657
|
+
config as any,
|
|
658
|
+
tokensByChainName,
|
|
659
|
+
testLogger,
|
|
660
|
+
bridgeConfigs,
|
|
661
|
+
);
|
|
662
|
+
|
|
663
|
+
const pendingRebalances: InventoryRoute[] = [
|
|
664
|
+
{
|
|
665
|
+
origin: chain2,
|
|
666
|
+
destination: chain1,
|
|
667
|
+
amount: 5_000_000n,
|
|
668
|
+
executionType: 'inventory',
|
|
669
|
+
externalBridge: ExternalBridgeType.LiFi,
|
|
670
|
+
},
|
|
671
|
+
];
|
|
672
|
+
|
|
673
|
+
const filtered = strategy['filterByConfiguredBridges'](pendingRebalances);
|
|
674
|
+
expect(filtered).to.have.lengthOf(1);
|
|
675
|
+
expect((filtered[0] as InventoryRoute).externalBridge).to.equal(
|
|
676
|
+
ExternalBridgeType.LiFi,
|
|
677
|
+
);
|
|
678
|
+
});
|
|
679
|
+
|
|
680
|
+
it('should exclude pending inventory rebalances with non-matching externalBridge', () => {
|
|
681
|
+
const bridgeConfigs: ChainMap<BridgeConfigWithOverride> = {
|
|
682
|
+
[chain1]: {
|
|
683
|
+
executionType: 'inventory',
|
|
684
|
+
externalBridge: ExternalBridgeType.LiFi,
|
|
685
|
+
},
|
|
686
|
+
[chain2]: {
|
|
687
|
+
executionType: 'inventory',
|
|
688
|
+
externalBridge: ExternalBridgeType.LiFi,
|
|
689
|
+
},
|
|
690
|
+
};
|
|
691
|
+
|
|
692
|
+
const config = {
|
|
693
|
+
[chain1]: {
|
|
694
|
+
executionType: 'inventory',
|
|
695
|
+
externalBridge: ExternalBridgeType.LiFi,
|
|
696
|
+
buffer: '1000',
|
|
697
|
+
},
|
|
698
|
+
[chain2]: {
|
|
699
|
+
executionType: 'inventory',
|
|
700
|
+
externalBridge: ExternalBridgeType.LiFi,
|
|
701
|
+
buffer: '500',
|
|
702
|
+
},
|
|
703
|
+
};
|
|
704
|
+
|
|
705
|
+
const strategy = new CollateralDeficitStrategy(
|
|
706
|
+
config as any,
|
|
707
|
+
tokensByChainName,
|
|
708
|
+
testLogger,
|
|
709
|
+
bridgeConfigs,
|
|
710
|
+
);
|
|
711
|
+
|
|
712
|
+
// Create a route with a different externalBridge value to test mismatch
|
|
713
|
+
// Using type assertion since we're testing the filtering logic
|
|
714
|
+
const pendingRebalances = [
|
|
715
|
+
{
|
|
716
|
+
origin: chain2,
|
|
717
|
+
destination: chain1,
|
|
718
|
+
amount: 5_000_000n,
|
|
719
|
+
executionType: 'inventory' as const,
|
|
720
|
+
externalBridge: 'nonexistent_bridge' as ExternalBridgeType, // Different bridge
|
|
721
|
+
},
|
|
722
|
+
];
|
|
723
|
+
|
|
724
|
+
const filtered = strategy['filterByConfiguredBridges'](pendingRebalances);
|
|
725
|
+
expect(filtered).to.have.lengthOf(0);
|
|
726
|
+
});
|
|
727
|
+
});
|
|
551
728
|
});
|
|
@@ -14,7 +14,12 @@ import type {
|
|
|
14
14
|
StrategyRoute,
|
|
15
15
|
} from '../interfaces/IStrategy.js';
|
|
16
16
|
import { Metrics } from '../metrics/Metrics.js';
|
|
17
|
-
import
|
|
17
|
+
import {
|
|
18
|
+
type BridgeConfigWithOverride,
|
|
19
|
+
createStrategyRoute,
|
|
20
|
+
isInventoryConfig,
|
|
21
|
+
isMovableCollateralConfig,
|
|
22
|
+
} from '../utils/bridgeUtils.js';
|
|
18
23
|
|
|
19
24
|
import { BaseStrategy, type Delta } from './BaseStrategy.js';
|
|
20
25
|
|
|
@@ -249,12 +254,14 @@ export class CollateralDeficitStrategy extends BaseStrategy {
|
|
|
249
254
|
surplus.chain,
|
|
250
255
|
deficit.chain,
|
|
251
256
|
);
|
|
252
|
-
routes.push(
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
257
|
+
routes.push(
|
|
258
|
+
createStrategyRoute(
|
|
259
|
+
bridgeConfig,
|
|
260
|
+
surplus.chain,
|
|
261
|
+
deficit.chain,
|
|
262
|
+
transferAmount,
|
|
263
|
+
),
|
|
264
|
+
);
|
|
258
265
|
}
|
|
259
266
|
|
|
260
267
|
deficit.amount -= transferAmount;
|
|
@@ -275,6 +282,11 @@ export class CollateralDeficitStrategy extends BaseStrategy {
|
|
|
275
282
|
|
|
276
283
|
const filteredRoutes = this.filterRoutes(routes, actualBalances);
|
|
277
284
|
|
|
285
|
+
// Record metrics for each intent created
|
|
286
|
+
for (const route of filteredRoutes) {
|
|
287
|
+
this.metrics?.recordIntentCreated(route, this.name);
|
|
288
|
+
}
|
|
289
|
+
|
|
278
290
|
this.logger.debug(
|
|
279
291
|
{
|
|
280
292
|
context: this.constructor.name,
|
|
@@ -299,18 +311,33 @@ export class CollateralDeficitStrategy extends BaseStrategy {
|
|
|
299
311
|
}
|
|
300
312
|
|
|
301
313
|
return pendingRebalances.filter((rebalance) => {
|
|
302
|
-
if (!('bridge' in rebalance) || !rebalance.bridge) {
|
|
303
|
-
this.logger.debug(
|
|
304
|
-
{ origin: rebalance.origin, destination: rebalance.destination },
|
|
305
|
-
'Including pending rebalance without bridge (recovered intent)',
|
|
306
|
-
);
|
|
307
|
-
return true;
|
|
308
|
-
}
|
|
309
314
|
const bridgeConfig = this.getBridgeConfigForRoute(
|
|
310
315
|
rebalance.origin,
|
|
311
316
|
rebalance.destination,
|
|
312
317
|
);
|
|
313
|
-
|
|
318
|
+
|
|
319
|
+
// For movableCollateral routes: match bridge address
|
|
320
|
+
if ('bridge' in rebalance && rebalance.bridge) {
|
|
321
|
+
return (
|
|
322
|
+
isMovableCollateralConfig(bridgeConfig) &&
|
|
323
|
+
bridgeConfig.bridge === rebalance.bridge
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// For inventory routes: match externalBridge type
|
|
328
|
+
if ('externalBridge' in rebalance && rebalance.externalBridge) {
|
|
329
|
+
return (
|
|
330
|
+
isInventoryConfig(bridgeConfig) &&
|
|
331
|
+
bridgeConfig.externalBridge === rebalance.externalBridge
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Recovered intents without bridge info - include to be safe
|
|
336
|
+
this.logger.debug(
|
|
337
|
+
{ origin: rebalance.origin, destination: rebalance.destination },
|
|
338
|
+
'Including pending rebalance without bridge (recovered intent)',
|
|
339
|
+
);
|
|
340
|
+
return true;
|
|
314
341
|
});
|
|
315
342
|
}
|
|
316
343
|
|
|
@@ -75,12 +75,14 @@ describe('CompositeStrategy', () => {
|
|
|
75
75
|
origin: chain1,
|
|
76
76
|
destination: chain2,
|
|
77
77
|
amount: 1000n,
|
|
78
|
+
executionType: 'movableCollateral',
|
|
78
79
|
bridge: TEST_BRIDGE,
|
|
79
80
|
};
|
|
80
81
|
const route2: StrategyRoute = {
|
|
81
82
|
origin: chain2,
|
|
82
83
|
destination: chain3,
|
|
83
84
|
amount: 2000n,
|
|
85
|
+
executionType: 'movableCollateral',
|
|
84
86
|
bridge: TEST_BRIDGE,
|
|
85
87
|
};
|
|
86
88
|
|
|
@@ -110,6 +112,7 @@ describe('CompositeStrategy', () => {
|
|
|
110
112
|
origin: chain1,
|
|
111
113
|
destination: chain2,
|
|
112
114
|
amount: 1000n,
|
|
115
|
+
executionType: 'movableCollateral',
|
|
113
116
|
bridge: TEST_BRIDGE,
|
|
114
117
|
};
|
|
115
118
|
|
|
@@ -147,18 +150,21 @@ describe('CompositeStrategy', () => {
|
|
|
147
150
|
origin: chain1,
|
|
148
151
|
destination: chain2,
|
|
149
152
|
amount: 1000n,
|
|
153
|
+
executionType: 'movableCollateral',
|
|
150
154
|
bridge: TEST_BRIDGE,
|
|
151
155
|
};
|
|
152
156
|
const route2: StrategyRoute = {
|
|
153
157
|
origin: chain2,
|
|
154
158
|
destination: chain3,
|
|
155
159
|
amount: 2000n,
|
|
160
|
+
executionType: 'movableCollateral',
|
|
156
161
|
bridge: TEST_BRIDGE,
|
|
157
162
|
};
|
|
158
163
|
const route3: StrategyRoute = {
|
|
159
164
|
origin: chain3,
|
|
160
165
|
destination: chain1,
|
|
161
166
|
amount: 3000n,
|
|
167
|
+
executionType: 'movableCollateral',
|
|
162
168
|
bridge: TEST_BRIDGE,
|
|
163
169
|
};
|
|
164
170
|
|
|
@@ -206,6 +212,7 @@ describe('CompositeStrategy', () => {
|
|
|
206
212
|
origin: chain3,
|
|
207
213
|
destination: chain1,
|
|
208
214
|
amount: 500n,
|
|
215
|
+
executionType: 'movableCollateral',
|
|
209
216
|
bridge: TEST_BRIDGE,
|
|
210
217
|
};
|
|
211
218
|
|
|
@@ -213,6 +220,7 @@ describe('CompositeStrategy', () => {
|
|
|
213
220
|
origin: chain1,
|
|
214
221
|
destination: chain2,
|
|
215
222
|
amount: 1000n,
|
|
223
|
+
executionType: 'movableCollateral',
|
|
216
224
|
bridge: TEST_BRIDGE,
|
|
217
225
|
};
|
|
218
226
|
|
|
@@ -306,18 +314,21 @@ describe('CompositeStrategy', () => {
|
|
|
306
314
|
origin: chain1,
|
|
307
315
|
destination: chain2,
|
|
308
316
|
amount: 1000n,
|
|
317
|
+
executionType: 'movableCollateral',
|
|
309
318
|
bridge: TEST_BRIDGE,
|
|
310
319
|
};
|
|
311
320
|
const route1b: StrategyRoute = {
|
|
312
321
|
origin: chain1,
|
|
313
322
|
destination: chain3,
|
|
314
323
|
amount: 1500n,
|
|
324
|
+
executionType: 'movableCollateral',
|
|
315
325
|
bridge: TEST_BRIDGE,
|
|
316
326
|
};
|
|
317
327
|
const route2a: StrategyRoute = {
|
|
318
328
|
origin: chain2,
|
|
319
329
|
destination: chain3,
|
|
320
330
|
amount: 2000n,
|
|
331
|
+
executionType: 'movableCollateral',
|
|
321
332
|
bridge: TEST_BRIDGE,
|
|
322
333
|
};
|
|
323
334
|
|
|
@@ -348,6 +359,7 @@ describe('CompositeStrategy', () => {
|
|
|
348
359
|
origin: chain2,
|
|
349
360
|
destination: chain3,
|
|
350
361
|
amount: 2000n,
|
|
362
|
+
executionType: 'movableCollateral',
|
|
351
363
|
bridge: TEST_BRIDGE,
|
|
352
364
|
};
|
|
353
365
|
|
|
@@ -377,6 +389,7 @@ describe('CompositeStrategy', () => {
|
|
|
377
389
|
origin: chain1,
|
|
378
390
|
destination: chain2,
|
|
379
391
|
amount: 1000n,
|
|
392
|
+
executionType: 'movableCollateral',
|
|
380
393
|
bridge: TEST_BRIDGE,
|
|
381
394
|
};
|
|
382
395
|
|
|
@@ -410,6 +410,7 @@ describe('MinAmountStrategy', () => {
|
|
|
410
410
|
destination: chain1,
|
|
411
411
|
amount: BigInt(70e18),
|
|
412
412
|
bridge: AddressZero,
|
|
413
|
+
executionType: 'movableCollateral',
|
|
413
414
|
},
|
|
414
415
|
]);
|
|
415
416
|
});
|
|
@@ -467,12 +468,14 @@ describe('MinAmountStrategy', () => {
|
|
|
467
468
|
destination: chain1,
|
|
468
469
|
amount: BigInt(50e18),
|
|
469
470
|
bridge: AddressZero,
|
|
471
|
+
executionType: 'movableCollateral',
|
|
470
472
|
},
|
|
471
473
|
{
|
|
472
474
|
origin: chain3,
|
|
473
475
|
destination: chain2,
|
|
474
476
|
amount: BigInt(25e18),
|
|
475
477
|
bridge: AddressZero,
|
|
478
|
+
executionType: 'movableCollateral',
|
|
476
479
|
},
|
|
477
480
|
]);
|
|
478
481
|
});
|
|
@@ -721,6 +724,7 @@ describe('MinAmountStrategy', () => {
|
|
|
721
724
|
destination: chain1,
|
|
722
725
|
amount: 100n,
|
|
723
726
|
bridge: AddressZero,
|
|
727
|
+
executionType: 'movableCollateral',
|
|
724
728
|
},
|
|
725
729
|
]);
|
|
726
730
|
});
|