@hyperlane-xyz/rebalancer 2.0.0 → 3.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/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 +7 -2
- package/dist/config/RebalancerConfig.d.ts.map +1 -1
- package/dist/config/RebalancerConfig.js +7 -4
- package/dist/config/RebalancerConfig.js.map +1 -1
- package/dist/config/RebalancerConfig.test.js +134 -1
- package/dist/config/RebalancerConfig.test.js.map +1 -1
- package/dist/config/types.d.ts +1016 -304
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +105 -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 +885 -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 +1351 -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 +714 -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 +71 -109
- 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/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 +6 -7
- 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 +170 -17
- package/dist/factories/RebalancerContextFactory.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- 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 +33 -1
- package/dist/tracking/ActionTracker.d.ts.map +1 -1
- package/dist/tracking/ActionTracker.js +193 -22
- package/dist/tracking/ActionTracker.js.map +1 -1
- package/dist/tracking/ActionTracker.test.js +107 -19
- package/dist/tracking/ActionTracker.test.js.map +1 -1
- package/dist/tracking/IActionTracker.d.ts +47 -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 +31 -2
- package/dist/tracking/types.d.ts.map +1 -1
- package/dist/utils/ExplorerClient.d.ts +2 -1
- package/dist/utils/ExplorerClient.d.ts.map +1 -1
- package/dist/utils/ExplorerClient.js +13 -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 +160 -0
- package/src/config/RebalancerConfig.ts +14 -3
- package/src/config/types.ts +136 -10
- package/src/core/InventoryRebalancer.test.ts +1684 -0
- package/src/core/InventoryRebalancer.ts +1255 -0
- package/src/core/Rebalancer.test.ts +84 -30
- package/src/core/Rebalancer.ts +144 -23
- package/src/core/RebalancerOrchestrator.test.ts +860 -0
- package/src/core/RebalancerOrchestrator.ts +146 -95
- package/src/core/RebalancerService.test.ts +80 -123
- 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/TestHelpers.ts +1 -4
- package/src/e2e/harness/TestRebalancer.ts +7 -7
- package/src/e2e/minAmount.e2e-test.ts +1 -2
- package/src/e2e/weighted.e2e-test.ts +1 -2
- package/src/factories/RebalancerContextFactory.ts +293 -24
- package/src/index.ts +20 -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 +122 -19
- package/src/tracking/ActionTracker.ts +284 -24
- package/src/tracking/IActionTracker.ts +58 -3
- package/src/tracking/InflightContextAdapter.test.ts +7 -4
- package/src/tracking/InflightContextAdapter.ts +42 -9
- package/src/tracking/types.ts +43 -2
- package/src/utils/ExplorerClient.ts +23 -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
|
@@ -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
|
});
|
|
@@ -3,12 +3,17 @@ import { type Logger } from 'pino';
|
|
|
3
3
|
import { type ChainMap, type Token } from '@hyperlane-xyz/sdk';
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
|
+
ExecutionType,
|
|
6
7
|
RebalancerStrategyOptions,
|
|
7
8
|
type StrategyConfig,
|
|
8
9
|
} from '../config/types.js';
|
|
9
10
|
import { type IStrategy } from '../interfaces/IStrategy.js';
|
|
10
11
|
import { type Metrics } from '../metrics/Metrics.js';
|
|
11
|
-
import type {
|
|
12
|
+
import type {
|
|
13
|
+
BridgeConfigWithOverride,
|
|
14
|
+
InventoryBridgeConfig,
|
|
15
|
+
MovableCollateralBridgeConfig,
|
|
16
|
+
} from '../utils/bridgeUtils.js';
|
|
12
17
|
|
|
13
18
|
import { CollateralDeficitStrategy } from './CollateralDeficitStrategy.js';
|
|
14
19
|
import { CompositeStrategy } from './CompositeStrategy.js';
|
|
@@ -26,6 +31,7 @@ export class StrategyFactory {
|
|
|
26
31
|
* @param initialTotalCollateral The initial total collateral of the rebalancer
|
|
27
32
|
* @param logger The logger to use for the strategy
|
|
28
33
|
* @param metrics The metrics to use for the strategy
|
|
34
|
+
* @param minAmountsByChain Optional minimum amounts per chain for filtering routes
|
|
29
35
|
* @returns A concrete strategy implementation
|
|
30
36
|
*/
|
|
31
37
|
static createStrategy(
|
|
@@ -34,6 +40,7 @@ export class StrategyFactory {
|
|
|
34
40
|
initialTotalCollateral: bigint,
|
|
35
41
|
logger: Logger,
|
|
36
42
|
metrics?: Metrics,
|
|
43
|
+
minAmountsByChain?: ChainMap<bigint>,
|
|
37
44
|
): IStrategy {
|
|
38
45
|
if (strategyConfigs.length === 0) {
|
|
39
46
|
throw new Error('At least one strategy must be configured');
|
|
@@ -47,6 +54,7 @@ export class StrategyFactory {
|
|
|
47
54
|
initialTotalCollateral,
|
|
48
55
|
logger,
|
|
49
56
|
metrics,
|
|
57
|
+
minAmountsByChain,
|
|
50
58
|
);
|
|
51
59
|
}
|
|
52
60
|
|
|
@@ -58,6 +66,7 @@ export class StrategyFactory {
|
|
|
58
66
|
initialTotalCollateral,
|
|
59
67
|
logger,
|
|
60
68
|
metrics,
|
|
69
|
+
minAmountsByChain,
|
|
61
70
|
),
|
|
62
71
|
);
|
|
63
72
|
return new CompositeStrategy(subStrategies, logger);
|
|
@@ -72,6 +81,7 @@ export class StrategyFactory {
|
|
|
72
81
|
initialTotalCollateral: bigint,
|
|
73
82
|
logger: Logger,
|
|
74
83
|
metrics?: Metrics,
|
|
84
|
+
_minAmountsByChain?: ChainMap<bigint>,
|
|
75
85
|
): IStrategy {
|
|
76
86
|
const bridgeConfigs = this.extractBridgeConfigs(strategyConfig);
|
|
77
87
|
|
|
@@ -116,13 +126,30 @@ export class StrategyFactory {
|
|
|
116
126
|
const bridgeConfigs: ChainMap<BridgeConfigWithOverride> = {};
|
|
117
127
|
|
|
118
128
|
for (const [chain, config] of Object.entries(strategyConfig.chains)) {
|
|
119
|
-
|
|
120
|
-
bridge: config.bridge,
|
|
129
|
+
const baseConfig = {
|
|
121
130
|
bridgeMinAcceptedAmount: config.bridgeMinAcceptedAmount ?? 0,
|
|
122
|
-
override: config.override as ChainMap<
|
|
123
|
-
Partial<{ bridge: string; bridgeMinAcceptedAmount: string | number }>
|
|
124
|
-
>,
|
|
125
131
|
};
|
|
132
|
+
|
|
133
|
+
const executionType =
|
|
134
|
+
config.executionType ?? ExecutionType.MovableCollateral;
|
|
135
|
+
|
|
136
|
+
if (executionType === ExecutionType.Inventory) {
|
|
137
|
+
bridgeConfigs[chain] = {
|
|
138
|
+
...baseConfig,
|
|
139
|
+
executionType: 'inventory',
|
|
140
|
+
externalBridge: config.externalBridge!, // Validated by config schema
|
|
141
|
+
override: config.override as ChainMap<Partial<InventoryBridgeConfig>>,
|
|
142
|
+
};
|
|
143
|
+
} else {
|
|
144
|
+
bridgeConfigs[chain] = {
|
|
145
|
+
...baseConfig,
|
|
146
|
+
executionType: 'movableCollateral',
|
|
147
|
+
bridge: config.bridge!, // Validated by config schema
|
|
148
|
+
override: config.override as ChainMap<
|
|
149
|
+
Partial<MovableCollateralBridgeConfig>
|
|
150
|
+
>,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
126
153
|
}
|
|
127
154
|
|
|
128
155
|
return bridgeConfigs;
|
|
@@ -258,6 +258,7 @@ describe('WeightedStrategy', () => {
|
|
|
258
258
|
destination: chain1,
|
|
259
259
|
amount: ethers.utils.parseEther('50').toBigInt(),
|
|
260
260
|
bridge: ethers.constants.AddressZero,
|
|
261
|
+
executionType: 'movableCollateral',
|
|
261
262
|
},
|
|
262
263
|
]);
|
|
263
264
|
});
|
|
@@ -325,6 +326,7 @@ describe('WeightedStrategy', () => {
|
|
|
325
326
|
destination: chain1,
|
|
326
327
|
amount: ethers.utils.parseEther('100').toBigInt(),
|
|
327
328
|
bridge: ethers.constants.AddressZero,
|
|
329
|
+
executionType: 'movableCollateral',
|
|
328
330
|
},
|
|
329
331
|
]);
|
|
330
332
|
});
|
|
@@ -363,12 +365,14 @@ describe('WeightedStrategy', () => {
|
|
|
363
365
|
destination: chain1,
|
|
364
366
|
amount: 133333333333333333333n,
|
|
365
367
|
bridge: ethers.constants.AddressZero,
|
|
368
|
+
executionType: 'movableCollateral',
|
|
366
369
|
},
|
|
367
370
|
{
|
|
368
371
|
origin: chain3,
|
|
369
372
|
destination: chain2,
|
|
370
373
|
amount: 133333333333333333333n,
|
|
371
374
|
bridge: ethers.constants.AddressZero,
|
|
375
|
+
executionType: 'movableCollateral',
|
|
372
376
|
},
|
|
373
377
|
]);
|
|
374
378
|
});
|
|
@@ -408,12 +412,14 @@ describe('WeightedStrategy', () => {
|
|
|
408
412
|
destination: chain1,
|
|
409
413
|
amount: ethers.utils.parseEther('25').toBigInt(),
|
|
410
414
|
bridge: ethers.constants.AddressZero,
|
|
415
|
+
executionType: 'movableCollateral',
|
|
411
416
|
},
|
|
412
417
|
{
|
|
413
418
|
origin: chain3,
|
|
414
419
|
destination: chain1,
|
|
415
420
|
amount: ethers.utils.parseEther('25').toBigInt(),
|
|
416
421
|
bridge: ethers.constants.AddressZero,
|
|
422
|
+
executionType: 'movableCollateral',
|
|
417
423
|
},
|
|
418
424
|
]);
|
|
419
425
|
});
|