@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.
Files changed (202) hide show
  1. package/README.md +178 -0
  2. package/dist/config/RebalancerConfig.d.ts +12 -0
  3. package/dist/config/RebalancerConfig.d.ts.map +1 -0
  4. package/dist/config/RebalancerConfig.js +29 -0
  5. package/dist/config/RebalancerConfig.js.map +1 -0
  6. package/dist/config/RebalancerConfig.test.d.ts +2 -0
  7. package/dist/config/RebalancerConfig.test.d.ts.map +1 -0
  8. package/dist/config/RebalancerConfig.test.js +325 -0
  9. package/dist/config/RebalancerConfig.test.js.map +1 -0
  10. package/dist/core/Rebalancer.d.ts +23 -0
  11. package/dist/core/Rebalancer.d.ts.map +1 -0
  12. package/dist/core/Rebalancer.js +290 -0
  13. package/dist/core/Rebalancer.js.map +1 -0
  14. package/dist/core/RebalancerService.d.ts +115 -0
  15. package/dist/core/RebalancerService.d.ts.map +1 -0
  16. package/dist/core/RebalancerService.js +227 -0
  17. package/dist/core/RebalancerService.js.map +1 -0
  18. package/dist/core/WithInflightGuard.d.ts +20 -0
  19. package/dist/core/WithInflightGuard.d.ts.map +1 -0
  20. package/dist/core/WithInflightGuard.js +47 -0
  21. package/dist/core/WithInflightGuard.js.map +1 -0
  22. package/dist/core/WithInflightGuard.test.d.ts +2 -0
  23. package/dist/core/WithInflightGuard.test.d.ts.map +1 -0
  24. package/dist/core/WithInflightGuard.test.js +64 -0
  25. package/dist/core/WithInflightGuard.test.js.map +1 -0
  26. package/dist/core/WithSemaphore.d.ts +22 -0
  27. package/dist/core/WithSemaphore.d.ts.map +1 -0
  28. package/dist/core/WithSemaphore.js +67 -0
  29. package/dist/core/WithSemaphore.js.map +1 -0
  30. package/dist/core/WithSemaphore.test.d.ts +2 -0
  31. package/dist/core/WithSemaphore.test.d.ts.map +1 -0
  32. package/dist/core/WithSemaphore.test.js +83 -0
  33. package/dist/core/WithSemaphore.test.js.map +1 -0
  34. package/dist/factories/RebalancerContextFactory.d.ts +41 -0
  35. package/dist/factories/RebalancerContextFactory.d.ts.map +1 -0
  36. package/dist/factories/RebalancerContextFactory.js +115 -0
  37. package/dist/factories/RebalancerContextFactory.js.map +1 -0
  38. package/dist/index.d.ts +33 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +35 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/interfaces/IMetrics.d.ts +5 -0
  43. package/dist/interfaces/IMetrics.d.ts.map +1 -0
  44. package/dist/interfaces/IMetrics.js +2 -0
  45. package/dist/interfaces/IMetrics.js.map +1 -0
  46. package/dist/interfaces/IMonitor.d.ts +51 -0
  47. package/dist/interfaces/IMonitor.d.ts.map +1 -0
  48. package/dist/interfaces/IMonitor.js +14 -0
  49. package/dist/interfaces/IMonitor.js.map +1 -0
  50. package/dist/interfaces/IRebalancer.d.ts +15 -0
  51. package/dist/interfaces/IRebalancer.d.ts.map +1 -0
  52. package/dist/interfaces/IRebalancer.js +2 -0
  53. package/dist/interfaces/IRebalancer.js.map +1 -0
  54. package/dist/interfaces/IStrategy.d.ts +11 -0
  55. package/dist/interfaces/IStrategy.d.ts.map +1 -0
  56. package/dist/interfaces/IStrategy.js +2 -0
  57. package/dist/interfaces/IStrategy.js.map +1 -0
  58. package/dist/metrics/Metrics.d.ts +31 -0
  59. package/dist/metrics/Metrics.d.ts.map +1 -0
  60. package/dist/metrics/Metrics.js +302 -0
  61. package/dist/metrics/Metrics.js.map +1 -0
  62. package/dist/metrics/PriceGetter.d.ts +10 -0
  63. package/dist/metrics/PriceGetter.d.ts.map +1 -0
  64. package/dist/metrics/PriceGetter.js +41 -0
  65. package/dist/metrics/PriceGetter.js.map +1 -0
  66. package/dist/metrics/scripts/metrics.d.ts +14 -0
  67. package/dist/metrics/scripts/metrics.d.ts.map +1 -0
  68. package/dist/metrics/scripts/metrics.js +198 -0
  69. package/dist/metrics/scripts/metrics.js.map +1 -0
  70. package/dist/metrics/types.d.ts +24 -0
  71. package/dist/metrics/types.d.ts.map +1 -0
  72. package/dist/metrics/types.js +2 -0
  73. package/dist/metrics/types.js.map +1 -0
  74. package/dist/metrics/utils/metrics.d.ts +12 -0
  75. package/dist/metrics/utils/metrics.d.ts.map +1 -0
  76. package/dist/metrics/utils/metrics.js +28 -0
  77. package/dist/metrics/utils/metrics.js.map +1 -0
  78. package/dist/monitor/Monitor.d.ts +26 -0
  79. package/dist/monitor/Monitor.d.ts.map +1 -0
  80. package/dist/monitor/Monitor.js +116 -0
  81. package/dist/monitor/Monitor.js.map +1 -0
  82. package/dist/service.d.ts +3 -0
  83. package/dist/service.d.ts.map +1 -0
  84. package/dist/service.js +125 -0
  85. package/dist/service.js.map +1 -0
  86. package/dist/strategy/BaseStrategy.d.ts +34 -0
  87. package/dist/strategy/BaseStrategy.d.ts.map +1 -0
  88. package/dist/strategy/BaseStrategy.js +127 -0
  89. package/dist/strategy/BaseStrategy.js.map +1 -0
  90. package/dist/strategy/MinAmountStrategy.d.ts +27 -0
  91. package/dist/strategy/MinAmountStrategy.d.ts.map +1 -0
  92. package/dist/strategy/MinAmountStrategy.js +103 -0
  93. package/dist/strategy/MinAmountStrategy.js.map +1 -0
  94. package/dist/strategy/MinAmountStrategy.test.d.ts +2 -0
  95. package/dist/strategy/MinAmountStrategy.test.d.ts.map +1 -0
  96. package/dist/strategy/MinAmountStrategy.test.js +472 -0
  97. package/dist/strategy/MinAmountStrategy.test.js.map +1 -0
  98. package/dist/strategy/StrategyFactory.d.ts +16 -0
  99. package/dist/strategy/StrategyFactory.d.ts.map +1 -0
  100. package/dist/strategy/StrategyFactory.js +25 -0
  101. package/dist/strategy/StrategyFactory.js.map +1 -0
  102. package/dist/strategy/StrategyFactory.test.d.ts +2 -0
  103. package/dist/strategy/StrategyFactory.test.d.ts.map +1 -0
  104. package/dist/strategy/StrategyFactory.test.js +80 -0
  105. package/dist/strategy/StrategyFactory.test.js.map +1 -0
  106. package/dist/strategy/WeightedStrategy.d.ts +23 -0
  107. package/dist/strategy/WeightedStrategy.d.ts.map +1 -0
  108. package/dist/strategy/WeightedStrategy.js +61 -0
  109. package/dist/strategy/WeightedStrategy.js.map +1 -0
  110. package/dist/strategy/WeightedStrategy.test.d.ts +2 -0
  111. package/dist/strategy/WeightedStrategy.test.d.ts.map +1 -0
  112. package/dist/strategy/WeightedStrategy.test.js +307 -0
  113. package/dist/strategy/WeightedStrategy.test.js.map +1 -0
  114. package/dist/strategy/index.d.ts +5 -0
  115. package/dist/strategy/index.d.ts.map +1 -0
  116. package/dist/strategy/index.js +5 -0
  117. package/dist/strategy/index.js.map +1 -0
  118. package/dist/test/helpers.d.ts +8 -0
  119. package/dist/test/helpers.d.ts.map +1 -0
  120. package/dist/test/helpers.js +33 -0
  121. package/dist/test/helpers.js.map +1 -0
  122. package/dist/utils/ExplorerClient.d.ts +14 -0
  123. package/dist/utils/ExplorerClient.d.ts.map +1 -0
  124. package/dist/utils/ExplorerClient.js +82 -0
  125. package/dist/utils/ExplorerClient.js.map +1 -0
  126. package/dist/utils/balanceUtils.d.ts +13 -0
  127. package/dist/utils/balanceUtils.d.ts.map +1 -0
  128. package/dist/utils/balanceUtils.js +43 -0
  129. package/dist/utils/balanceUtils.js.map +1 -0
  130. package/dist/utils/balanceUtils.test.d.ts +2 -0
  131. package/dist/utils/balanceUtils.test.d.ts.map +1 -0
  132. package/dist/utils/balanceUtils.test.js +54 -0
  133. package/dist/utils/balanceUtils.test.js.map +1 -0
  134. package/dist/utils/bridgeUtils.d.ts +19 -0
  135. package/dist/utils/bridgeUtils.d.ts.map +1 -0
  136. package/dist/utils/bridgeUtils.js +20 -0
  137. package/dist/utils/bridgeUtils.js.map +1 -0
  138. package/dist/utils/bridgeUtils.test.d.ts +2 -0
  139. package/dist/utils/bridgeUtils.test.d.ts.map +1 -0
  140. package/dist/utils/bridgeUtils.test.js +77 -0
  141. package/dist/utils/bridgeUtils.test.js.map +1 -0
  142. package/dist/utils/errors.d.ts +4 -0
  143. package/dist/utils/errors.d.ts.map +1 -0
  144. package/dist/utils/errors.js +6 -0
  145. package/dist/utils/errors.js.map +1 -0
  146. package/dist/utils/files.d.ts +35 -0
  147. package/dist/utils/files.d.ts.map +1 -0
  148. package/dist/utils/files.js +190 -0
  149. package/dist/utils/files.js.map +1 -0
  150. package/dist/utils/generalUtils.d.ts +3 -0
  151. package/dist/utils/generalUtils.d.ts.map +1 -0
  152. package/dist/utils/generalUtils.js +9 -0
  153. package/dist/utils/generalUtils.js.map +1 -0
  154. package/dist/utils/index.d.ts +5 -0
  155. package/dist/utils/index.d.ts.map +1 -0
  156. package/dist/utils/index.js +5 -0
  157. package/dist/utils/index.js.map +1 -0
  158. package/dist/utils/tokenUtils.d.ts +14 -0
  159. package/dist/utils/tokenUtils.d.ts.map +1 -0
  160. package/dist/utils/tokenUtils.js +21 -0
  161. package/dist/utils/tokenUtils.js.map +1 -0
  162. package/package.json +70 -0
  163. package/src/config/RebalancerConfig.test.ts +388 -0
  164. package/src/config/RebalancerConfig.ts +39 -0
  165. package/src/core/Rebalancer.ts +471 -0
  166. package/src/core/RebalancerService.ts +333 -0
  167. package/src/core/WithInflightGuard.test.ts +131 -0
  168. package/src/core/WithInflightGuard.ts +67 -0
  169. package/src/core/WithSemaphore.test.ts +112 -0
  170. package/src/core/WithSemaphore.ts +92 -0
  171. package/src/factories/RebalancerContextFactory.ts +210 -0
  172. package/src/index.ts +68 -0
  173. package/src/interfaces/IMetrics.ts +5 -0
  174. package/src/interfaces/IMonitor.ts +63 -0
  175. package/src/interfaces/IRebalancer.ts +20 -0
  176. package/src/interfaces/IStrategy.ts +13 -0
  177. package/src/metrics/Metrics.ts +558 -0
  178. package/src/metrics/PriceGetter.ts +74 -0
  179. package/src/metrics/scripts/metrics.ts +298 -0
  180. package/src/metrics/types.ts +27 -0
  181. package/src/metrics/utils/metrics.ts +33 -0
  182. package/src/monitor/Monitor.ts +174 -0
  183. package/src/service.ts +154 -0
  184. package/src/strategy/BaseStrategy.ts +210 -0
  185. package/src/strategy/MinAmountStrategy.test.ts +625 -0
  186. package/src/strategy/MinAmountStrategy.ts +170 -0
  187. package/src/strategy/StrategyFactory.test.ts +109 -0
  188. package/src/strategy/StrategyFactory.ts +48 -0
  189. package/src/strategy/WeightedStrategy.test.ts +408 -0
  190. package/src/strategy/WeightedStrategy.ts +93 -0
  191. package/src/strategy/index.ts +4 -0
  192. package/src/test/helpers.ts +46 -0
  193. package/src/utils/ExplorerClient.ts +99 -0
  194. package/src/utils/balanceUtils.test.ts +74 -0
  195. package/src/utils/balanceUtils.ts +69 -0
  196. package/src/utils/bridgeUtils.test.ts +92 -0
  197. package/src/utils/bridgeUtils.ts +42 -0
  198. package/src/utils/errors.ts +5 -0
  199. package/src/utils/files.ts +276 -0
  200. package/src/utils/generalUtils.ts +13 -0
  201. package/src/utils/index.ts +4 -0
  202. package/src/utils/tokenUtils.ts +26 -0
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Base abstract class for rebalancing strategies
3
+ */
4
+ export class BaseStrategy {
5
+ chains;
6
+ metrics;
7
+ logger;
8
+ constructor(chains, logger, metrics) {
9
+ // Rebalancing makes sense only with more than one chain.
10
+ if (chains.length < 2) {
11
+ throw new Error('At least two chains must be configured');
12
+ }
13
+ this.chains = chains;
14
+ this.logger = logger;
15
+ this.metrics = metrics;
16
+ }
17
+ /**
18
+ * Main method to get rebalancing routes
19
+ */
20
+ getRebalancingRoutes(rawBalances) {
21
+ this.logger.info({
22
+ context: this.constructor.name,
23
+ rawBalances,
24
+ }, 'Input rawBalances');
25
+ this.logger.info({
26
+ context: this.constructor.name,
27
+ }, 'Calculating rebalancing routes');
28
+ this.validateRawBalances(rawBalances);
29
+ // Get balances categorized by surplus and deficit
30
+ const { surpluses, deficits } = this.getCategorizedBalances(rawBalances);
31
+ this.logger.debug({
32
+ context: this.constructor.name,
33
+ surpluses,
34
+ }, 'Surpluses calculated');
35
+ this.logger.debug({
36
+ context: this.constructor.name,
37
+ deficits,
38
+ }, 'Deficits calculated');
39
+ // Calculate sums of surpluses and deficits
40
+ const totalSurplus = surpluses.reduce((sum, surplus) => sum + surplus.amount, 0n);
41
+ const totalDeficit = deficits.reduce((sum, deficit) => sum + deficit.amount, 0n);
42
+ this.logger.debug({
43
+ context: this.constructor.name,
44
+ totalSurplus: totalSurplus.toString(),
45
+ }, 'Total surplus calculated');
46
+ this.logger.debug({
47
+ context: this.constructor.name,
48
+ totalDeficit: totalDeficit.toString(),
49
+ }, 'Total deficit calculated');
50
+ // If total surplus is less than total deficit, scale down deficits proportionally
51
+ if (totalSurplus < totalDeficit) {
52
+ this.logger.warn({
53
+ context: this.constructor.name,
54
+ totalSurplus: totalSurplus.toString(),
55
+ totalDeficit: totalDeficit.toString(),
56
+ }, 'Deficits are greater than surpluses. Scaling deficits');
57
+ // we consider this a failure because we cannot rebalance the route completely
58
+ // however we can still transfer some amount of the deficit to reduce the imbalances
59
+ this.metrics?.recordRebalancerFailure();
60
+ for (const deficit of deficits) {
61
+ const newAmount = (deficit.amount * totalSurplus) / totalDeficit;
62
+ deficit.amount = newAmount;
63
+ }
64
+ this.logger.debug({
65
+ context: this.constructor.name,
66
+ deficits,
67
+ }, 'Scaled deficits');
68
+ }
69
+ // Sort from largest to smallest amounts as to always transfer largest amounts
70
+ // first and decrease the amount of routes required
71
+ surpluses.sort((a, b) => (a.amount > b.amount ? -1 : 1));
72
+ deficits.sort((a, b) => (a.amount > b.amount ? -1 : 1));
73
+ const routes = [];
74
+ // Transfer from surplus to deficit until all deficits are balanced.
75
+ while (deficits.length > 0 && surpluses.length > 0) {
76
+ const surplus = surpluses[0];
77
+ const deficit = deficits[0];
78
+ // Transfers the whole surplus or just the amount to balance the deficit
79
+ const transferAmount = surplus.amount > deficit.amount ? deficit.amount : surplus.amount;
80
+ // Creates the balancing route
81
+ routes.push({
82
+ origin: surplus.chain,
83
+ destination: deficit.chain,
84
+ amount: transferAmount,
85
+ });
86
+ // Decreases the amounts for the following iterations
87
+ deficit.amount -= transferAmount;
88
+ surplus.amount -= transferAmount;
89
+ // Removes the deficit if it is fully balanced
90
+ if (!deficit.amount) {
91
+ deficits.shift();
92
+ }
93
+ // Removes the surplus if it has been drained
94
+ if (!surplus.amount) {
95
+ surpluses.shift();
96
+ }
97
+ }
98
+ this.logger.debug({
99
+ context: this.constructor.name,
100
+ routes,
101
+ }, 'Generated routes');
102
+ this.logger.info({
103
+ context: this.constructor.name,
104
+ numberOfRoutes: routes.length,
105
+ }, 'Found rebalancing routes');
106
+ return routes;
107
+ }
108
+ /**
109
+ * Validates the raw balances against the chains configuration
110
+ */
111
+ validateRawBalances(rawBalances) {
112
+ const rawBalancesChains = Object.keys(rawBalances);
113
+ if (this.chains.length !== rawBalancesChains.length) {
114
+ throw new Error('Config chains do not match raw balances chains length');
115
+ }
116
+ for (const chain of this.chains) {
117
+ const balance = rawBalances[chain];
118
+ if (balance === undefined) {
119
+ throw new Error(`Raw balance for chain ${chain} not found`);
120
+ }
121
+ if (balance < 0n) {
122
+ throw new Error(`Raw balance for chain ${chain} is negative`);
123
+ }
124
+ }
125
+ }
126
+ }
127
+ //# sourceMappingURL=BaseStrategy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseStrategy.js","sourceRoot":"","sources":["../../src/strategy/BaseStrategy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import { Logger } from 'pino';
2
+ import { type ChainMap, type MinAmountStrategyConfig, type Token } from '@hyperlane-xyz/sdk';
3
+ import type { RawBalances } from '../interfaces/IStrategy.js';
4
+ import { Metrics } from '../metrics/Metrics.js';
5
+ import { BaseStrategy, type Delta } from './BaseStrategy.js';
6
+ /**
7
+ * Strategy implementation that rebalance based on minimum amounts
8
+ * It ensures each chain has at least the specified minimum amount
9
+ */
10
+ export declare class MinAmountStrategy extends BaseStrategy {
11
+ private readonly tokensByChainName;
12
+ private readonly config;
13
+ protected readonly logger: Logger;
14
+ constructor(config: MinAmountStrategyConfig, tokensByChainName: ChainMap<Token>, initialTotalCollateral: bigint, logger: Logger, metrics?: Metrics);
15
+ /**
16
+ * Gets balances categorized by surplus and deficit based on minimum amounts and targets
17
+ * - For absolute values: Uses exact token amounts
18
+ * - For relative values: Uses percentages of total balance across all chains
19
+ */
20
+ protected getCategorizedBalances(rawBalances: RawBalances): {
21
+ surpluses: Delta[];
22
+ deficits: Delta[];
23
+ };
24
+ protected getTokenByChainName(chainName: string): Token;
25
+ private validateAmounts;
26
+ }
27
+ //# sourceMappingURL=MinAmountStrategy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MinAmountStrategy.d.ts","sourceRoot":"","sources":["../../src/strategy/MinAmountStrategy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAE9B,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,uBAAuB,EAE5B,KAAK,KAAK,EACX,MAAM,oBAAoB,CAAC;AAG5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAEhD,OAAO,EAAE,YAAY,EAAE,KAAK,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE7D;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,YAAY;IAM/C,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IALpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA+B;IACtD,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAGhC,MAAM,EAAE,uBAAuB,EACd,iBAAiB,EAAE,QAAQ,CAAC,KAAK,CAAC,EACnD,sBAAsB,EAAE,MAAM,EAC9B,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,OAAO;IAqCnB;;;;OAIG;IACH,SAAS,CAAC,sBAAsB,CAAC,WAAW,EAAE,WAAW,GAAG;QAC1D,SAAS,EAAE,KAAK,EAAE,CAAC;QACnB,QAAQ,EAAE,KAAK,EAAE,CAAC;KACnB;IAmDD,SAAS,CAAC,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,KAAK;IAUvD,OAAO,CAAC,eAAe;CAkCxB"}
@@ -0,0 +1,103 @@
1
+ import { BigNumber } from 'bignumber.js';
2
+ import { RebalancerMinAmountType, } from '@hyperlane-xyz/sdk';
3
+ import { fromWei, toWei } from '@hyperlane-xyz/utils';
4
+ import { BaseStrategy } from './BaseStrategy.js';
5
+ /**
6
+ * Strategy implementation that rebalance based on minimum amounts
7
+ * It ensures each chain has at least the specified minimum amount
8
+ */
9
+ export class MinAmountStrategy extends BaseStrategy {
10
+ tokensByChainName;
11
+ config = {};
12
+ logger;
13
+ constructor(config, tokensByChainName, initialTotalCollateral, logger, metrics) {
14
+ const chains = Object.keys(config);
15
+ const log = logger.child({ class: MinAmountStrategy.name });
16
+ super(chains, log, metrics);
17
+ this.tokensByChainName = tokensByChainName;
18
+ this.logger = log;
19
+ const minAmountType = config[chains[0]].minAmount.type;
20
+ this.validateAmounts(initialTotalCollateral, minAmountType, config);
21
+ for (const chain of chains) {
22
+ const { min, target } = config[chain].minAmount;
23
+ // check range constraints
24
+ if (BigNumber(target).lt(min)) {
25
+ throw new Error(`Target (${target}) must be greater than or equal to min (${min}) for chain ${chain}`);
26
+ }
27
+ if (BigNumber(min).lt(0)) {
28
+ throw new Error(`Minimum amount (${min}) cannot be negative for chain ${chain}`);
29
+ }
30
+ if (BigNumber(target).lt(0)) {
31
+ throw new Error(`Target amount (${target}) cannot be negative for chain ${chain}`);
32
+ }
33
+ }
34
+ this.config = config;
35
+ this.logger.info('MinAmountStrategy created');
36
+ }
37
+ /**
38
+ * Gets balances categorized by surplus and deficit based on minimum amounts and targets
39
+ * - For absolute values: Uses exact token amounts
40
+ * - For relative values: Uses percentages of total balance across all chains
41
+ */
42
+ getCategorizedBalances(rawBalances) {
43
+ const totalCollateral = this.chains.reduce((sum, chain) => sum + rawBalances[chain], 0n);
44
+ return this.chains.reduce((acc, chain) => {
45
+ const config = this.config[chain];
46
+ const balance = rawBalances[chain];
47
+ let minAmount;
48
+ let targetAmount;
49
+ if (config.minAmount.type === RebalancerMinAmountType.Absolute) {
50
+ const token = this.getTokenByChainName(chain);
51
+ minAmount = BigInt(toWei(config.minAmount.min, token.decimals));
52
+ targetAmount = BigInt(toWei(config.minAmount.target, token.decimals));
53
+ }
54
+ else {
55
+ minAmount = BigInt(BigNumber(totalCollateral.toString())
56
+ .times(config.minAmount.min)
57
+ .toFixed(0, BigNumber.ROUND_FLOOR));
58
+ targetAmount = BigInt(BigNumber(totalCollateral.toString())
59
+ .times(config.minAmount.target)
60
+ .toFixed(0, BigNumber.ROUND_FLOOR));
61
+ }
62
+ // If balance is less than minAmount, it has a deficit
63
+ if (balance < minAmount) {
64
+ acc.deficits.push({ chain, amount: targetAmount - balance });
65
+ }
66
+ else {
67
+ // Any chain with more than minAmount potentially has surplus
68
+ const surplus = balance - minAmount;
69
+ if (surplus > 0n) {
70
+ acc.surpluses.push({ chain, amount: surplus });
71
+ }
72
+ }
73
+ return acc;
74
+ }, {
75
+ surpluses: [],
76
+ deficits: [],
77
+ });
78
+ }
79
+ getTokenByChainName(chainName) {
80
+ const token = this.tokensByChainName[chainName];
81
+ if (token === undefined) {
82
+ throw new Error(`Token not found for chain ${chainName}`);
83
+ }
84
+ return token;
85
+ }
86
+ validateAmounts(totalCollateral, minAmountType, config) {
87
+ config ??= this.config;
88
+ if (minAmountType === RebalancerMinAmountType.Absolute) {
89
+ let totalTargets = 0n;
90
+ let decimals = 0;
91
+ for (const chainName of this.chains) {
92
+ const token = this.getTokenByChainName(chainName);
93
+ // all the tokens have the same amount of decimals
94
+ decimals = token.decimals;
95
+ totalTargets += BigInt(toWei(config[chainName].minAmount.target, token.decimals));
96
+ }
97
+ if (totalTargets > totalCollateral) {
98
+ throw new Error(`Consider reducing the targets as the sum (${fromWei(totalTargets.toString(), decimals)}) is greater than sum of collaterals (${fromWei(totalCollateral.toString(), decimals)})`);
99
+ }
100
+ }
101
+ }
102
+ }
103
+ //# sourceMappingURL=MinAmountStrategy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MinAmountStrategy.js","sourceRoot":"","sources":["../../src/strategy/MinAmountStrategy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=MinAmountStrategy.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MinAmountStrategy.test.d.ts","sourceRoot":"","sources":["../../src/strategy/MinAmountStrategy.test.ts"],"names":[],"mappings":""}