@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,170 @@
|
|
|
1
|
+
import { BigNumber } from 'bignumber.js';
|
|
2
|
+
import { Logger } from 'pino';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
type ChainMap,
|
|
6
|
+
type MinAmountStrategyConfig,
|
|
7
|
+
RebalancerMinAmountType,
|
|
8
|
+
type Token,
|
|
9
|
+
} from '@hyperlane-xyz/sdk';
|
|
10
|
+
import { fromWei, toWei } from '@hyperlane-xyz/utils';
|
|
11
|
+
|
|
12
|
+
import type { RawBalances } from '../interfaces/IStrategy.js';
|
|
13
|
+
import { Metrics } from '../metrics/Metrics.js';
|
|
14
|
+
|
|
15
|
+
import { BaseStrategy, type Delta } from './BaseStrategy.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Strategy implementation that rebalance based on minimum amounts
|
|
19
|
+
* It ensures each chain has at least the specified minimum amount
|
|
20
|
+
*/
|
|
21
|
+
export class MinAmountStrategy extends BaseStrategy {
|
|
22
|
+
private readonly config: MinAmountStrategyConfig = {};
|
|
23
|
+
protected readonly logger: Logger;
|
|
24
|
+
|
|
25
|
+
constructor(
|
|
26
|
+
config: MinAmountStrategyConfig,
|
|
27
|
+
private readonly tokensByChainName: ChainMap<Token>,
|
|
28
|
+
initialTotalCollateral: bigint,
|
|
29
|
+
logger: Logger,
|
|
30
|
+
metrics?: Metrics,
|
|
31
|
+
) {
|
|
32
|
+
const chains = Object.keys(config);
|
|
33
|
+
const log = logger.child({ class: MinAmountStrategy.name });
|
|
34
|
+
super(chains, log, metrics);
|
|
35
|
+
this.logger = log;
|
|
36
|
+
|
|
37
|
+
const minAmountType = config[chains[0]].minAmount.type;
|
|
38
|
+
this.validateAmounts(initialTotalCollateral, minAmountType, config);
|
|
39
|
+
|
|
40
|
+
for (const chain of chains) {
|
|
41
|
+
const { min, target } = config[chain].minAmount;
|
|
42
|
+
|
|
43
|
+
// check range constraints
|
|
44
|
+
if (BigNumber(target).lt(min)) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
`Target (${target}) must be greater than or equal to min (${min}) for chain ${chain}`,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (BigNumber(min).lt(0)) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
`Minimum amount (${min}) cannot be negative for chain ${chain}`,
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (BigNumber(target).lt(0)) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`Target amount (${target}) cannot be negative for chain ${chain}`,
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this.config = config;
|
|
64
|
+
this.logger.info('MinAmountStrategy created');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Gets balances categorized by surplus and deficit based on minimum amounts and targets
|
|
69
|
+
* - For absolute values: Uses exact token amounts
|
|
70
|
+
* - For relative values: Uses percentages of total balance across all chains
|
|
71
|
+
*/
|
|
72
|
+
protected getCategorizedBalances(rawBalances: RawBalances): {
|
|
73
|
+
surpluses: Delta[];
|
|
74
|
+
deficits: Delta[];
|
|
75
|
+
} {
|
|
76
|
+
const totalCollateral = this.chains.reduce(
|
|
77
|
+
(sum, chain) => sum + rawBalances[chain],
|
|
78
|
+
0n,
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
return this.chains.reduce(
|
|
82
|
+
(acc, chain) => {
|
|
83
|
+
const config = this.config[chain];
|
|
84
|
+
const balance = rawBalances[chain];
|
|
85
|
+
let minAmount: bigint;
|
|
86
|
+
let targetAmount: bigint;
|
|
87
|
+
|
|
88
|
+
if (config.minAmount.type === RebalancerMinAmountType.Absolute) {
|
|
89
|
+
const token = this.getTokenByChainName(chain);
|
|
90
|
+
|
|
91
|
+
minAmount = BigInt(toWei(config.minAmount.min, token.decimals));
|
|
92
|
+
targetAmount = BigInt(toWei(config.minAmount.target, token.decimals));
|
|
93
|
+
} else {
|
|
94
|
+
minAmount = BigInt(
|
|
95
|
+
BigNumber(totalCollateral.toString())
|
|
96
|
+
.times(config.minAmount.min)
|
|
97
|
+
.toFixed(0, BigNumber.ROUND_FLOOR),
|
|
98
|
+
);
|
|
99
|
+
targetAmount = BigInt(
|
|
100
|
+
BigNumber(totalCollateral.toString())
|
|
101
|
+
.times(config.minAmount.target)
|
|
102
|
+
.toFixed(0, BigNumber.ROUND_FLOOR),
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// If balance is less than minAmount, it has a deficit
|
|
107
|
+
if (balance < minAmount) {
|
|
108
|
+
acc.deficits.push({ chain, amount: targetAmount - balance });
|
|
109
|
+
} else {
|
|
110
|
+
// Any chain with more than minAmount potentially has surplus
|
|
111
|
+
const surplus = balance - minAmount;
|
|
112
|
+
if (surplus > 0n) {
|
|
113
|
+
acc.surpluses.push({ chain, amount: surplus });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return acc;
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
surpluses: [] as Delta[],
|
|
121
|
+
deficits: [] as Delta[],
|
|
122
|
+
},
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
protected getTokenByChainName(chainName: string): Token {
|
|
127
|
+
const token = this.tokensByChainName[chainName];
|
|
128
|
+
|
|
129
|
+
if (token === undefined) {
|
|
130
|
+
throw new Error(`Token not found for chain ${chainName}`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return token;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private validateAmounts(
|
|
137
|
+
totalCollateral: bigint,
|
|
138
|
+
minAmountType: RebalancerMinAmountType,
|
|
139
|
+
config?: MinAmountStrategyConfig,
|
|
140
|
+
): void {
|
|
141
|
+
config ??= this.config;
|
|
142
|
+
|
|
143
|
+
if (minAmountType === RebalancerMinAmountType.Absolute) {
|
|
144
|
+
let totalTargets = 0n;
|
|
145
|
+
let decimals: number = 0;
|
|
146
|
+
|
|
147
|
+
for (const chainName of this.chains) {
|
|
148
|
+
const token = this.getTokenByChainName(chainName);
|
|
149
|
+
// all the tokens have the same amount of decimals
|
|
150
|
+
decimals = token.decimals;
|
|
151
|
+
|
|
152
|
+
totalTargets += BigInt(
|
|
153
|
+
toWei(config[chainName].minAmount.target, token.decimals),
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (totalTargets > totalCollateral) {
|
|
158
|
+
throw new Error(
|
|
159
|
+
`Consider reducing the targets as the sum (${fromWei(
|
|
160
|
+
totalTargets.toString(),
|
|
161
|
+
decimals,
|
|
162
|
+
)}) is greater than sum of collaterals (${fromWei(
|
|
163
|
+
totalCollateral.toString(),
|
|
164
|
+
decimals,
|
|
165
|
+
)})`,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { expect } from 'chai';
|
|
2
|
+
import { ethers } from 'ethers';
|
|
3
|
+
import { pino } from 'pino';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
type ChainMap,
|
|
7
|
+
MinAmountStrategyConfig,
|
|
8
|
+
RebalancerMinAmountType,
|
|
9
|
+
RebalancerStrategyOptions,
|
|
10
|
+
StrategyConfig,
|
|
11
|
+
Token,
|
|
12
|
+
TokenStandard,
|
|
13
|
+
WeightedStrategyConfig,
|
|
14
|
+
} from '@hyperlane-xyz/sdk';
|
|
15
|
+
|
|
16
|
+
import { MinAmountStrategy } from './MinAmountStrategy.js';
|
|
17
|
+
import { StrategyFactory } from './StrategyFactory.js';
|
|
18
|
+
import { WeightedStrategy } from './WeightedStrategy.js';
|
|
19
|
+
|
|
20
|
+
const testLogger = pino({ level: 'silent' });
|
|
21
|
+
|
|
22
|
+
describe('StrategyFactory', () => {
|
|
23
|
+
const chain1 = 'chain1';
|
|
24
|
+
const chain2 = 'chain2';
|
|
25
|
+
const totalCollateral = BigInt(20e18);
|
|
26
|
+
|
|
27
|
+
const tokensByChainName: ChainMap<Token> = {};
|
|
28
|
+
const tokenArgs = {
|
|
29
|
+
name: 'token',
|
|
30
|
+
decimals: 18,
|
|
31
|
+
symbol: 'TOKEN',
|
|
32
|
+
standard: TokenStandard.ERC20,
|
|
33
|
+
addressOrDenom: '',
|
|
34
|
+
};
|
|
35
|
+
tokensByChainName[chain1] = new Token({ ...tokenArgs, chainName: chain1 });
|
|
36
|
+
tokensByChainName[chain2] = new Token({ ...tokenArgs, chainName: chain2 });
|
|
37
|
+
|
|
38
|
+
describe('createStrategy', () => {
|
|
39
|
+
it('creates a WeightedStrategy when given weighted configuration', () => {
|
|
40
|
+
const config: WeightedStrategyConfig = {
|
|
41
|
+
[chain1]: {
|
|
42
|
+
weighted: {
|
|
43
|
+
weight: 100n,
|
|
44
|
+
tolerance: 0n,
|
|
45
|
+
},
|
|
46
|
+
bridge: ethers.constants.AddressZero,
|
|
47
|
+
bridgeLockTime: 1,
|
|
48
|
+
},
|
|
49
|
+
[chain2]: {
|
|
50
|
+
weighted: {
|
|
51
|
+
weight: 100n,
|
|
52
|
+
tolerance: 0n,
|
|
53
|
+
},
|
|
54
|
+
bridge: ethers.constants.AddressZero,
|
|
55
|
+
bridgeLockTime: 1,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const strategyConfig: StrategyConfig = {
|
|
60
|
+
rebalanceStrategy: RebalancerStrategyOptions.Weighted,
|
|
61
|
+
chains: config,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const strategy = StrategyFactory.createStrategy(
|
|
65
|
+
strategyConfig,
|
|
66
|
+
tokensByChainName,
|
|
67
|
+
totalCollateral,
|
|
68
|
+
testLogger,
|
|
69
|
+
);
|
|
70
|
+
expect(strategy).to.be.instanceOf(WeightedStrategy);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('creates a MinAmountStrategy when given minAmount configuration', () => {
|
|
74
|
+
const config: MinAmountStrategyConfig = {
|
|
75
|
+
[chain1]: {
|
|
76
|
+
minAmount: {
|
|
77
|
+
min: 8,
|
|
78
|
+
target: 10,
|
|
79
|
+
type: RebalancerMinAmountType.Absolute,
|
|
80
|
+
},
|
|
81
|
+
bridge: ethers.constants.AddressZero,
|
|
82
|
+
bridgeLockTime: 1,
|
|
83
|
+
},
|
|
84
|
+
[chain2]: {
|
|
85
|
+
minAmount: {
|
|
86
|
+
min: 8,
|
|
87
|
+
target: 10,
|
|
88
|
+
type: RebalancerMinAmountType.Absolute,
|
|
89
|
+
},
|
|
90
|
+
bridge: ethers.constants.AddressZero,
|
|
91
|
+
bridgeLockTime: 1,
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const strategyConfig: StrategyConfig = {
|
|
96
|
+
rebalanceStrategy: RebalancerStrategyOptions.MinAmount,
|
|
97
|
+
chains: config,
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const strategy = StrategyFactory.createStrategy(
|
|
101
|
+
strategyConfig,
|
|
102
|
+
tokensByChainName,
|
|
103
|
+
totalCollateral,
|
|
104
|
+
testLogger,
|
|
105
|
+
);
|
|
106
|
+
expect(strategy).to.be.instanceOf(MinAmountStrategy);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Logger } from 'pino';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
ChainMap,
|
|
5
|
+
RebalancerStrategyOptions,
|
|
6
|
+
StrategyConfig,
|
|
7
|
+
Token,
|
|
8
|
+
} from '@hyperlane-xyz/sdk';
|
|
9
|
+
|
|
10
|
+
import { type IStrategy } from '../interfaces/IStrategy.js';
|
|
11
|
+
import { Metrics } from '../metrics/Metrics.js';
|
|
12
|
+
|
|
13
|
+
import { MinAmountStrategy } from './MinAmountStrategy.js';
|
|
14
|
+
import { WeightedStrategy } from './WeightedStrategy.js';
|
|
15
|
+
|
|
16
|
+
export class StrategyFactory {
|
|
17
|
+
/**
|
|
18
|
+
* @param strategyConfig A discriminated union of strategy-specific configurations.
|
|
19
|
+
* @param tokensByChainName - A map of chain->token to ease the lookup of token by chain
|
|
20
|
+
* @param initialTotalCollateral - The initial total collateral of the rebalancer
|
|
21
|
+
* @param logger - The logger to use for the strategy
|
|
22
|
+
* @param metrics - The metrics to use for the strategy
|
|
23
|
+
* @returns A concrete strategy implementation
|
|
24
|
+
*/
|
|
25
|
+
static createStrategy(
|
|
26
|
+
strategyConfig: StrategyConfig,
|
|
27
|
+
tokensByChainName: ChainMap<Token>,
|
|
28
|
+
initialTotalCollateral: bigint,
|
|
29
|
+
logger: Logger,
|
|
30
|
+
metrics?: Metrics,
|
|
31
|
+
): IStrategy {
|
|
32
|
+
switch (strategyConfig.rebalanceStrategy) {
|
|
33
|
+
case RebalancerStrategyOptions.Weighted:
|
|
34
|
+
return new WeightedStrategy(strategyConfig.chains, logger, metrics);
|
|
35
|
+
case RebalancerStrategyOptions.MinAmount:
|
|
36
|
+
return new MinAmountStrategy(
|
|
37
|
+
strategyConfig.chains,
|
|
38
|
+
tokensByChainName,
|
|
39
|
+
initialTotalCollateral,
|
|
40
|
+
logger,
|
|
41
|
+
metrics,
|
|
42
|
+
);
|
|
43
|
+
default: {
|
|
44
|
+
throw new Error('Unsupported strategy type');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|