@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
@@ -3,12 +3,17 @@ import { type Logger } from 'pino';
3
3
  import { type ChainMap, type Token } from '@hyperlane-xyz/sdk';
4
4
 
5
5
  import {
6
+ ExecutionType,
6
7
  RebalancerStrategyOptions,
7
8
  type StrategyConfig,
8
9
  } from '../config/types.js';
9
10
  import { type IStrategy } from '../interfaces/IStrategy.js';
10
11
  import { type Metrics } from '../metrics/Metrics.js';
11
- import type { BridgeConfigWithOverride } from '../utils/bridgeUtils.js';
12
+ import type {
13
+ BridgeConfigWithOverride,
14
+ InventoryBridgeConfig,
15
+ MovableCollateralBridgeConfig,
16
+ } from '../utils/bridgeUtils.js';
12
17
 
13
18
  import { CollateralDeficitStrategy } from './CollateralDeficitStrategy.js';
14
19
  import { CompositeStrategy } from './CompositeStrategy.js';
@@ -26,6 +31,7 @@ export class StrategyFactory {
26
31
  * @param initialTotalCollateral The initial total collateral of the rebalancer
27
32
  * @param logger The logger to use for the strategy
28
33
  * @param metrics The metrics to use for the strategy
34
+ * @param minAmountsByChain Optional minimum amounts per chain for filtering routes
29
35
  * @returns A concrete strategy implementation
30
36
  */
31
37
  static createStrategy(
@@ -34,6 +40,7 @@ export class StrategyFactory {
34
40
  initialTotalCollateral: bigint,
35
41
  logger: Logger,
36
42
  metrics?: Metrics,
43
+ minAmountsByChain?: ChainMap<bigint>,
37
44
  ): IStrategy {
38
45
  if (strategyConfigs.length === 0) {
39
46
  throw new Error('At least one strategy must be configured');
@@ -47,6 +54,7 @@ export class StrategyFactory {
47
54
  initialTotalCollateral,
48
55
  logger,
49
56
  metrics,
57
+ minAmountsByChain,
50
58
  );
51
59
  }
52
60
 
@@ -58,6 +66,7 @@ export class StrategyFactory {
58
66
  initialTotalCollateral,
59
67
  logger,
60
68
  metrics,
69
+ minAmountsByChain,
61
70
  ),
62
71
  );
63
72
  return new CompositeStrategy(subStrategies, logger);
@@ -72,6 +81,7 @@ export class StrategyFactory {
72
81
  initialTotalCollateral: bigint,
73
82
  logger: Logger,
74
83
  metrics?: Metrics,
84
+ _minAmountsByChain?: ChainMap<bigint>,
75
85
  ): IStrategy {
76
86
  const bridgeConfigs = this.extractBridgeConfigs(strategyConfig);
77
87
 
@@ -116,13 +126,30 @@ export class StrategyFactory {
116
126
  const bridgeConfigs: ChainMap<BridgeConfigWithOverride> = {};
117
127
 
118
128
  for (const [chain, config] of Object.entries(strategyConfig.chains)) {
119
- bridgeConfigs[chain] = {
120
- bridge: config.bridge,
129
+ const baseConfig = {
121
130
  bridgeMinAcceptedAmount: config.bridgeMinAcceptedAmount ?? 0,
122
- override: config.override as ChainMap<
123
- Partial<{ bridge: string; bridgeMinAcceptedAmount: string | number }>
124
- >,
125
131
  };
132
+
133
+ const executionType =
134
+ config.executionType ?? ExecutionType.MovableCollateral;
135
+
136
+ if (executionType === ExecutionType.Inventory) {
137
+ bridgeConfigs[chain] = {
138
+ ...baseConfig,
139
+ executionType: 'inventory',
140
+ externalBridge: config.externalBridge!, // Validated by config schema
141
+ override: config.override as ChainMap<Partial<InventoryBridgeConfig>>,
142
+ };
143
+ } else {
144
+ bridgeConfigs[chain] = {
145
+ ...baseConfig,
146
+ executionType: 'movableCollateral',
147
+ bridge: config.bridge!, // Validated by config schema
148
+ override: config.override as ChainMap<
149
+ Partial<MovableCollateralBridgeConfig>
150
+ >,
151
+ };
152
+ }
126
153
  }
127
154
 
128
155
  return bridgeConfigs;
@@ -258,6 +258,7 @@ describe('WeightedStrategy', () => {
258
258
  destination: chain1,
259
259
  amount: ethers.utils.parseEther('50').toBigInt(),
260
260
  bridge: ethers.constants.AddressZero,
261
+ executionType: 'movableCollateral',
261
262
  },
262
263
  ]);
263
264
  });
@@ -325,6 +326,7 @@ describe('WeightedStrategy', () => {
325
326
  destination: chain1,
326
327
  amount: ethers.utils.parseEther('100').toBigInt(),
327
328
  bridge: ethers.constants.AddressZero,
329
+ executionType: 'movableCollateral',
328
330
  },
329
331
  ]);
330
332
  });
