@hyperlane-xyz/rebalancer 2.0.0 → 3.1.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.
Files changed (213) hide show
  1. package/dist/bridges/LiFiBridge.d.ts +67 -0
  2. package/dist/bridges/LiFiBridge.d.ts.map +1 -0
  3. package/dist/bridges/LiFiBridge.js +386 -0
  4. package/dist/bridges/LiFiBridge.js.map +1 -0
  5. package/dist/config/RebalancerConfig.d.ts +8 -2
  6. package/dist/config/RebalancerConfig.d.ts.map +1 -1
  7. package/dist/config/RebalancerConfig.js +9 -4
  8. package/dist/config/RebalancerConfig.js.map +1 -1
  9. package/dist/config/RebalancerConfig.test.js +135 -1
  10. package/dist/config/RebalancerConfig.test.js.map +1 -1
  11. package/dist/config/types.d.ts +1023 -304
  12. package/dist/config/types.d.ts.map +1 -1
  13. package/dist/config/types.js +113 -10
  14. package/dist/config/types.js.map +1 -1
  15. package/dist/core/InventoryRebalancer.d.ts +190 -0
  16. package/dist/core/InventoryRebalancer.d.ts.map +1 -0
  17. package/dist/core/InventoryRebalancer.js +892 -0
  18. package/dist/core/InventoryRebalancer.js.map +1 -0
  19. package/dist/core/InventoryRebalancer.test.d.ts +2 -0
  20. package/dist/core/InventoryRebalancer.test.d.ts.map +1 -0
  21. package/dist/core/InventoryRebalancer.test.js +1382 -0
  22. package/dist/core/InventoryRebalancer.test.js.map +1 -0
  23. package/dist/core/Rebalancer.d.ts +11 -4
  24. package/dist/core/Rebalancer.d.ts.map +1 -1
  25. package/dist/core/Rebalancer.js +92 -9
  26. package/dist/core/Rebalancer.js.map +1 -1
  27. package/dist/core/Rebalancer.test.js +82 -49
  28. package/dist/core/Rebalancer.test.js.map +1 -1
  29. package/dist/core/RebalancerOrchestrator.d.ts +30 -9
  30. package/dist/core/RebalancerOrchestrator.d.ts.map +1 -1
  31. package/dist/core/RebalancerOrchestrator.js +79 -71
  32. package/dist/core/RebalancerOrchestrator.js.map +1 -1
  33. package/dist/core/RebalancerOrchestrator.test.d.ts +2 -0
  34. package/dist/core/RebalancerOrchestrator.test.d.ts.map +1 -0
  35. package/dist/core/RebalancerOrchestrator.test.js +719 -0
  36. package/dist/core/RebalancerOrchestrator.test.js.map +1 -0
  37. package/dist/core/RebalancerService.d.ts +7 -3
  38. package/dist/core/RebalancerService.d.ts.map +1 -1
  39. package/dist/core/RebalancerService.js +44 -24
  40. package/dist/core/RebalancerService.js.map +1 -1
  41. package/dist/core/RebalancerService.test.js +74 -110
  42. package/dist/core/RebalancerService.test.js.map +1 -1
  43. package/dist/e2e/collateral-deficit.e2e-test.js +1 -3
  44. package/dist/e2e/collateral-deficit.e2e-test.js.map +1 -1
  45. package/dist/e2e/composite.e2e-test.js.map +1 -1
  46. package/dist/e2e/harness/BridgeSetup.d.ts +6 -0
  47. package/dist/e2e/harness/BridgeSetup.d.ts.map +1 -1
  48. package/dist/e2e/harness/BridgeSetup.js +10 -1
  49. package/dist/e2e/harness/BridgeSetup.js.map +1 -1
  50. package/dist/e2e/harness/ForkIndexer.d.ts.map +1 -1
  51. package/dist/e2e/harness/ForkIndexer.js +1 -0
  52. package/dist/e2e/harness/ForkIndexer.js.map +1 -1
  53. package/dist/e2e/harness/TestHelpers.d.ts.map +1 -1
  54. package/dist/e2e/harness/TestHelpers.js +1 -4
  55. package/dist/e2e/harness/TestHelpers.js.map +1 -1
  56. package/dist/e2e/harness/TestRebalancer.d.ts +1 -1
  57. package/dist/e2e/harness/TestRebalancer.d.ts.map +1 -1
  58. package/dist/e2e/harness/TestRebalancer.js +9 -9
  59. package/dist/e2e/harness/TestRebalancer.js.map +1 -1
  60. package/dist/e2e/minAmount.e2e-test.js +0 -1
  61. package/dist/e2e/minAmount.e2e-test.js.map +1 -1
  62. package/dist/e2e/weighted.e2e-test.js +0 -1
  63. package/dist/e2e/weighted.e2e-test.js.map +1 -1
  64. package/dist/factories/RebalancerContextFactory.d.ts +48 -6
  65. package/dist/factories/RebalancerContextFactory.d.ts.map +1 -1
  66. package/dist/factories/RebalancerContextFactory.js +171 -17
  67. package/dist/factories/RebalancerContextFactory.js.map +1 -1
  68. package/dist/index.d.ts +6 -6
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +2 -2
  71. package/dist/index.js.map +1 -1
  72. package/dist/interfaces/IExternalBridge.d.ts +101 -0
  73. package/dist/interfaces/IExternalBridge.d.ts.map +1 -0
  74. package/dist/interfaces/IExternalBridge.js +2 -0
  75. package/dist/interfaces/IExternalBridge.js.map +1 -0
  76. package/dist/interfaces/IMonitor.d.ts +1 -0
  77. package/dist/interfaces/IMonitor.d.ts.map +1 -1
  78. package/dist/interfaces/IRebalancer.d.ts +25 -25
  79. package/dist/interfaces/IRebalancer.d.ts.map +1 -1
  80. package/dist/interfaces/IStrategy.d.ts +36 -3
  81. package/dist/interfaces/IStrategy.d.ts.map +1 -1
  82. package/dist/interfaces/IStrategy.js +12 -1
  83. package/dist/interfaces/IStrategy.js.map +1 -1
  84. package/dist/metrics/PriceGetter.js +1 -1
  85. package/dist/metrics/PriceGetter.js.map +1 -1
  86. package/dist/metrics/scripts/metrics.d.ts +3 -3
  87. package/dist/monitor/Monitor.d.ts +12 -2
  88. package/dist/monitor/Monitor.d.ts.map +1 -1
  89. package/dist/monitor/Monitor.js +46 -1
  90. package/dist/monitor/Monitor.js.map +1 -1
  91. package/dist/service.js +40 -17
  92. package/dist/service.js.map +1 -1
  93. package/dist/strategy/BaseStrategy.d.ts +12 -6
  94. package/dist/strategy/BaseStrategy.d.ts.map +1 -1
  95. package/dist/strategy/BaseStrategy.js +56 -21
  96. package/dist/strategy/BaseStrategy.js.map +1 -1
  97. package/dist/strategy/CollateralDeficitStrategy.d.ts +1 -1
  98. package/dist/strategy/CollateralDeficitStrategy.d.ts.map +1 -1
  99. package/dist/strategy/CollateralDeficitStrategy.js +19 -11
  100. package/dist/strategy/CollateralDeficitStrategy.js.map +1 -1
  101. package/dist/strategy/CollateralDeficitStrategy.test.js +135 -2
  102. package/dist/strategy/CollateralDeficitStrategy.test.js.map +1 -1
  103. package/dist/strategy/CompositeStrategy.test.js +13 -0
  104. package/dist/strategy/CompositeStrategy.test.js.map +1 -1
  105. package/dist/strategy/MinAmountStrategy.test.js +4 -0
  106. package/dist/strategy/MinAmountStrategy.test.js.map +1 -1
  107. package/dist/strategy/StrategyFactory.d.ts +2 -1
  108. package/dist/strategy/StrategyFactory.d.ts.map +1 -1
  109. package/dist/strategy/StrategyFactory.js +24 -8
  110. package/dist/strategy/StrategyFactory.js.map +1 -1
  111. package/dist/strategy/WeightedStrategy.test.js +6 -0
  112. package/dist/strategy/WeightedStrategy.test.js.map +1 -1
  113. package/dist/test/helpers.d.ts +8 -7
  114. package/dist/test/helpers.d.ts.map +1 -1
  115. package/dist/test/helpers.js +23 -5
  116. package/dist/test/helpers.js.map +1 -1
  117. package/dist/test/lifiMocks.d.ts +51 -0
  118. package/dist/test/lifiMocks.d.ts.map +1 -0
  119. package/dist/test/lifiMocks.js +130 -0
  120. package/dist/test/lifiMocks.js.map +1 -0
  121. package/dist/tracking/ActionTracker.d.ts +34 -1
  122. package/dist/tracking/ActionTracker.d.ts.map +1 -1
  123. package/dist/tracking/ActionTracker.js +233 -26
  124. package/dist/tracking/ActionTracker.js.map +1 -1
  125. package/dist/tracking/ActionTracker.test.js +380 -19
  126. package/dist/tracking/ActionTracker.test.js.map +1 -1
  127. package/dist/tracking/IActionTracker.d.ts +48 -3
  128. package/dist/tracking/IActionTracker.d.ts.map +1 -1
  129. package/dist/tracking/InflightContextAdapter.d.ts.map +1 -1
  130. package/dist/tracking/InflightContextAdapter.js +24 -7
  131. package/dist/tracking/InflightContextAdapter.js.map +1 -1
  132. package/dist/tracking/InflightContextAdapter.test.js +7 -4
  133. package/dist/tracking/InflightContextAdapter.test.js.map +1 -1
  134. package/dist/tracking/types.d.ts +33 -2
  135. package/dist/tracking/types.d.ts.map +1 -1
  136. package/dist/utils/ExplorerClient.d.ts +3 -1
  137. package/dist/utils/ExplorerClient.d.ts.map +1 -1
  138. package/dist/utils/ExplorerClient.js +16 -8
  139. package/dist/utils/ExplorerClient.js.map +1 -1
  140. package/dist/utils/bridgeUtils.d.ts +27 -4
  141. package/dist/utils/bridgeUtils.d.ts.map +1 -1
  142. package/dist/utils/bridgeUtils.js +38 -0
  143. package/dist/utils/bridgeUtils.js.map +1 -1
  144. package/dist/utils/bridgeUtils.test.js +9 -0
  145. package/dist/utils/bridgeUtils.test.js.map +1 -1
  146. package/dist/utils/gasEstimation.d.ts +65 -0
  147. package/dist/utils/gasEstimation.d.ts.map +1 -0
  148. package/dist/utils/gasEstimation.js +176 -0
  149. package/dist/utils/gasEstimation.js.map +1 -0
  150. package/dist/utils/tokenUtils.d.ts +9 -1
  151. package/dist/utils/tokenUtils.d.ts.map +1 -1
  152. package/dist/utils/tokenUtils.js +11 -0
  153. package/dist/utils/tokenUtils.js.map +1 -1
  154. package/package.json +9 -7
  155. package/src/bridges/LiFiBridge.ts +538 -0
  156. package/src/config/RebalancerConfig.test.ts +162 -0
  157. package/src/config/RebalancerConfig.ts +21 -3
  158. package/src/config/types.ts +147 -10
  159. package/src/core/InventoryRebalancer.test.ts +1721 -0
  160. package/src/core/InventoryRebalancer.ts +1265 -0
  161. package/src/core/Rebalancer.test.ts +84 -30
  162. package/src/core/Rebalancer.ts +144 -23
  163. package/src/core/RebalancerOrchestrator.test.ts +869 -0
  164. package/src/core/RebalancerOrchestrator.ts +146 -95
  165. package/src/core/RebalancerService.test.ts +86 -124
  166. package/src/core/RebalancerService.ts +67 -33
  167. package/src/e2e/collateral-deficit.e2e-test.ts +2 -4
  168. package/src/e2e/composite.e2e-test.ts +5 -5
  169. package/src/e2e/harness/BridgeSetup.ts +28 -1
  170. package/src/e2e/harness/ForkIndexer.ts +1 -0
  171. package/src/e2e/harness/TestHelpers.ts +1 -4
  172. package/src/e2e/harness/TestRebalancer.ts +10 -7
  173. package/src/e2e/minAmount.e2e-test.ts +1 -2
  174. package/src/e2e/weighted.e2e-test.ts +1 -2
  175. package/src/factories/RebalancerContextFactory.ts +294 -24
  176. package/src/index.ts +22 -5
  177. package/src/interfaces/IExternalBridge.ts +115 -0
  178. package/src/interfaces/IMonitor.ts +1 -0
  179. package/src/interfaces/IRebalancer.ts +45 -29
  180. package/src/interfaces/IStrategy.ts +50 -3
  181. package/src/metrics/PriceGetter.ts +1 -1
  182. package/src/monitor/Monitor.ts +81 -2
  183. package/src/service.ts +59 -18
  184. package/src/strategy/BaseStrategy.ts +77 -24
  185. package/src/strategy/CollateralDeficitStrategy.test.ts +181 -4
  186. package/src/strategy/CollateralDeficitStrategy.ts +42 -15
  187. package/src/strategy/CompositeStrategy.test.ts +13 -0
  188. package/src/strategy/MinAmountStrategy.test.ts +4 -0
  189. package/src/strategy/StrategyFactory.ts +33 -6
  190. package/src/strategy/WeightedStrategy.test.ts +6 -0
  191. package/src/test/helpers.ts +39 -14
  192. package/src/test/lifiMocks.ts +174 -0
  193. package/src/tracking/ActionTracker.test.ts +443 -19
  194. package/src/tracking/ActionTracker.ts +339 -28
  195. package/src/tracking/IActionTracker.ts +59 -3
  196. package/src/tracking/InflightContextAdapter.test.ts +7 -4
  197. package/src/tracking/InflightContextAdapter.ts +42 -9
  198. package/src/tracking/types.ts +45 -2
  199. package/src/utils/ExplorerClient.ts +27 -10
  200. package/src/utils/bridgeUtils.test.ts +9 -0
  201. package/src/utils/bridgeUtils.ts +75 -6
  202. package/src/utils/gasEstimation.ts +272 -0
  203. package/src/utils/tokenUtils.ts +12 -0
  204. package/dist/tracking/index.d.ts +0 -7
  205. package/dist/tracking/index.d.ts.map +0 -1
  206. package/dist/tracking/index.js +0 -6
  207. package/dist/tracking/index.js.map +0 -1
  208. package/dist/utils/index.d.ts +0 -5
  209. package/dist/utils/index.d.ts.map +0 -1
  210. package/dist/utils/index.js +0 -5
  211. package/dist/utils/index.js.map +0 -1
  212. package/src/tracking/index.ts +0 -36
  213. package/src/utils/index.ts +0 -4
