@hyperlane-xyz/rebalancer 0.1.0-beta.5a8bd28ab
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +178 -0
- package/dist/config/RebalancerConfig.d.ts +12 -0
- package/dist/config/RebalancerConfig.d.ts.map +1 -0
- package/dist/config/RebalancerConfig.js +29 -0
- package/dist/config/RebalancerConfig.js.map +1 -0
- package/dist/config/RebalancerConfig.test.d.ts +2 -0
- package/dist/config/RebalancerConfig.test.d.ts.map +1 -0
- package/dist/config/RebalancerConfig.test.js +325 -0
- package/dist/config/RebalancerConfig.test.js.map +1 -0
- package/dist/core/Rebalancer.d.ts +23 -0
- package/dist/core/Rebalancer.d.ts.map +1 -0
- package/dist/core/Rebalancer.js +290 -0
- package/dist/core/Rebalancer.js.map +1 -0
- package/dist/core/RebalancerService.d.ts +115 -0
- package/dist/core/RebalancerService.d.ts.map +1 -0
- package/dist/core/RebalancerService.js +227 -0
- package/dist/core/RebalancerService.js.map +1 -0
- package/dist/core/WithInflightGuard.d.ts +20 -0
- package/dist/core/WithInflightGuard.d.ts.map +1 -0
- package/dist/core/WithInflightGuard.js +47 -0
- package/dist/core/WithInflightGuard.js.map +1 -0
- package/dist/core/WithInflightGuard.test.d.ts +2 -0
- package/dist/core/WithInflightGuard.test.d.ts.map +1 -0
- package/dist/core/WithInflightGuard.test.js +64 -0
- package/dist/core/WithInflightGuard.test.js.map +1 -0
- package/dist/core/WithSemaphore.d.ts +22 -0
- package/dist/core/WithSemaphore.d.ts.map +1 -0
- package/dist/core/WithSemaphore.js +67 -0
- package/dist/core/WithSemaphore.js.map +1 -0
- package/dist/core/WithSemaphore.test.d.ts +2 -0
- package/dist/core/WithSemaphore.test.d.ts.map +1 -0
- package/dist/core/WithSemaphore.test.js +83 -0
- package/dist/core/WithSemaphore.test.js.map +1 -0
- package/dist/factories/RebalancerContextFactory.d.ts +41 -0
- package/dist/factories/RebalancerContextFactory.d.ts.map +1 -0
- package/dist/factories/RebalancerContextFactory.js +115 -0
- package/dist/factories/RebalancerContextFactory.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/IMetrics.d.ts +5 -0
- package/dist/interfaces/IMetrics.d.ts.map +1 -0
- package/dist/interfaces/IMetrics.js +2 -0
- package/dist/interfaces/IMetrics.js.map +1 -0
- package/dist/interfaces/IMonitor.d.ts +51 -0
- package/dist/interfaces/IMonitor.d.ts.map +1 -0
- package/dist/interfaces/IMonitor.js +14 -0
- package/dist/interfaces/IMonitor.js.map +1 -0
- package/dist/interfaces/IRebalancer.d.ts +15 -0
- package/dist/interfaces/IRebalancer.d.ts.map +1 -0
- package/dist/interfaces/IRebalancer.js +2 -0
- package/dist/interfaces/IRebalancer.js.map +1 -0
- package/dist/interfaces/IStrategy.d.ts +11 -0
- package/dist/interfaces/IStrategy.d.ts.map +1 -0
- package/dist/interfaces/IStrategy.js +2 -0
- package/dist/interfaces/IStrategy.js.map +1 -0
- package/dist/metrics/Metrics.d.ts +31 -0
- package/dist/metrics/Metrics.d.ts.map +1 -0
- package/dist/metrics/Metrics.js +302 -0
- package/dist/metrics/Metrics.js.map +1 -0
- package/dist/metrics/PriceGetter.d.ts +10 -0
- package/dist/metrics/PriceGetter.d.ts.map +1 -0
- package/dist/metrics/PriceGetter.js +41 -0
- package/dist/metrics/PriceGetter.js.map +1 -0
- package/dist/metrics/scripts/metrics.d.ts +14 -0
- package/dist/metrics/scripts/metrics.d.ts.map +1 -0
- package/dist/metrics/scripts/metrics.js +198 -0
- package/dist/metrics/scripts/metrics.js.map +1 -0
- package/dist/metrics/types.d.ts +24 -0
- package/dist/metrics/types.d.ts.map +1 -0
- package/dist/metrics/types.js +2 -0
- package/dist/metrics/types.js.map +1 -0
- package/dist/metrics/utils/metrics.d.ts +12 -0
- package/dist/metrics/utils/metrics.d.ts.map +1 -0
- package/dist/metrics/utils/metrics.js +28 -0
- package/dist/metrics/utils/metrics.js.map +1 -0
- package/dist/monitor/Monitor.d.ts +26 -0
- package/dist/monitor/Monitor.d.ts.map +1 -0
- package/dist/monitor/Monitor.js +116 -0
- package/dist/monitor/Monitor.js.map +1 -0
- package/dist/service.d.ts +3 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +125 -0
- package/dist/service.js.map +1 -0
- package/dist/strategy/BaseStrategy.d.ts +34 -0
- package/dist/strategy/BaseStrategy.d.ts.map +1 -0
- package/dist/strategy/BaseStrategy.js +127 -0
- package/dist/strategy/BaseStrategy.js.map +1 -0
- package/dist/strategy/MinAmountStrategy.d.ts +27 -0
- package/dist/strategy/MinAmountStrategy.d.ts.map +1 -0
- package/dist/strategy/MinAmountStrategy.js +103 -0
- package/dist/strategy/MinAmountStrategy.js.map +1 -0
- package/dist/strategy/MinAmountStrategy.test.d.ts +2 -0
- package/dist/strategy/MinAmountStrategy.test.d.ts.map +1 -0
- package/dist/strategy/MinAmountStrategy.test.js +472 -0
- package/dist/strategy/MinAmountStrategy.test.js.map +1 -0
- package/dist/strategy/StrategyFactory.d.ts +16 -0
- package/dist/strategy/StrategyFactory.d.ts.map +1 -0
- package/dist/strategy/StrategyFactory.js +25 -0
- package/dist/strategy/StrategyFactory.js.map +1 -0
- package/dist/strategy/StrategyFactory.test.d.ts +2 -0
- package/dist/strategy/StrategyFactory.test.d.ts.map +1 -0
- package/dist/strategy/StrategyFactory.test.js +80 -0
- package/dist/strategy/StrategyFactory.test.js.map +1 -0
- package/dist/strategy/WeightedStrategy.d.ts +23 -0
- package/dist/strategy/WeightedStrategy.d.ts.map +1 -0
- package/dist/strategy/WeightedStrategy.js +61 -0
- package/dist/strategy/WeightedStrategy.js.map +1 -0
- package/dist/strategy/WeightedStrategy.test.d.ts +2 -0
- package/dist/strategy/WeightedStrategy.test.d.ts.map +1 -0
- package/dist/strategy/WeightedStrategy.test.js +307 -0
- package/dist/strategy/WeightedStrategy.test.js.map +1 -0
- package/dist/strategy/index.d.ts +5 -0
- package/dist/strategy/index.d.ts.map +1 -0
- package/dist/strategy/index.js +5 -0
- package/dist/strategy/index.js.map +1 -0
- package/dist/test/helpers.d.ts +8 -0
- package/dist/test/helpers.d.ts.map +1 -0
- package/dist/test/helpers.js +33 -0
- package/dist/test/helpers.js.map +1 -0
- package/dist/utils/ExplorerClient.d.ts +14 -0
- package/dist/utils/ExplorerClient.d.ts.map +1 -0
- package/dist/utils/ExplorerClient.js +82 -0
- package/dist/utils/ExplorerClient.js.map +1 -0
- package/dist/utils/balanceUtils.d.ts +13 -0
- package/dist/utils/balanceUtils.d.ts.map +1 -0
- package/dist/utils/balanceUtils.js +43 -0
- package/dist/utils/balanceUtils.js.map +1 -0
- package/dist/utils/balanceUtils.test.d.ts +2 -0
- package/dist/utils/balanceUtils.test.d.ts.map +1 -0
- package/dist/utils/balanceUtils.test.js +54 -0
- package/dist/utils/balanceUtils.test.js.map +1 -0
- package/dist/utils/bridgeUtils.d.ts +19 -0
- package/dist/utils/bridgeUtils.d.ts.map +1 -0
- package/dist/utils/bridgeUtils.js +20 -0
- package/dist/utils/bridgeUtils.js.map +1 -0
- package/dist/utils/bridgeUtils.test.d.ts +2 -0
- package/dist/utils/bridgeUtils.test.d.ts.map +1 -0
- package/dist/utils/bridgeUtils.test.js +77 -0
- package/dist/utils/bridgeUtils.test.js.map +1 -0
- package/dist/utils/errors.d.ts +4 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +6 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/files.d.ts +35 -0
- package/dist/utils/files.d.ts.map +1 -0
- package/dist/utils/files.js +190 -0
- package/dist/utils/files.js.map +1 -0
- package/dist/utils/generalUtils.d.ts +3 -0
- package/dist/utils/generalUtils.d.ts.map +1 -0
- package/dist/utils/generalUtils.js +9 -0
- package/dist/utils/generalUtils.js.map +1 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +5 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/tokenUtils.d.ts +14 -0
- package/dist/utils/tokenUtils.d.ts.map +1 -0
- package/dist/utils/tokenUtils.js +21 -0
- package/dist/utils/tokenUtils.js.map +1 -0
- package/package.json +70 -0
- package/src/config/RebalancerConfig.test.ts +388 -0
- package/src/config/RebalancerConfig.ts +39 -0
- package/src/core/Rebalancer.ts +471 -0
- package/src/core/RebalancerService.ts +333 -0
- package/src/core/WithInflightGuard.test.ts +131 -0
- package/src/core/WithInflightGuard.ts +67 -0
- package/src/core/WithSemaphore.test.ts +112 -0
- package/src/core/WithSemaphore.ts +92 -0
- package/src/factories/RebalancerContextFactory.ts +210 -0
- package/src/index.ts +68 -0
- package/src/interfaces/IMetrics.ts +5 -0
- package/src/interfaces/IMonitor.ts +63 -0
- package/src/interfaces/IRebalancer.ts +20 -0
- package/src/interfaces/IStrategy.ts +13 -0
- package/src/metrics/Metrics.ts +558 -0
- package/src/metrics/PriceGetter.ts +74 -0
- package/src/metrics/scripts/metrics.ts +298 -0
- package/src/metrics/types.ts +27 -0
- package/src/metrics/utils/metrics.ts +33 -0
- package/src/monitor/Monitor.ts +174 -0
- package/src/service.ts +154 -0
- package/src/strategy/BaseStrategy.ts +210 -0
- package/src/strategy/MinAmountStrategy.test.ts +625 -0
- package/src/strategy/MinAmountStrategy.ts +170 -0
- package/src/strategy/StrategyFactory.test.ts +109 -0
- package/src/strategy/StrategyFactory.ts +48 -0
- package/src/strategy/WeightedStrategy.test.ts +408 -0
- package/src/strategy/WeightedStrategy.ts +93 -0
- package/src/strategy/index.ts +4 -0
- package/src/test/helpers.ts +46 -0
- package/src/utils/ExplorerClient.ts +99 -0
- package/src/utils/balanceUtils.test.ts +74 -0
- package/src/utils/balanceUtils.ts +69 -0
- package/src/utils/bridgeUtils.test.ts +92 -0
- package/src/utils/bridgeUtils.ts +42 -0
- package/src/utils/errors.ts +5 -0
- package/src/utils/files.ts +276 -0
- package/src/utils/generalUtils.ts +13 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/tokenUtils.ts +26 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { Logger } from 'pino';
|
|
2
|
+
|
|
3
|
+
import type { ChainName } from '@hyperlane-xyz/sdk';
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
IStrategy,
|
|
7
|
+
RawBalances,
|
|
8
|
+
RebalancingRoute,
|
|
9
|
+
} from '../interfaces/IStrategy.js';
|
|
10
|
+
import { Metrics } from '../metrics/Metrics.js';
|
|
11
|
+
|
|
12
|
+
export type Delta = { chain: ChainName; amount: bigint };
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Base abstract class for rebalancing strategies
|
|
16
|
+
*/
|
|
17
|
+
export abstract class BaseStrategy implements IStrategy {
|
|
18
|
+
protected readonly chains: ChainName[];
|
|
19
|
+
protected readonly metrics?: Metrics;
|
|
20
|
+
protected readonly logger: Logger;
|
|
21
|
+
|
|
22
|
+
constructor(chains: ChainName[], logger: Logger, metrics?: Metrics) {
|
|
23
|
+
// Rebalancing makes sense only with more than one chain.
|
|
24
|
+
if (chains.length < 2) {
|
|
25
|
+
throw new Error('At least two chains must be configured');
|
|
26
|
+
}
|
|
27
|
+
this.chains = chains;
|
|
28
|
+
this.logger = logger;
|
|
29
|
+
this.metrics = metrics;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Main method to get rebalancing routes
|
|
34
|
+
*/
|
|
35
|
+
getRebalancingRoutes(rawBalances: RawBalances): RebalancingRoute[] {
|
|
36
|
+
this.logger.info(
|
|
37
|
+
{
|
|
38
|
+
context: this.constructor.name,
|
|
39
|
+
rawBalances,
|
|
40
|
+
},
|
|
41
|
+
'Input rawBalances',
|
|
42
|
+
);
|
|
43
|
+
this.logger.info(
|
|
44
|
+
{
|
|
45
|
+
context: this.constructor.name,
|
|
46
|
+
},
|
|
47
|
+
'Calculating rebalancing routes',
|
|
48
|
+
);
|
|
49
|
+
this.validateRawBalances(rawBalances);
|
|
50
|
+
|
|
51
|
+
// Get balances categorized by surplus and deficit
|
|
52
|
+
const { surpluses, deficits } = this.getCategorizedBalances(rawBalances);
|
|
53
|
+
|
|
54
|
+
this.logger.debug(
|
|
55
|
+
{
|
|
56
|
+
context: this.constructor.name,
|
|
57
|
+
surpluses,
|
|
58
|
+
},
|
|
59
|
+
'Surpluses calculated',
|
|
60
|
+
);
|
|
61
|
+
this.logger.debug(
|
|
62
|
+
{
|
|
63
|
+
context: this.constructor.name,
|
|
64
|
+
deficits,
|
|
65
|
+
},
|
|
66
|
+
'Deficits calculated',
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
// Calculate sums of surpluses and deficits
|
|
70
|
+
const totalSurplus = surpluses.reduce(
|
|
71
|
+
(sum, surplus) => sum + surplus.amount,
|
|
72
|
+
0n,
|
|
73
|
+
);
|
|
74
|
+
const totalDeficit = deficits.reduce(
|
|
75
|
+
(sum, deficit) => sum + deficit.amount,
|
|
76
|
+
0n,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
this.logger.debug(
|
|
80
|
+
{
|
|
81
|
+
context: this.constructor.name,
|
|
82
|
+
totalSurplus: totalSurplus.toString(),
|
|
83
|
+
},
|
|
84
|
+
'Total surplus calculated',
|
|
85
|
+
);
|
|
86
|
+
this.logger.debug(
|
|
87
|
+
{
|
|
88
|
+
context: this.constructor.name,
|
|
89
|
+
totalDeficit: totalDeficit.toString(),
|
|
90
|
+
},
|
|
91
|
+
'Total deficit calculated',
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
// If total surplus is less than total deficit, scale down deficits proportionally
|
|
95
|
+
if (totalSurplus < totalDeficit) {
|
|
96
|
+
this.logger.warn(
|
|
97
|
+
{
|
|
98
|
+
context: this.constructor.name,
|
|
99
|
+
totalSurplus: totalSurplus.toString(),
|
|
100
|
+
totalDeficit: totalDeficit.toString(),
|
|
101
|
+
},
|
|
102
|
+
'Deficits are greater than surpluses. Scaling deficits',
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
// we consider this a failure because we cannot rebalance the route completely
|
|
106
|
+
// however we can still transfer some amount of the deficit to reduce the imbalances
|
|
107
|
+
this.metrics?.recordRebalancerFailure();
|
|
108
|
+
|
|
109
|
+
for (const deficit of deficits) {
|
|
110
|
+
const newAmount = (deficit.amount * totalSurplus) / totalDeficit;
|
|
111
|
+
|
|
112
|
+
deficit.amount = newAmount;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
this.logger.debug(
|
|
116
|
+
{
|
|
117
|
+
context: this.constructor.name,
|
|
118
|
+
deficits,
|
|
119
|
+
},
|
|
120
|
+
'Scaled deficits',
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Sort from largest to smallest amounts as to always transfer largest amounts
|
|
125
|
+
// first and decrease the amount of routes required
|
|
126
|
+
surpluses.sort((a, b) => (a.amount > b.amount ? -1 : 1));
|
|
127
|
+
deficits.sort((a, b) => (a.amount > b.amount ? -1 : 1));
|
|
128
|
+
|
|
129
|
+
const routes: RebalancingRoute[] = [];
|
|
130
|
+
|
|
131
|
+
// Transfer from surplus to deficit until all deficits are balanced.
|
|
132
|
+
while (deficits.length > 0 && surpluses.length > 0) {
|
|
133
|
+
const surplus = surpluses[0];
|
|
134
|
+
const deficit = deficits[0];
|
|
135
|
+
|
|
136
|
+
// Transfers the whole surplus or just the amount to balance the deficit
|
|
137
|
+
const transferAmount =
|
|
138
|
+
surplus.amount > deficit.amount ? deficit.amount : surplus.amount;
|
|
139
|
+
|
|
140
|
+
// Creates the balancing route
|
|
141
|
+
routes.push({
|
|
142
|
+
origin: surplus.chain,
|
|
143
|
+
destination: deficit.chain,
|
|
144
|
+
amount: transferAmount,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Decreases the amounts for the following iterations
|
|
148
|
+
deficit.amount -= transferAmount;
|
|
149
|
+
surplus.amount -= transferAmount;
|
|
150
|
+
|
|
151
|
+
// Removes the deficit if it is fully balanced
|
|
152
|
+
if (!deficit.amount) {
|
|
153
|
+
deficits.shift();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Removes the surplus if it has been drained
|
|
157
|
+
if (!surplus.amount) {
|
|
158
|
+
surpluses.shift();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
this.logger.debug(
|
|
163
|
+
{
|
|
164
|
+
context: this.constructor.name,
|
|
165
|
+
routes,
|
|
166
|
+
},
|
|
167
|
+
'Generated routes',
|
|
168
|
+
);
|
|
169
|
+
this.logger.info(
|
|
170
|
+
{
|
|
171
|
+
context: this.constructor.name,
|
|
172
|
+
numberOfRoutes: routes.length,
|
|
173
|
+
},
|
|
174
|
+
'Found rebalancing routes',
|
|
175
|
+
);
|
|
176
|
+
return routes;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Abstract method to get balances categorized by surplus and deficit
|
|
181
|
+
* Each specific strategy should implement its own logic
|
|
182
|
+
*/
|
|
183
|
+
protected abstract getCategorizedBalances(rawBalances: RawBalances): {
|
|
184
|
+
surpluses: Delta[];
|
|
185
|
+
deficits: Delta[];
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Validates the raw balances against the chains configuration
|
|
190
|
+
*/
|
|
191
|
+
protected validateRawBalances(rawBalances: RawBalances): void {
|
|
192
|
+
const rawBalancesChains = Object.keys(rawBalances);
|
|
193
|
+
|
|
194
|
+
if (this.chains.length !== rawBalancesChains.length) {
|
|
195
|
+
throw new Error('Config chains do not match raw balances chains length');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
for (const chain of this.chains) {
|
|
199
|
+
const balance: bigint | undefined = rawBalances[chain];
|
|
200
|
+
|
|
201
|
+
if (balance === undefined) {
|
|
202
|
+
throw new Error(`Raw balance for chain ${chain} not found`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (balance < 0n) {
|
|
206
|
+
throw new Error(`Raw balance for chain ${chain} is negative`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|