@@ -363,12 +365,14 @@ describe('WeightedStrategy', () => {
363
365
  destination: chain1,
364
366
  amount: 133333333333333333333n,
365
367
  bridge: ethers.constants.AddressZero,
368
+ executionType: 'movableCollateral',
366
369
  },
367
370
  {
368
371
  origin: chain3,
369
372
  destination: chain2,
370
373
  amount: 133333333333333333333n,
371
374
  bridge: ethers.constants.AddressZero,
375
+ executionType: 'movableCollateral',
372
376
  },
373
377
  ]);
374
378
  });
@@ -408,12 +412,14 @@ describe('WeightedStrategy', () => {
408
412
  destination: chain1,
409
413
  amount: ethers.utils.parseEther('25').toBigInt(),
410
414
  bridge: ethers.constants.AddressZero,
415
+ executionType: 'movableCollateral',
411
416
  },
412
417
  {
413
418
  origin: chain3,
414
419
  destination: chain1,
415
420
  amount: ethers.utils.parseEther('25').toBigInt(),
416
421
  bridge: ethers.constants.AddressZero,
422
+ executionType: 'movableCollateral',
417
423
  },
418
424
  ]);
419
425
  });
@@ -16,18 +16,24 @@ import {
16
16
  import type { RebalancerConfig } from '../config/RebalancerConfig.js';
17
17
  import { RebalancerStrategyOptions } from '../config/types.js';
18
18
  import type {
19
+ ExecutionResult,
19
20
  IRebalancer,
21
+ MovableCollateralExecutionResult,
20
22
  PreparedTransaction,
21
- RebalanceExecutionResult,
22
- RebalanceRoute,
23
+ RebalancerType,
23
24
  } from '../interfaces/IRebalancer.js';
24
- import type { StrategyRoute } from '../interfaces/IStrategy.js';
25
- import type { BridgeConfigWithOverride } from '../utils/index.js';
25
+ import type {
26
+ MovableCollateralRoute,
27
+ StrategyRoute,
28
+ } from '../interfaces/IStrategy.js';
29
+ import type { BridgeConfigWithOverride } from '../utils/bridgeUtils.js';
26
30
 
27
31
  // === Mock Classes ===
28
32
 
29
33
  export class MockRebalancer implements IRebalancer {
30
- rebalance(_routes: RebalanceRoute[]): Promise<RebalanceExecutionResult[]> {
34
+ readonly rebalancerType: RebalancerType = 'movableCollateral';
35
+
36
+ rebalance(_routes: MovableCollateralRoute[]): Promise<ExecutionResult[]> {
31
37
  return Promise.resolve([]);
32
38
  }
33
39
  }
@@ -36,33 +42,45 @@ export class MockRebalancer implements IRebalancer {
36
42
 
37
43
  export function buildTestRoute(
38
44
  overrides: Partial<StrategyRoute> = {},
45
+ executionType: 'movableCollateral' | 'inventory' = 'movableCollateral',
39
46
  ): StrategyRoute {
47
+ if (executionType === 'inventory') {
48
+ return {
49
+ origin: 'ethereum',
50
+ destination: 'arbitrum',
51
+ amount: ethers.utils.parseEther('100').toBigInt(),
52
+ executionType: 'inventory',
53
+ externalBridge: 'lifi',
54
+ ...overrides,
55
+ } as StrategyRoute;
56
+ }
40
57
  return {
41
58
  origin: 'ethereum',
42
59
  destination: 'arbitrum',
43
60
  amount: ethers.utils.parseEther('100').toBigInt(),
61
+ executionType: 'movableCollateral',
44
62
  bridge: TEST_ADDRESSES.bridge,
45
63
  ...overrides,
46
- };
64
+ } as StrategyRoute;
47
65
  }
48
66
 
49
- export function buildTestRebalanceRoute(
50
- overrides: Partial<RebalanceRoute> = {},
51
- ): RebalanceRoute {
67
+ export function buildTestMovableCollateralRoute(
68
+ overrides: Partial<MovableCollateralRoute> = {},
69
+ ): MovableCollateralRoute {
52
70
  return {
53
- intentId: overrides.intentId ?? `test-route-${Date.now()}`,
54
71
  origin: 'ethereum',
55
72
  destination: 'arbitrum',
56
73
  amount: ethers.utils.parseEther('100').toBigInt(),
74
+ executionType: 'movableCollateral',
57
75
  bridge: TEST_ADDRESSES.bridge,
58
76
  ...overrides,
59
77
  };
60
78
  }
61
79
 
62
80
  export function buildTestResult(
63
- overrides: Partial<RebalanceExecutionResult> = {},
64
- ): RebalanceExecutionResult {
65
- const route = overrides.route ?? buildTestRebalanceRoute();
81
+ overrides: Partial<MovableCollateralExecutionResult> = {},
82
+ ): MovableCollateralExecutionResult {
83
+ const route = overrides.route ?? buildTestMovableCollateralRoute();
66
84
  return {
67
85
  route,
68
86
  success: true,
@@ -77,7 +95,12 @@ export function buildTestResult(
77
95
  export function buildTestPreparedTransaction(
78
96
  overrides: Partial<PreparedTransaction> = {},
79
97
  ): PreparedTransaction {
80
- const route = overrides.route ?? buildTestRebalanceRoute();
98
+ const route =
99
+ overrides.route ??
100
+ ({
101
+ ...buildTestMovableCollateralRoute(),
102
+ intentId: 'test-intent',
103
+ } as MovableCollateralRoute & { intentId: string });
81
104
  return {
82
105
  populatedTx: {
83
106
  to: TEST_ADDRESSES.token,
@@ -272,6 +295,7 @@ export function buildTestBridges(
272
295
  ): ChainMap<BridgeConfigWithOverride> {
273
296
  return chains.reduce((acc, chain) => {
274
297
  acc[chain] = {
298
+ executionType: 'movableCollateral',
275
299
  bridge: TEST_ADDRESSES.bridge,
276
300
  bridgeMinAcceptedAmount: 0,
277
301
  };
@@ -291,6 +315,7 @@ export function extractBridgeConfigs(
291
315
  ): ChainMap<BridgeConfigWithOverride> {
292
316
  return Object.entries(chainConfig).reduce((acc, [chain, config]) => {
293
317
  acc[chain] = {
318
+ executionType: 'movableCollateral',
294
319
  bridge: config.bridge,
295
320
  bridgeMinAcceptedAmount: config.bridgeMinAcceptedAmount ?? 0,
296
321
  };
@@ -0,0 +1,174 @@
1
+ import Sinon, { type SinonStub } from 'sinon';
2
+
3
+ import type {
4
+ BridgeQuote,
5
+ BridgeTransferStatus,
6
+ } from '../interfaces/IExternalBridge.js';
7
+
8
+ /**
9
+ * Create mock functions for LiFi SDK.
10
+ */
11
+ export function createLiFiSdkMocks() {
12
+ return {
13
+ createConfig: Sinon.stub(),
14
+ getQuote: Sinon.stub(),
15
+ executeRoute: Sinon.stub(),
16
+ getStatus: Sinon.stub(),
17
+ convertQuoteToRoute: Sinon.stub().callsFake((quote: unknown) => {
18
+ const q = quote as Record<string, unknown> & {
19
+ action?: { fromChainId?: number; toChainId?: number };
20
+ };
21
+ return {
22
+ ...q,
23
+ fromChainId: q.action?.fromChainId ?? 42161,
24
+ toChainId: q.action?.toChainId ?? 1399811149,
25
+ steps: [],
26
+ };
27
+ }),
28
+ };
29
+ }
30
+
31
+ /**
32
+ * Configure a mock getQuote to return a successful quote.
33
+ */
34
+ export function mockSuccessfulQuote(
35
+ stub: SinonStub,
36
+ overrides?: Partial<{
37
+ id: string;
38
+ tool: string;
39
+ fromAmount: string;
40
+ toAmount: string;
41
+ toAmountMin: string;
42
+ executionDuration: number;
43
+ fromChainId: number;
44
+ toChainId: number;
45
+ }>,
46
+ ) {
47
+ const fromChainId = overrides?.fromChainId ?? 42161;
48
+ const toChainId = overrides?.toChainId ?? 1399811149;
49
+ const fromAmount = overrides?.fromAmount ?? '10000000000';
50
+ const toAmount = overrides?.toAmount ?? '9950000000';
51
+ const toAmountMin = overrides?.toAmountMin ?? '9900000000';
52
+
53
+ stub.resolves({
54
+ id: overrides?.id ?? 'quote-123',
55
+ tool: overrides?.tool ?? 'across',
56
+ action: {
57
+ fromAmount,
58
+ fromChainId,
59
+ toChainId,
60
+ },
61
+ estimate: {
62
+ toAmount,
63
+ toAmountMin,
64
+ executionDuration: overrides?.executionDuration ?? 300,
65
+ },
66
+ });
67
+ }
68
+
69
+ /**
70
+ * Configure a mock executeRoute to return a successful execution.
71
+ */
72
+ export function mockSuccessfulExecution(stub: SinonStub, txHash: string) {
73
+ stub.resolves({
74
+ steps: [
75
+ {
76
+ execution: {
77
+ process: [{ txHash }],
78
+ },
79
+ },
80
+ ],
81
+ });
82
+ }
83
+
84
+ /**
85
+ * Configure a mock getStatus to return a specific status.
86
+ */
87
+ export function mockLiFiStatus(
88
+ stub: SinonStub,
89
+ status: 'DONE' | 'PENDING' | 'FAILED' | 'NOT_FOUND',
90
+ overrides?: Partial<{
91
+ receivingTxHash: string;
92
+ amount: string;
93
+ substatus: string;
94
+ }>,
95
+ ) {
96
+ const responses: Record<string, unknown> = {
97
+ DONE: {
98
+ status: 'DONE',
99
+ receiving: {
100
+ txHash: overrides?.receivingTxHash ?? '0xReceivingTxHash',
101
+ amount: overrides?.amount ?? '9950000000',
102
+ },
103
+ },
104
+ PENDING: {
105
+ status: 'PENDING',
106
+ substatus: overrides?.substatus ?? 'WAIT_SOURCE_CONFIRMATIONS',
107
+ },
108
+ FAILED: {
109
+ status: 'FAILED',
110
+ substatus: overrides?.substatus ?? 'BRIDGE_CALL_FAILED',
111
+ },
112
+ NOT_FOUND: {
113
+ status: 'NOT_FOUND',
114
+ },
115
+ };
116
+
117
+ stub.resolves(responses[status]);
118
+ }
119
+
120
+ /**
121
+ * Create a mock BridgeQuote for testing.
122
+ */
123
+ export function createMockBridgeQuote(
124
+ overrides?: Partial<BridgeQuote>,
125
+ ): BridgeQuote {
126
+ return {
127
+ id: 'quote-123',
128
+ tool: 'across',
129
+ fromAmount: 10000000000n,
130
+ toAmount: 9950000000n,
131
+ toAmountMin: 9900000000n,
132
+ executionDuration: 300,
133
+ gasCosts: 50000000n, // Default mock gas costs
134
+ feeCosts: 0n, // Default mock fee costs
135
+ route: {
136
+ action: { fromChainId: 42161, toChainId: 1399811149 },
137
+ },
138
+ ...overrides,
139
+ };
140
+ }
141
+
142
+ /**
143
+ * Create a mock BridgeTransferStatus for testing.
144
+ */
145
+ export function createMockBridgeStatus(
146
+ status: 'pending' | 'complete' | 'failed' | 'not_found',
147
+ overrides?: Partial<{
148
+ substatus: string;
149
+ receivingTxHash: string;
150
+ receivedAmount: bigint;
151
+ error: string;
152
+ }>,
153
+ ): BridgeTransferStatus {
154
+ switch (status) {
155
+ case 'pending':
156
+ return {
157
+ status: 'pending',
158
+ substatus: overrides?.substatus,
159
+ };
160
+ case 'complete':
161
+ return {
162
+ status: 'complete',
163
+ receivingTxHash: overrides?.receivingTxHash ?? '0xReceivingTxHash',
164
+ receivedAmount: overrides?.receivedAmount ?? 9950000000n,
165
+ };
166
+ case 'failed':
167
+ return {
168
+ status: 'failed',
169
+ error: overrides?.error,
170
+ };
171
+ case 'not_found':
172
+ return { status: 'not_found' };
173
+ }
174
+ }