@@ -1,18 +1,25 @@
1
+ import type { z } from 'zod';
1
2
  import { fromZodError } from 'zod-validation-error';
2
3
 
3
4
  import { readYamlOrJson } from '@hyperlane-xyz/utils/fs';
4
5
 
5
6
  import {
7
+ type ExternalBridgesConfigSchema,
6
8
  type RebalancerConfigFileInput,
7
9
  RebalancerConfigSchema,
8
10
  type StrategyConfig,
9
11
  getStrategyChainNames,
10
12
  } from './types.js';
11
13
 
14
+ type ExternalBridgesConfig = z.infer<typeof ExternalBridgesConfigSchema>;
15
+
12
16
  export class RebalancerConfig {
13
17
  constructor(
14
18
  public readonly warpRouteId: string,
15
19
  public readonly strategyConfig: StrategyConfig[],
20
+ public readonly intentTTL: number,
21
+ public readonly inventorySigner?: string,
22
+ public readonly externalBridges?: ExternalBridgesConfig,
16
23
  ) {}
17
24
 
18
25
  /**
@@ -28,14 +35,25 @@ export class RebalancerConfig {
28
35
  throw new Error(fromZodError(validationResult.error).message);
29
36
  }
30
37
 
31
- const { warpRouteId, strategy } = validationResult.data;
38
+ const {
39
+ warpRouteId,
40
+ strategy,
41
+ intentTTL,
42
+ inventorySigner,
43
+ externalBridges,
44
+ } = validationResult.data;
32
45
 
33
- // Check that at least one chain is configured across all strategies
34
46
  const chainNames = getStrategyChainNames(strategy);
35
47
  if (chainNames.length === 0) {
36
48
  throw new Error('No chains configured');
37
49
  }
38
50
 
39
- return new RebalancerConfig(warpRouteId, strategy);
51
+ return new RebalancerConfig(
52
+ warpRouteId,
53
+ strategy,
54
+ intentTTL,
55
+ inventorySigner,
56
+ externalBridges,
57
+ );
40
58
  }
41
59
  }
@@ -23,22 +23,39 @@ export enum RebalancerMinAmountType {
23
23
  Relative = 'relative',
24
24
  }
25
25
 
26
+ /**
27
+ * Execution type for rebalancing on a chain:
28
+ * - `movableCollateral`: Uses MovableCollateralRouter.rebalance() on-chain (requires bridge address)
29
+ * - `inventory`: Uses external bridges (LiFi) + transferRemote (no bridge address needed)
30
+ */
31
+ export enum ExecutionType {
32
+ MovableCollateral = 'movableCollateral',
33
+ Inventory = 'inventory',
34
+ }
35
+
36
+ export enum ExternalBridgeType {
37
+ LiFi = 'lifi',
38
+ }
39
+
26
40
  export const RebalancerMinAmountConfigSchema = z.object({
27
41
  min: z.string().or(z.number()),
28
42
  target: z.string().or(z.number()),
29
43
  type: z.nativeEnum(RebalancerMinAmountType),
30
44
  });
31
45
 
32
- // Base chain config with common properties
33
46
  const RebalancerBridgeConfigSchema = z.object({
34
- bridge: z.string().regex(/0x[a-fA-F0-9]{40}/),
47
+ bridge: z
48
+ .string()
49
+ .regex(/0x[a-fA-F0-9]{40}/)
50
+ .optional(),
51
+ executionType: z.nativeEnum(ExecutionType).optional(),
52
+ externalBridge: z.nativeEnum(ExternalBridgeType).optional(),
35
53
  bridgeMinAcceptedAmount: z.string().or(z.number()).optional(),
36
54
  bridgeLockTime: z
37
55
  .number()
38
56
  .positive()
39
57
  .transform((val) => val * 1_000)
40
- .optional()
41
- .describe('Expected time in seconds for bridge to process a transfer'),
58
+ .optional(),
42
59
  });
43
60
 
44
61
  export const RebalancerBaseChainConfigSchema =
@@ -94,19 +111,39 @@ export const StrategyConfigSchema = z.discriminatedUnion('rebalanceStrategy', [
94
111
  CollateralDeficitStrategySchema,
95
112
  ]);
96
113
 
97
- // Accept either a single strategy (backwards compatible) or an array of strategies
98
- // Normalizes to array internally so the rest of the code doesn't need to change
99
114
  export const RebalancerStrategySchema = z
100
- .union([
101
- StrategyConfigSchema, // Old format: single object
102
- z.array(StrategyConfigSchema).min(1), // New format: array
103
- ])
115
+ .union([StrategyConfigSchema, z.array(StrategyConfigSchema).min(1)])
104
116
  .transform((val) => (Array.isArray(val) ? val : [val]));
105
117
 
118
+ export const DEFAULT_INTENT_TTL_S = 604800; // 7 days
119
+ export const DEFAULT_INTENT_TTL_MS = DEFAULT_INTENT_TTL_S * 1_000;
120
+
121
+ export const LiFiBridgeConfigSchema = z.object({
122
+ integrator: z.string(),
123
+ defaultSlippage: z.number().optional(),
124
+ });
125
+
126
+ export const ExternalBridgesConfigSchema = z.object({
127
+ lifi: LiFiBridgeConfigSchema.optional(),
128
+ });
129
+
106
130
  export const RebalancerConfigSchema = z
107
131
  .object({
108
132
  warpRouteId: z.string(),
109
133
  strategy: RebalancerStrategySchema,
134
+ inventorySigner: z
135
+ .string()
136
+ .regex(/0x[a-fA-F0-9]{40}/)
137
+ .optional(),
138
+ externalBridges: ExternalBridgesConfigSchema.optional(),
139
+ intentTTL: z
140
+ .number()
141
+ .positive()
142
+ .default(DEFAULT_INTENT_TTL_S)
143
+ .describe(
144
+ 'Max age in seconds before in-progress intent is expired. Default 7 days.',
145
+ )
146
+ .transform((val) => val * 1_000),
110
147
  })
111
148
  .superRefine((config, ctx) => {
112
149
  // CollateralDeficitStrategy must be first in composite if it is used
@@ -189,6 +226,83 @@ export const RebalancerConfigSchema = z
189
226
  });
190
227
  }
