@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
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
|
|
12
12
|
import { RebalancerMinAmountType } from '../config/types.js';
|
|
13
13
|
import type { RawBalances } from '../interfaces/IStrategy.js';
|
|
14
|
+
import { extractBridgeConfigs } from '../test/helpers.js';
|
|
14
15
|
|
|
15
16
|
import { MinAmountStrategy } from './MinAmountStrategy.js';
|
|
16
17
|
|
|
@@ -58,6 +59,7 @@ describe('MinAmountStrategy', () => {
|
|
|
58
59
|
tokensByChainName,
|
|
59
60
|
totalCollateral,
|
|
60
61
|
testLogger,
|
|
62
|
+
{},
|
|
61
63
|
),
|
|
62
64
|
).to.throw('At least two chains must be configured');
|
|
63
65
|
});
|
|
@@ -87,6 +89,7 @@ describe('MinAmountStrategy', () => {
|
|
|
87
89
|
tokensByChainName,
|
|
88
90
|
totalCollateral,
|
|
89
91
|
testLogger,
|
|
92
|
+
{},
|
|
90
93
|
);
|
|
91
94
|
});
|
|
92
95
|
|
|
@@ -115,6 +118,7 @@ describe('MinAmountStrategy', () => {
|
|
|
115
118
|
tokensByChainName,
|
|
116
119
|
totalCollateral,
|
|
117
120
|
testLogger,
|
|
121
|
+
{},
|
|
118
122
|
);
|
|
119
123
|
});
|
|
120
124
|
|
|
@@ -145,6 +149,7 @@ describe('MinAmountStrategy', () => {
|
|
|
145
149
|
tokensByChainName,
|
|
146
150
|
totalCollateral,
|
|
147
151
|
testLogger,
|
|
152
|
+
{},
|
|
148
153
|
),
|
|
149
154
|
).to.throw('Minimum amount (-10) cannot be negative for chain chain2');
|
|
150
155
|
});
|
|
@@ -176,6 +181,7 @@ describe('MinAmountStrategy', () => {
|
|
|
176
181
|
tokensByChainName,
|
|
177
182
|
totalCollateral,
|
|
178
183
|
testLogger,
|
|
184
|
+
{},
|
|
179
185
|
),
|
|
180
186
|
).to.throw(
|
|
181
187
|
'Target (80) must be greater than or equal to min (100) for chain chain1',
|
|
@@ -209,6 +215,7 @@ describe('MinAmountStrategy', () => {
|
|
|
209
215
|
tokensByChainName,
|
|
210
216
|
totalCollateral,
|
|
211
217
|
testLogger,
|
|
218
|
+
{},
|
|
212
219
|
),
|
|
213
220
|
).to.throw(
|
|
214
221
|
'Target (0.4) must be greater than or equal to min (0.5) for chain chain1',
|
|
@@ -241,6 +248,7 @@ describe('MinAmountStrategy', () => {
|
|
|
241
248
|
tokensByChainName,
|
|
242
249
|
totalCollateral,
|
|
243
250
|
testLogger,
|
|
251
|
+
{},
|
|
244
252
|
).getRebalancingRoutes({
|
|
245
253
|
[chain1]: 100n,
|
|
246
254
|
[chain2]: 200n,
|
|
@@ -275,6 +283,7 @@ describe('MinAmountStrategy', () => {
|
|
|
275
283
|
tokensByChainName,
|
|
276
284
|
totalCollateral,
|
|
277
285
|
testLogger,
|
|
286
|
+
{},
|
|
278
287
|
).getRebalancingRoutes({
|
|
279
288
|
[chain1]: 100n,
|
|
280
289
|
[chain3]: 300n,
|
|
@@ -308,6 +317,7 @@ describe('MinAmountStrategy', () => {
|
|
|
308
317
|
tokensByChainName,
|
|
309
318
|
totalCollateral,
|
|
310
319
|
testLogger,
|
|
320
|
+
{},
|
|
311
321
|
).getRebalancingRoutes({
|
|
312
322
|
[chain1]: 100n,
|
|
313
323
|
[chain2]: -2n,
|
|
@@ -342,6 +352,7 @@ describe('MinAmountStrategy', () => {
|
|
|
342
352
|
tokensByChainName,
|
|
343
353
|
totalCollateral,
|
|
344
354
|
testLogger,
|
|
355
|
+
{},
|
|
345
356
|
);
|
|
346
357
|
|
|
347
358
|
const rawBalances: RawBalances = {
|
|
@@ -355,30 +366,33 @@ describe('MinAmountStrategy', () => {
|
|
|
355
366
|
});
|
|
356
367
|
|
|
357
368
|
it('should return a single route when a chain is below minimum amount', () => {
|
|
358
|
-
const
|
|
359
|
-
{
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
type: RebalancerMinAmountType.Absolute,
|
|
365
|
-
},
|
|
366
|
-
bridge: AddressZero,
|
|
367
|
-
bridgeLockTime: 1,
|
|
369
|
+
const config = {
|
|
370
|
+
[chain1]: {
|
|
371
|
+
minAmount: {
|
|
372
|
+
min: '100',
|
|
373
|
+
target: '120',
|
|
374
|
+
type: RebalancerMinAmountType.Absolute,
|
|
368
375
|
},
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
376
|
+
bridge: AddressZero,
|
|
377
|
+
bridgeLockTime: 1,
|
|
378
|
+
},
|
|
379
|
+
[chain2]: {
|
|
380
|
+
minAmount: {
|
|
381
|
+
min: '100',
|
|
382
|
+
target: '120',
|
|
383
|
+
type: RebalancerMinAmountType.Absolute,
|
|
377
384
|
},
|
|
385
|
+
bridge: AddressZero,
|
|
386
|
+
bridgeLockTime: 1,
|
|
378
387
|
},
|
|
388
|
+
};
|
|
389
|
+
const bridgeConfigs = extractBridgeConfigs(config);
|
|
390
|
+
const strategy = new MinAmountStrategy(
|
|
391
|
+
config,
|
|
379
392
|
tokensByChainName,
|
|
380
393
|
totalCollateral,
|
|
381
394
|
testLogger,
|
|
395
|
+
bridgeConfigs,
|
|
382
396
|
);
|
|
383
397
|
|
|
384
398
|
const rawBalances = {
|
|
@@ -393,44 +407,48 @@ describe('MinAmountStrategy', () => {
|
|
|
393
407
|
origin: chain2,
|
|
394
408
|
destination: chain1,
|
|
395
409
|
amount: BigInt(70e18),
|
|
410
|
+
bridge: AddressZero,
|
|
396
411
|
},
|
|
397
412
|
]);
|
|
398
413
|
});
|
|
399
414
|
|
|
400
415
|
it('should return multiple routes for multiple chains below minimum amount', () => {
|
|
401
|
-
const
|
|
402
|
-
{
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
type: RebalancerMinAmountType.Absolute,
|
|
408
|
-
},
|
|
409
|
-
bridge: AddressZero,
|
|
410
|
-
bridgeLockTime: 1,
|
|
416
|
+
const config = {
|
|
417
|
+
[chain1]: {
|
|
418
|
+
minAmount: {
|
|
419
|
+
min: '80',
|
|
420
|
+
target: '100',
|
|
421
|
+
type: RebalancerMinAmountType.Absolute,
|
|
411
422
|
},
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
423
|
+
bridge: AddressZero,
|
|
424
|
+
bridgeLockTime: 1,
|
|
425
|
+
},
|
|
426
|
+
[chain2]: {
|
|
427
|
+
minAmount: {
|
|
428
|
+
min: '80',
|
|
429
|
+
target: '100',
|
|
430
|
+
type: RebalancerMinAmountType.Absolute,
|
|
420
431
|
},
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
432
|
+
bridge: AddressZero,
|
|
433
|
+
bridgeLockTime: 1,
|
|
434
|
+
},
|
|
435
|
+
[chain3]: {
|
|
436
|
+
minAmount: {
|
|
437
|
+
min: '80',
|
|
438
|
+
target: '100',
|
|
439
|
+
type: RebalancerMinAmountType.Absolute,
|
|
429
440
|
},
|
|
441
|
+
bridge: AddressZero,
|
|
442
|
+
bridgeLockTime: 1,
|
|
430
443
|
},
|
|
444
|
+
};
|
|
445
|
+
const bridgeConfigs = extractBridgeConfigs(config);
|
|
446
|
+
const strategy = new MinAmountStrategy(
|
|
447
|
+
config,
|
|
431
448
|
tokensByChainName,
|
|
432
449
|
totalCollateral,
|
|
433
450
|
testLogger,
|
|
451
|
+
bridgeConfigs,
|
|
434
452
|
);
|
|
435
453
|
|
|
436
454
|
const rawBalances = {
|
|
@@ -446,11 +464,13 @@ describe('MinAmountStrategy', () => {
|
|
|
446
464
|
origin: chain3,
|
|
447
465
|
destination: chain1,
|
|
448
466
|
amount: BigInt(50e18),
|
|
467
|
+
bridge: AddressZero,
|
|
449
468
|
},
|
|
450
469
|
{
|
|
451
470
|
origin: chain3,
|
|
452
471
|
destination: chain2,
|
|
453
472
|
amount: BigInt(25e18),
|
|
473
|
+
bridge: AddressZero,
|
|
454
474
|
},
|
|
455
475
|
]);
|
|
456
476
|
});
|
|
@@ -482,6 +502,7 @@ describe('MinAmountStrategy', () => {
|
|
|
482
502
|
tokensByChainName,
|
|
483
503
|
totalCollateral,
|
|
484
504
|
testLogger,
|
|
505
|
+
{},
|
|
485
506
|
),
|
|
486
507
|
).to.throw(
|
|
487
508
|
`Consider reducing the targets as the sum (340) is greater than sum of collaterals (300)`,
|
|
@@ -489,39 +510,42 @@ describe('MinAmountStrategy', () => {
|
|
|
489
510
|
});
|
|
490
511
|
|
|
491
512
|
it('should handle case where there is not enough surplus to meet all minimum requirements by scaling down deficits', () => {
|
|
492
|
-
const
|
|
493
|
-
{
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
type: RebalancerMinAmountType.Absolute,
|
|
499
|
-
},
|
|
500
|
-
bridge: AddressZero,
|
|
501
|
-
bridgeLockTime: 1,
|
|
513
|
+
const config = {
|
|
514
|
+
[chain1]: {
|
|
515
|
+
minAmount: {
|
|
516
|
+
min: '100',
|
|
517
|
+
target: '100',
|
|
518
|
+
type: RebalancerMinAmountType.Absolute,
|
|
502
519
|
},
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
520
|
+
bridge: AddressZero,
|
|
521
|
+
bridgeLockTime: 1,
|
|
522
|
+
},
|
|
523
|
+
[chain2]: {
|
|
524
|
+
minAmount: {
|
|
525
|
+
min: '100',
|
|
526
|
+
target: '100',
|
|
527
|
+
type: RebalancerMinAmountType.Absolute,
|
|
511
528
|
},
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
529
|
+
bridge: AddressZero,
|
|
530
|
+
bridgeLockTime: 1,
|
|
531
|
+
},
|
|
532
|
+
[chain3]: {
|
|
533
|
+
minAmount: {
|
|
534
|
+
min: '100',
|
|
535
|
+
target: '100',
|
|
536
|
+
type: RebalancerMinAmountType.Absolute,
|
|
520
537
|
},
|
|
538
|
+
bridge: AddressZero,
|
|
539
|
+
bridgeLockTime: 1,
|
|
521
540
|
},
|
|
541
|
+
};
|
|
542
|
+
const bridgeConfigs = extractBridgeConfigs(config);
|
|
543
|
+
const strategy = new MinAmountStrategy(
|
|
544
|
+
config,
|
|
522
545
|
tokensByChainName,
|
|
523
546
|
totalCollateral,
|
|
524
547
|
testLogger,
|
|
548
|
+
bridgeConfigs,
|
|
525
549
|
);
|
|
526
550
|
|
|
527
551
|
const rawBalances = {
|
|
@@ -542,6 +566,78 @@ describe('MinAmountStrategy', () => {
|
|
|
542
566
|
expect(routes[1].amount).to.equal(BigInt(25e18));
|
|
543
567
|
});
|
|
544
568
|
|
|
569
|
+
it('should not produce zero-amount routes when scaling causes amounts to round to zero', () => {
|
|
570
|
+
// This test ensures that when deficit scaling produces zero amounts (due to integer division),
|
|
571
|
+
// these zero-amount routes are NOT included in the output.
|
|
572
|
+
// Bug scenario: totalSurplus=1, totalDeficit=3 -> each deficit of 1 scales to (1*1)/3 = 0
|
|
573
|
+
const config = {
|
|
574
|
+
[chain1]: {
|
|
575
|
+
minAmount: {
|
|
576
|
+
min: '90',
|
|
577
|
+
target: '100',
|
|
578
|
+
type: RebalancerMinAmountType.Absolute,
|
|
579
|
+
},
|
|
580
|
+
bridge: AddressZero,
|
|
581
|
+
bridgeLockTime: 1,
|
|
582
|
+
},
|
|
583
|
+
[chain2]: {
|
|
584
|
+
minAmount: {
|
|
585
|
+
min: '90',
|
|
586
|
+
target: '100',
|
|
587
|
+
type: RebalancerMinAmountType.Absolute,
|
|
588
|
+
},
|
|
589
|
+
bridge: AddressZero,
|
|
590
|
+
bridgeLockTime: 1,
|
|
591
|
+
},
|
|
592
|
+
[chain3]: {
|
|
593
|
+
minAmount: {
|
|
594
|
+
min: '90',
|
|
595
|
+
target: '100',
|
|
596
|
+
type: RebalancerMinAmountType.Absolute,
|
|
597
|
+
},
|
|
598
|
+
bridge: AddressZero,
|
|
599
|
+
bridgeLockTime: 1,
|
|
600
|
+
},
|
|
601
|
+
};
|
|
602
|
+
const bridgeConfigs = extractBridgeConfigs(config);
|
|
603
|
+
const strategy = new MinAmountStrategy(
|
|
604
|
+
config,
|
|
605
|
+
tokensByChainName,
|
|
606
|
+
totalCollateral,
|
|
607
|
+
testLogger,
|
|
608
|
+
bridgeConfigs,
|
|
609
|
+
);
|
|
610
|
+
|
|
611
|
+
// chain1 and chain2 are at 1 token each (far below min 90)
|
|
612
|
+
// chain3 has 91 tokens (surplus of 1 above min)
|
|
613
|
+
// Deficits: chain1 needs 99 (100-1), chain2 needs 99 (100-1), total = 198
|
|
614
|
+
// Surplus: chain3 has 1 (91-90)
|
|
615
|
+
// Scaling: each deficit of 99 becomes (99 * 1) / 198 = 0 (integer division!)
|
|
616
|
+
const rawBalances = {
|
|
617
|
+
[chain1]: BigInt(1e18),
|
|
618
|
+
[chain2]: BigInt(1e18),
|
|
619
|
+
[chain3]: BigInt(91e18),
|
|
620
|
+
};
|
|
621
|
+
|
|
622
|
+
const routes = strategy.getRebalancingRoutes(rawBalances);
|
|
623
|
+
|
|
624
|
+
// All routes should have non-zero amounts
|
|
625
|
+
// (Chai's greaterThan doesn't support BigInt, so use direct comparison)
|
|
626
|
+
for (const route of routes) {
|
|
627
|
+
expect(
|
|
628
|
+
route.amount > 0n,
|
|
629
|
+
`Route amount should be > 0, got ${route.amount}`,
|
|
630
|
+
).to.be.true;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// The single token of surplus may produce one route (or none if both scale to 0)
|
|
634
|
+
// Either way, no zero-amount routes should exist
|
|
635
|
+
expect(
|
|
636
|
+
routes.every((r) => r.amount > 0n),
|
|
637
|
+
'All routes must have non-zero amounts',
|
|
638
|
+
).to.be.true;
|
|
639
|
+
});
|
|
640
|
+
|
|
545
641
|
it('should have no surplus or deficit when all at min', () => {
|
|
546
642
|
const strategy = new MinAmountStrategy(
|
|
547
643
|
{
|
|
@@ -567,6 +663,7 @@ describe('MinAmountStrategy', () => {
|
|
|
567
663
|
tokensByChainName,
|
|
568
664
|
totalCollateral,
|
|
569
665
|
testLogger,
|
|
666
|
+
{},
|
|
570
667
|
);
|
|
571
668
|
|
|
572
669
|
const rawBalances = {
|
|
@@ -580,30 +677,33 @@ describe('MinAmountStrategy', () => {
|
|
|
580
677
|
});
|
|
581
678
|
|
|
582
679
|
it('should consider the target amount with relative configuration', () => {
|
|
583
|
-
const
|
|
584
|
-
{
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
type: RebalancerMinAmountType.Relative,
|
|
590
|
-
},
|
|
591
|
-
bridge: AddressZero,
|
|
592
|
-
bridgeLockTime: 1,
|
|
680
|
+
const config = {
|
|
681
|
+
[chain1]: {
|
|
682
|
+
minAmount: {
|
|
683
|
+
min: 0.25,
|
|
684
|
+
target: 0.3,
|
|
685
|
+
type: RebalancerMinAmountType.Relative,
|
|
593
686
|
},
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
687
|
+
bridge: AddressZero,
|
|
688
|
+
bridgeLockTime: 1,
|
|
689
|
+
},
|
|
690
|
+
[chain2]: {
|
|
691
|
+
minAmount: {
|
|
692
|
+
min: 0.25,
|
|
693
|
+
target: 0.3,
|
|
694
|
+
type: RebalancerMinAmountType.Relative,
|
|
602
695
|
},
|
|
696
|
+
bridge: AddressZero,
|
|
697
|
+
bridgeLockTime: 1,
|
|
603
698
|
},
|
|
699
|
+
};
|
|
700
|
+
const bridgeConfigs = extractBridgeConfigs(config);
|
|
701
|
+
const strategy = new MinAmountStrategy(
|
|
702
|
+
config,
|
|
604
703
|
tokensByChainName,
|
|
605
704
|
totalCollateral,
|
|
606
705
|
testLogger,
|
|
706
|
+
bridgeConfigs,
|
|
607
707
|
);
|
|
608
708
|
|
|
609
709
|
const rawBalances: RawBalances = {
|
|
@@ -618,6 +718,7 @@ describe('MinAmountStrategy', () => {
|
|
|
618
718
|
origin: chain2,
|
|
619
719
|
destination: chain1,
|
|
620
720
|
amount: 100n,
|
|
721
|
+
bridge: AddressZero,
|
|
621
722
|
},
|
|
622
723
|
]);
|
|
623
724
|
});
|
|
@@ -7,9 +7,15 @@ import { fromWei, toWei } from '@hyperlane-xyz/utils';
|
|
|
7
7
|
import {
|
|
8
8
|
type MinAmountStrategyConfig,
|
|
9
9
|
RebalancerMinAmountType,
|
|
10
|
+
RebalancerStrategyOptions,
|
|
10
11
|
} from '../config/types.js';
|
|
11
|
-
import type {
|
|
12
|
+
import type {
|
|
13
|
+
RawBalances,
|
|
14
|
+
Route,
|
|
15
|
+
StrategyRoute,
|
|
16
|
+
} from '../interfaces/IStrategy.js';
|
|
12
17
|
import { type Metrics } from '../metrics/Metrics.js';
|
|
18
|
+
import type { BridgeConfigWithOverride } from '../utils/bridgeUtils.js';
|
|
13
19
|
|
|
14
20
|
import { BaseStrategy, type Delta } from './BaseStrategy.js';
|
|
15
21
|
|
|
@@ -18,19 +24,21 @@ import { BaseStrategy, type Delta } from './BaseStrategy.js';
|
|
|
18
24
|
* It ensures each chain has at least the specified minimum amount
|
|
19
25
|
*/
|
|
20
26
|
export class MinAmountStrategy extends BaseStrategy {
|
|
27
|
+
readonly name = RebalancerStrategyOptions.MinAmount;
|
|
21
28
|
private readonly config: MinAmountStrategyConfig = {};
|
|
22
29
|
protected readonly logger: Logger;
|
|
23
30
|
|
|
24
31
|
constructor(
|
|
25
32
|
config: MinAmountStrategyConfig,
|
|
26
|
-
|
|
33
|
+
tokensByChainName: ChainMap<Token>,
|
|
27
34
|
initialTotalCollateral: bigint,
|
|
28
35
|
logger: Logger,
|
|
36
|
+
bridgeConfigs: ChainMap<BridgeConfigWithOverride>,
|
|
29
37
|
metrics?: Metrics,
|
|
30
38
|
) {
|
|
31
39
|
const chains = Object.keys(config);
|
|
32
40
|
const log = logger.child({ class: MinAmountStrategy.name });
|
|
33
|
-
super(chains, log, metrics);
|
|
41
|
+
super(chains, log, bridgeConfigs, metrics, tokensByChainName);
|
|
34
42
|
this.logger = log;
|
|
35
43
|
|
|
36
44
|
const minAmountType = config[chains[0]].minAmount.type;
|
|
@@ -67,37 +75,60 @@ export class MinAmountStrategy extends BaseStrategy {
|
|
|
67
75
|
* Gets balances categorized by surplus and deficit based on minimum amounts and targets
|
|
68
76
|
* - For absolute values: Uses exact token amounts
|
|
69
77
|
* - For relative values: Uses percentages of total balance across all chains
|
|
78
|
+
*
|
|
79
|
+
* Simulates both types of rebalances before calculating surpluses/deficits:
|
|
80
|
+
* - pendingRebalances: in-flight intents (origin tx confirmed, add to destination only)
|
|
81
|
+
* - proposedRebalances: routes from earlier strategies (subtract from origin AND add to destination)
|
|
82
|
+
*
|
|
83
|
+
* This prevents over-rebalancing when multiple strategies run in sequence.
|
|
70
84
|
*/
|
|
71
|
-
protected getCategorizedBalances(
|
|
85
|
+
protected getCategorizedBalances(
|
|
86
|
+
rawBalances: RawBalances,
|
|
87
|
+
pendingRebalances?: Route[],
|
|
88
|
+
proposedRebalances?: StrategyRoute[],
|
|
89
|
+
): {
|
|
72
90
|
surpluses: Delta[];
|
|
73
91
|
deficits: Delta[];
|
|
74
92
|
} {
|
|
93
|
+
// Step 1: Simulate pending rebalances (in-flight, origin already deducted on-chain)
|
|
94
|
+
let simulatedBalances = this.simulatePendingRebalances(
|
|
95
|
+
rawBalances,
|
|
96
|
+
pendingRebalances ?? [],
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
// Step 2: Simulate proposed rebalances (from earlier strategies, not yet executed)
|
|
100
|
+
simulatedBalances = this.simulateProposedRebalances(
|
|
101
|
+
simulatedBalances,
|
|
102
|
+
proposedRebalances ?? [],
|
|
103
|
+
);
|
|
75
104
|
const totalCollateral = this.chains.reduce(
|
|
76
|
-
(sum, chain) => sum +
|
|
105
|
+
(sum, chain) => sum + simulatedBalances[chain],
|
|
77
106
|
0n,
|
|
78
107
|
);
|
|
79
108
|
|
|
80
109
|
return this.chains.reduce(
|
|
81
110
|
(acc, chain) => {
|
|
82
|
-
const
|
|
83
|
-
const balance =
|
|
111
|
+
const chainConfig = this.config[chain];
|
|
112
|
+
const balance = simulatedBalances[chain];
|
|
84
113
|
let minAmount: bigint;
|
|
85
114
|
let targetAmount: bigint;
|
|
86
115
|
|
|
87
|
-
if (
|
|
116
|
+
if (chainConfig.minAmount.type === RebalancerMinAmountType.Absolute) {
|
|
88
117
|
const token = this.getTokenByChainName(chain);
|
|
89
118
|
|
|
90
|
-
minAmount = BigInt(toWei(
|
|
91
|
-
targetAmount = BigInt(
|
|
119
|
+
minAmount = BigInt(toWei(chainConfig.minAmount.min, token.decimals));
|
|
120
|
+
targetAmount = BigInt(
|
|
121
|
+
toWei(chainConfig.minAmount.target, token.decimals),
|
|
122
|
+
);
|
|
92
123
|
} else {
|
|
93
124
|
minAmount = BigInt(
|
|
94
125
|
BigNumber(totalCollateral.toString())
|
|
95
|
-
.times(
|
|
126
|
+
.times(chainConfig.minAmount.min)
|
|
96
127
|
.toFixed(0, BigNumber.ROUND_FLOOR),
|
|
97
128
|
);
|
|
98
129
|
targetAmount = BigInt(
|
|
99
130
|
BigNumber(totalCollateral.toString())
|
|
100
|
-
.times(
|
|
131
|
+
.times(chainConfig.minAmount.target)
|
|
101
132
|
.toFixed(0, BigNumber.ROUND_FLOOR),
|
|
102
133
|
);
|
|
103
134
|
}
|
|
@@ -123,7 +154,7 @@ export class MinAmountStrategy extends BaseStrategy {
|
|
|
123
154
|
}
|
|
124
155
|
|
|
125
156
|
protected getTokenByChainName(chainName: string): Token {
|
|
126
|
-
const token = this.tokensByChainName[chainName];
|
|
157
|
+
const token = this.tokensByChainName![chainName];
|
|
127
158
|
|
|
128
159
|
if (token === undefined) {
|
|
129
160
|
throw new Error(`Token not found for chain ${chainName}`);
|
|
@@ -61,7 +61,7 @@ describe('StrategyFactory', () => {
|
|
|
61
61
|
};
|
|
62
62
|
|
|
63
63
|
const strategy = StrategyFactory.createStrategy(
|
|
64
|
-
strategyConfig,
|
|
64
|
+
[strategyConfig],
|
|
65
65
|
tokensByChainName,
|
|
66
66
|
totalCollateral,
|
|
67
67
|
testLogger,
|
|
@@ -97,7 +97,7 @@ describe('StrategyFactory', () => {
|
|
|
97
97
|
};
|
|
98
98
|
|
|
99
99
|
const strategy = StrategyFactory.createStrategy(
|
|
100
|
-
strategyConfig,
|
|
100
|
+
[strategyConfig],
|
|
101
101
|
tokensByChainName,
|
|
102
102
|
totalCollateral,
|
|
103
103
|
testLogger,
|