191
228
  }
229
+
230
+ // Validate bridge requirement based on executionType
231
+ for (const [chainName, chainConfig] of Object.entries(strategy.chains)) {
232
+ const executionType =
233
+ chainConfig.executionType ?? ExecutionType.MovableCollateral;
234
+
235
+ // bridge is required for movableCollateral execution type
236
+ if (
237
+ executionType === ExecutionType.MovableCollateral &&
238
+ !chainConfig.bridge
239
+ ) {
240
+ ctx.addIssue({
241
+ code: z.ZodIssueCode.custom,
242
+ message: `Chain '${chainName}' uses movableCollateral execution but has no 'bridge' address`,
243
+ path: ['strategy', strategyIndex, 'chains', chainName, 'bridge'],
244
+ });
245
+ }
246
+
247
+ // externalBridge is required for inventory execution type
248
+ if (
249
+ executionType === ExecutionType.Inventory &&
250
+ !chainConfig.externalBridge
251
+ ) {
252
+ ctx.addIssue({
253
+ code: z.ZodIssueCode.custom,
254
+ message: `Chain '${chainName}' uses inventory execution but has no 'externalBridge' configured`,
255
+ path: [
256
+ 'strategy',
257
+ strategyIndex,
258
+ 'chains',
259
+ chainName,
260
+ 'externalBridge',
261
+ ],
262
+ });
263
+ }
264
+ }
265
+ }
266
+
267
+ const hasInventoryChains = config.strategy.some((strategy) =>
268
+ Object.values(strategy.chains).some(
269
+ (chainConfig) => chainConfig.executionType === ExecutionType.Inventory,
270
+ ),
271
+ );
272
+
273
+ if (hasInventoryChains) {
274
+ if (!config.inventorySigner) {
275
+ ctx.addIssue({
276
+ code: z.ZodIssueCode.custom,
277
+ message:
278
+ 'inventorySigner is required when any chain uses inventory execution type',
279
+ path: ['inventorySigner'],
280
+ });
281
+ }
282
+
283
+ if (!config.externalBridges?.lifi?.integrator) {
284
+ ctx.addIssue({
285
+ code: z.ZodIssueCode.custom,
286
+ message:
287
+ 'externalBridges.lifi is required when using inventory execution',
288
+ path: ['externalBridges', 'lifi'],
289
+ });
290
+ }
291
+ }
292
+
293
+ for (const strategy of config.strategy) {
294
+ for (const [chainName, chainConfig] of Object.entries(strategy.chains)) {
295
+ if (
296
+ chainConfig.externalBridge === ExternalBridgeType.LiFi &&
297
+ !config.externalBridges?.lifi?.integrator
298
+ ) {
299
+ ctx.addIssue({
300
+ code: z.ZodIssueCode.custom,
301
+ message: `Chain '${chainName}' uses externalBridge: 'lifi' but externalBridges.lifi is not configured`,
302
+ path: ['externalBridges', 'lifi'],
303
+ });
304
+ }
305
+ }
192
306
  }
193
307
  });
194
308
 
@@ -261,3 +375,26 @@ export function getAllBridges(strategies: StrategyConfig[]): string[] {
261
375
 
262
376
  return Array.from(bridges);
263
377
  }
378
+
379
+ /**
380
+ * Get the execution type for a chain.
381
+ * Returns the executionType from chain config, or MovableCollateral as default.
382
+ */
383
+ export function getChainExecutionType(
384
+ strategies: StrategyConfig[],
385
+ chainName: string,
386
+ ): ExecutionType {
387
+ const chainConfig = getStrategyChainConfig(strategies, chainName);
388
+ return chainConfig?.executionType ?? ExecutionType.MovableCollateral;
389
+ }
390
+
391
+ /**
392
+ * Check if any chain in the strategies uses inventory execution type.
393
+ */
394
+ export function hasInventoryChains(strategies: StrategyConfig[]): boolean {
395
+ return strategies.some((strategy) =>
396
+ Object.values(strategy.chains).some(
397
+ (chainConfig) => chainConfig.executionType === ExecutionType.Inventory,
398
+ ),
399
+ );
400
+ }