@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.
- package/dist/bridges/LiFiBridge.d.ts +67 -0
- package/dist/bridges/LiFiBridge.d.ts.map +1 -0
- package/dist/bridges/LiFiBridge.js +386 -0
- package/dist/bridges/LiFiBridge.js.map +1 -0
- package/dist/config/RebalancerConfig.d.ts +8 -2
- package/dist/config/RebalancerConfig.d.ts.map +1 -1
- package/dist/config/RebalancerConfig.js +9 -4
- package/dist/config/RebalancerConfig.js.map +1 -1
- package/dist/config/RebalancerConfig.test.js +135 -1
- package/dist/config/RebalancerConfig.test.js.map +1 -1
- package/dist/config/types.d.ts +1023 -304
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +113 -10
- package/dist/config/types.js.map +1 -1
- package/dist/core/InventoryRebalancer.d.ts +190 -0
- package/dist/core/InventoryRebalancer.d.ts.map +1 -0
- package/dist/core/InventoryRebalancer.js +892 -0
- package/dist/core/InventoryRebalancer.js.map +1 -0
- package/dist/core/InventoryRebalancer.test.d.ts +2 -0
- package/dist/core/InventoryRebalancer.test.d.ts.map +1 -0
- package/dist/core/InventoryRebalancer.test.js +1382 -0
- package/dist/core/InventoryRebalancer.test.js.map +1 -0
- package/dist/core/Rebalancer.d.ts +11 -4
- package/dist/core/Rebalancer.d.ts.map +1 -1
- package/dist/core/Rebalancer.js +92 -9
- package/dist/core/Rebalancer.js.map +1 -1
- package/dist/core/Rebalancer.test.js +82 -49
- package/dist/core/Rebalancer.test.js.map +1 -1
- package/dist/core/RebalancerOrchestrator.d.ts +30 -9
- package/dist/core/RebalancerOrchestrator.d.ts.map +1 -1
- package/dist/core/RebalancerOrchestrator.js +79 -71
- package/dist/core/RebalancerOrchestrator.js.map +1 -1
- package/dist/core/RebalancerOrchestrator.test.d.ts +2 -0
- package/dist/core/RebalancerOrchestrator.test.d.ts.map +1 -0
- package/dist/core/RebalancerOrchestrator.test.js +719 -0
- package/dist/core/RebalancerOrchestrator.test.js.map +1 -0
- package/dist/core/RebalancerService.d.ts +7 -3
- package/dist/core/RebalancerService.d.ts.map +1 -1
- package/dist/core/RebalancerService.js +44 -24
- package/dist/core/RebalancerService.js.map +1 -1
- package/dist/core/RebalancerService.test.js +74 -110
- package/dist/core/RebalancerService.test.js.map +1 -1
- package/dist/e2e/collateral-deficit.e2e-test.js +1 -3
- package/dist/e2e/collateral-deficit.e2e-test.js.map +1 -1
- package/dist/e2e/composite.e2e-test.js.map +1 -1
- package/dist/e2e/harness/BridgeSetup.d.ts +6 -0
- package/dist/e2e/harness/BridgeSetup.d.ts.map +1 -1
- package/dist/e2e/harness/BridgeSetup.js +10 -1
- package/dist/e2e/harness/BridgeSetup.js.map +1 -1
- package/dist/e2e/harness/ForkIndexer.d.ts.map +1 -1
- package/dist/e2e/harness/ForkIndexer.js +1 -0
- package/dist/e2e/harness/ForkIndexer.js.map +1 -1
- package/dist/e2e/harness/TestHelpers.d.ts.map +1 -1
- package/dist/e2e/harness/TestHelpers.js +1 -4
- package/dist/e2e/harness/TestHelpers.js.map +1 -1
- package/dist/e2e/harness/TestRebalancer.d.ts +1 -1
- package/dist/e2e/harness/TestRebalancer.d.ts.map +1 -1
- package/dist/e2e/harness/TestRebalancer.js +9 -9
- package/dist/e2e/harness/TestRebalancer.js.map +1 -1
- package/dist/e2e/minAmount.e2e-test.js +0 -1
- package/dist/e2e/minAmount.e2e-test.js.map +1 -1
- package/dist/e2e/weighted.e2e-test.js +0 -1
- package/dist/e2e/weighted.e2e-test.js.map +1 -1
- package/dist/factories/RebalancerContextFactory.d.ts +48 -6
- package/dist/factories/RebalancerContextFactory.d.ts.map +1 -1
- package/dist/factories/RebalancerContextFactory.js +171 -17
- package/dist/factories/RebalancerContextFactory.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/interfaces/IExternalBridge.d.ts +101 -0
- package/dist/interfaces/IExternalBridge.d.ts.map +1 -0
- package/dist/interfaces/IExternalBridge.js +2 -0
- package/dist/interfaces/IExternalBridge.js.map +1 -0
- package/dist/interfaces/IMonitor.d.ts +1 -0
- package/dist/interfaces/IMonitor.d.ts.map +1 -1
- package/dist/interfaces/IRebalancer.d.ts +25 -25
- package/dist/interfaces/IRebalancer.d.ts.map +1 -1
- package/dist/interfaces/IStrategy.d.ts +36 -3
- package/dist/interfaces/IStrategy.d.ts.map +1 -1
- package/dist/interfaces/IStrategy.js +12 -1
- package/dist/interfaces/IStrategy.js.map +1 -1
- package/dist/metrics/PriceGetter.js +1 -1
- package/dist/metrics/PriceGetter.js.map +1 -1
- package/dist/metrics/scripts/metrics.d.ts +3 -3
- package/dist/monitor/Monitor.d.ts +12 -2
- package/dist/monitor/Monitor.d.ts.map +1 -1
- package/dist/monitor/Monitor.js +46 -1
- package/dist/monitor/Monitor.js.map +1 -1
- package/dist/service.js +40 -17
- package/dist/service.js.map +1 -1
- package/dist/strategy/BaseStrategy.d.ts +12 -6
- package/dist/strategy/BaseStrategy.d.ts.map +1 -1
- package/dist/strategy/BaseStrategy.js +56 -21
- package/dist/strategy/BaseStrategy.js.map +1 -1
- package/dist/strategy/CollateralDeficitStrategy.d.ts +1 -1
- package/dist/strategy/CollateralDeficitStrategy.d.ts.map +1 -1
- package/dist/strategy/CollateralDeficitStrategy.js +19 -11
- package/dist/strategy/CollateralDeficitStrategy.js.map +1 -1
- package/dist/strategy/CollateralDeficitStrategy.test.js +135 -2
- package/dist/strategy/CollateralDeficitStrategy.test.js.map +1 -1
- package/dist/strategy/CompositeStrategy.test.js +13 -0
- package/dist/strategy/CompositeStrategy.test.js.map +1 -1
- package/dist/strategy/MinAmountStrategy.test.js +4 -0
- package/dist/strategy/MinAmountStrategy.test.js.map +1 -1
- package/dist/strategy/StrategyFactory.d.ts +2 -1
- package/dist/strategy/StrategyFactory.d.ts.map +1 -1
- package/dist/strategy/StrategyFactory.js +24 -8
- package/dist/strategy/StrategyFactory.js.map +1 -1
- package/dist/strategy/WeightedStrategy.test.js +6 -0
- package/dist/strategy/WeightedStrategy.test.js.map +1 -1
- package/dist/test/helpers.d.ts +8 -7
- package/dist/test/helpers.d.ts.map +1 -1
- package/dist/test/helpers.js +23 -5
- package/dist/test/helpers.js.map +1 -1
- package/dist/test/lifiMocks.d.ts +51 -0
- package/dist/test/lifiMocks.d.ts.map +1 -0
- package/dist/test/lifiMocks.js +130 -0
- package/dist/test/lifiMocks.js.map +1 -0
- package/dist/tracking/ActionTracker.d.ts +34 -1
- package/dist/tracking/ActionTracker.d.ts.map +1 -1
- package/dist/tracking/ActionTracker.js +233 -26
- package/dist/tracking/ActionTracker.js.map +1 -1
- package/dist/tracking/ActionTracker.test.js +380 -19
- package/dist/tracking/ActionTracker.test.js.map +1 -1
- package/dist/tracking/IActionTracker.d.ts +48 -3
- package/dist/tracking/IActionTracker.d.ts.map +1 -1
- package/dist/tracking/InflightContextAdapter.d.ts.map +1 -1
- package/dist/tracking/InflightContextAdapter.js +24 -7
- package/dist/tracking/InflightContextAdapter.js.map +1 -1
- package/dist/tracking/InflightContextAdapter.test.js +7 -4
- package/dist/tracking/InflightContextAdapter.test.js.map +1 -1
- package/dist/tracking/types.d.ts +33 -2
- package/dist/tracking/types.d.ts.map +1 -1
- package/dist/utils/ExplorerClient.d.ts +3 -1
- package/dist/utils/ExplorerClient.d.ts.map +1 -1
- package/dist/utils/ExplorerClient.js +16 -8
- package/dist/utils/ExplorerClient.js.map +1 -1
- package/dist/utils/bridgeUtils.d.ts +27 -4
- package/dist/utils/bridgeUtils.d.ts.map +1 -1
- package/dist/utils/bridgeUtils.js +38 -0
- package/dist/utils/bridgeUtils.js.map +1 -1
- package/dist/utils/bridgeUtils.test.js +9 -0
- package/dist/utils/bridgeUtils.test.js.map +1 -1
- package/dist/utils/gasEstimation.d.ts +65 -0
- package/dist/utils/gasEstimation.d.ts.map +1 -0
- package/dist/utils/gasEstimation.js +176 -0
- package/dist/utils/gasEstimation.js.map +1 -0
- package/dist/utils/tokenUtils.d.ts +9 -1
- package/dist/utils/tokenUtils.d.ts.map +1 -1
- package/dist/utils/tokenUtils.js +11 -0
- package/dist/utils/tokenUtils.js.map +1 -1
- package/package.json +9 -7
- package/src/bridges/LiFiBridge.ts +538 -0
- package/src/config/RebalancerConfig.test.ts +162 -0
- package/src/config/RebalancerConfig.ts +21 -3
- package/src/config/types.ts +147 -10
- package/src/core/InventoryRebalancer.test.ts +1721 -0
- package/src/core/InventoryRebalancer.ts +1265 -0
- package/src/core/Rebalancer.test.ts +84 -30
- package/src/core/Rebalancer.ts +144 -23
- package/src/core/RebalancerOrchestrator.test.ts +869 -0
- package/src/core/RebalancerOrchestrator.ts +146 -95
- package/src/core/RebalancerService.test.ts +86 -124
- package/src/core/RebalancerService.ts +67 -33
- package/src/e2e/collateral-deficit.e2e-test.ts +2 -4
- package/src/e2e/composite.e2e-test.ts +5 -5
- package/src/e2e/harness/BridgeSetup.ts +28 -1
- package/src/e2e/harness/ForkIndexer.ts +1 -0
- package/src/e2e/harness/TestHelpers.ts +1 -4
- package/src/e2e/harness/TestRebalancer.ts +10 -7
- package/src/e2e/minAmount.e2e-test.ts +1 -2
- package/src/e2e/weighted.e2e-test.ts +1 -2
- package/src/factories/RebalancerContextFactory.ts +294 -24
- package/src/index.ts +22 -5
- package/src/interfaces/IExternalBridge.ts +115 -0
- package/src/interfaces/IMonitor.ts +1 -0
- package/src/interfaces/IRebalancer.ts +45 -29
- package/src/interfaces/IStrategy.ts +50 -3
- package/src/metrics/PriceGetter.ts +1 -1
- package/src/monitor/Monitor.ts +81 -2
- package/src/service.ts +59 -18
- package/src/strategy/BaseStrategy.ts +77 -24
- package/src/strategy/CollateralDeficitStrategy.test.ts +181 -4
- package/src/strategy/CollateralDeficitStrategy.ts +42 -15
- package/src/strategy/CompositeStrategy.test.ts +13 -0
- package/src/strategy/MinAmountStrategy.test.ts +4 -0
- package/src/strategy/StrategyFactory.ts +33 -6
- package/src/strategy/WeightedStrategy.test.ts +6 -0
- package/src/test/helpers.ts +39 -14
- package/src/test/lifiMocks.ts +174 -0
- package/src/tracking/ActionTracker.test.ts +443 -19
- package/src/tracking/ActionTracker.ts +339 -28
- package/src/tracking/IActionTracker.ts +59 -3
- package/src/tracking/InflightContextAdapter.test.ts +7 -4
- package/src/tracking/InflightContextAdapter.ts +42 -9
- package/src/tracking/types.ts +45 -2
- package/src/utils/ExplorerClient.ts +27 -10
- package/src/utils/bridgeUtils.test.ts +9 -0
- package/src/utils/bridgeUtils.ts +75 -6
- package/src/utils/gasEstimation.ts +272 -0
- package/src/utils/tokenUtils.ts +12 -0
- package/dist/tracking/index.d.ts +0 -7
- package/dist/tracking/index.d.ts.map +0 -1
- package/dist/tracking/index.js +0 -6
- package/dist/tracking/index.js.map +0 -1
- package/dist/utils/index.d.ts +0 -5
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js +0 -5
- package/dist/utils/index.js.map +0 -1
- package/src/tracking/index.ts +0 -36
- package/src/utils/index.ts +0 -4
package/src/index.ts
CHANGED
|
@@ -20,6 +20,8 @@ export { Rebalancer } from './core/Rebalancer.js';
|
|
|
20
20
|
// Configuration
|
|
21
21
|
export { RebalancerConfig } from './config/RebalancerConfig.js';
|
|
22
22
|
export {
|
|
23
|
+
DEFAULT_INTENT_TTL_MS,
|
|
24
|
+
DEFAULT_INTENT_TTL_S,
|
|
23
25
|
getStrategyChainConfig,
|
|
24
26
|
getStrategyChainNames,
|
|
25
27
|
RebalancerBaseChainConfigSchema,
|
|
@@ -57,16 +59,23 @@ export { PriceGetter } from './metrics/PriceGetter.js';
|
|
|
57
59
|
|
|
58
60
|
// Interfaces
|
|
59
61
|
export type {
|
|
62
|
+
ExecutionResult,
|
|
63
|
+
IInventoryRebalancer,
|
|
64
|
+
IMovableCollateralRebalancer,
|
|
60
65
|
IRebalancer,
|
|
66
|
+
InventoryExecutionResult,
|
|
67
|
+
MovableCollateralExecutionResult,
|
|
61
68
|
PreparedTransaction,
|
|
62
|
-
|
|
63
|
-
RebalanceExecutionResult,
|
|
69
|
+
RebalancerType,
|
|
64
70
|
} from './interfaces/IRebalancer.js';
|
|
65
71
|
export type {
|
|
66
72
|
IStrategy,
|
|
67
|
-
StrategyRoute,
|
|
68
|
-
RawBalances,
|
|
69
73
|
InflightContext,
|
|
74
|
+
InventoryRoute,
|
|
75
|
+
MovableCollateralRoute,
|
|
76
|
+
RawBalances,
|
|
77
|
+
Route,
|
|
78
|
+
StrategyRoute,
|
|
70
79
|
} from './interfaces/IStrategy.js';
|
|
71
80
|
export type {
|
|
72
81
|
ConfirmedBlockTag,
|
|
@@ -82,10 +91,16 @@ export {
|
|
|
82
91
|
export type { IMetrics } from './interfaces/IMetrics.js';
|
|
83
92
|
|
|
84
93
|
// Utils
|
|
85
|
-
export {
|
|
94
|
+
export {
|
|
95
|
+
getBridgeConfig,
|
|
96
|
+
isMovableCollateralConfig,
|
|
97
|
+
isInventoryConfig,
|
|
98
|
+
} from './utils/bridgeUtils.js';
|
|
86
99
|
export type {
|
|
87
100
|
BridgeConfigWithOverride,
|
|
88
101
|
BridgeConfig,
|
|
102
|
+
MovableCollateralBridgeConfig,
|
|
103
|
+
InventoryBridgeConfig,
|
|
89
104
|
} from './utils/bridgeUtils.js';
|
|
90
105
|
export { getRawBalances } from './utils/balanceUtils.js';
|
|
91
106
|
export { isCollateralizedTokenEligibleForRebalancing } from './utils/tokenUtils.js';
|
|
@@ -102,6 +117,8 @@ export type {
|
|
|
102
117
|
Transfer,
|
|
103
118
|
RebalanceIntent,
|
|
104
119
|
RebalanceAction,
|
|
120
|
+
ActionType,
|
|
121
|
+
PartialInventoryIntent,
|
|
105
122
|
} from './tracking/types.js';
|
|
106
123
|
|
|
107
124
|
// Factory
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { Logger } from 'pino';
|
|
2
|
+
|
|
3
|
+
import type { ChainMap, ChainMetadata } from '@hyperlane-xyz/sdk';
|
|
4
|
+
|
|
5
|
+
import type { ExternalBridgeType } from '../config/types.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Configuration for an external bridge.
|
|
9
|
+
*/
|
|
10
|
+
export interface ExternalBridgeConfig {
|
|
11
|
+
integrator: string; // Required: dApp/company name for bridge integration
|
|
12
|
+
apiKey?: string; // Optional: API key for higher rate limits
|
|
13
|
+
defaultSlippage?: number; // Default slippage tolerance (e.g., 0.005 = 0.5%)
|
|
14
|
+
chainMetadata?: ChainMap<ChainMetadata>; // Optional: chain metadata for resolving RPC URLs by chainId
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Parameters for requesting a bridge quote.
|
|
19
|
+
* Either fromAmount OR toAmount must be provided (mutually exclusive).
|
|
20
|
+
* - fromAmount: "I'm sending X, what do I get?" (standard quote)
|
|
21
|
+
* - toAmount: "I want X on destination, how much do I send?" (reverse quote)
|
|
22
|
+
*/
|
|
23
|
+
export interface BridgeQuoteParams {
|
|
24
|
+
fromChain: number; // Source chain ID
|
|
25
|
+
toChain: number; // Destination chain ID
|
|
26
|
+
fromToken: string; // Source token address
|
|
27
|
+
toToken: string; // Destination token address
|
|
28
|
+
fromAmount?: bigint; // Amount to send (in token decimals) - use for "I'm sending X"
|
|
29
|
+
toAmount?: bigint; // Amount to receive (in token decimals) - use for "I want X"
|
|
30
|
+
fromAddress: string; // Sender address
|
|
31
|
+
toAddress?: string; // Recipient address (defaults to fromAddress)
|
|
32
|
+
slippage?: number; // Slippage tolerance (overrides default)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Quote response from a bridge.
|
|
37
|
+
*/
|
|
38
|
+
export interface BridgeQuote {
|
|
39
|
+
id: string; // Unique quote identifier
|
|
40
|
+
tool: string; // Bridge/DEX tool used (e.g., 'stargate', 'across')
|
|
41
|
+
fromAmount: bigint; // Amount being sent (input required)
|
|
42
|
+
toAmount: bigint; // Expected amount to receive
|
|
43
|
+
toAmountMin: bigint; // Minimum amount to receive (after slippage)
|
|
44
|
+
executionDuration: number; // Estimated execution time in seconds
|
|
45
|
+
gasCosts: bigint; // Sum of gas costs for the bridge operation
|
|
46
|
+
feeCosts: bigint; // Sum of non-included fee costs (protocol fees, etc.)
|
|
47
|
+
route: unknown; // Bridge-specific route data for execution
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Result of executing a bridge transfer.
|
|
52
|
+
*/
|
|
53
|
+
export interface BridgeTransferResult {
|
|
54
|
+
txHash: string; // Origin chain transaction hash
|
|
55
|
+
fromChain: number; // Source chain ID
|
|
56
|
+
toChain: number; // Destination chain ID
|
|
57
|
+
transferId?: string; // Bridge-specific transfer identifier
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Status of a bridge transfer.
|
|
62
|
+
*/
|
|
63
|
+
export type BridgeTransferStatus =
|
|
64
|
+
| { status: 'pending'; substatus?: string }
|
|
65
|
+
| { status: 'complete'; receivingTxHash: string; receivedAmount: bigint }
|
|
66
|
+
| { status: 'failed'; error?: string }
|
|
67
|
+
| { status: 'not_found' };
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Interface for external bridge implementations (e.g., LiFi, Socket).
|
|
71
|
+
*
|
|
72
|
+
* External bridges are used for inventory rebalancing when chains don't support
|
|
73
|
+
* MovableCollateralRouter. The flow is:
|
|
74
|
+
* 1. quote() - Get a quote for bridging tokens
|
|
75
|
+
* 2. execute() - Execute the bridge transfer
|
|
76
|
+
* 3. getStatus() - Poll for transfer completion
|
|
77
|
+
*/
|
|
78
|
+
export interface IExternalBridge {
|
|
79
|
+
readonly externalBridgeId: string;
|
|
80
|
+
readonly logger: Logger;
|
|
81
|
+
|
|
82
|
+
getNativeTokenAddress?(): string;
|
|
83
|
+
|
|
84
|
+
quote(params: BridgeQuoteParams): Promise<BridgeQuote>;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Execute a bridge transfer using a previously obtained quote.
|
|
88
|
+
* @param quote - Quote obtained from quote()
|
|
89
|
+
* @param privateKey - Private key hex string (0x-prefixed) for signing the transaction
|
|
90
|
+
*/
|
|
91
|
+
execute(
|
|
92
|
+
quote: BridgeQuote,
|
|
93
|
+
privateKey: string,
|
|
94
|
+
): Promise<BridgeTransferResult>;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Get the status of a bridge transfer.
|
|
98
|
+
* @param txHash - Origin chain transaction hash
|
|
99
|
+
* @param fromChain - Source chain ID
|
|
100
|
+
* @param toChain - Destination chain ID
|
|
101
|
+
*/
|
|
102
|
+
getStatus(
|
|
103
|
+
txHash: string,
|
|
104
|
+
fromChain: number,
|
|
105
|
+
toChain: number,
|
|
106
|
+
): Promise<BridgeTransferStatus>;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Registry mapping external bridge types to their implementations.
|
|
111
|
+
*/
|
|
112
|
+
export type ExternalBridgeRegistry = Record<
|
|
113
|
+
ExternalBridgeType,
|
|
114
|
+
IExternalBridge
|
|
115
|
+
>;
|
|
@@ -3,39 +3,55 @@ import {
|
|
|
3
3
|
type TokenAmount,
|
|
4
4
|
} from '@hyperlane-xyz/sdk';
|
|
5
5
|
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
6
|
+
import {
|
|
7
|
+
type InventoryRoute,
|
|
8
|
+
type MovableCollateralRoute,
|
|
9
|
+
type Route,
|
|
10
|
+
} from './IStrategy.js';
|
|
11
|
+
|
|
12
|
+
export type RebalancerType = 'movableCollateral' | 'inventory';
|
|
13
|
+
|
|
14
|
+
export interface ExecutionResult<R extends Route = Route> {
|
|
15
|
+
route: R;
|
|
16
|
+
success: boolean;
|
|
17
|
+
error?: string;
|
|
18
|
+
// messageId?: string;
|
|
19
|
+
txHash?: string;
|
|
20
|
+
// amountSent?: bigint;
|
|
21
|
+
reason?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface MovableCollateralExecutionResult extends ExecutionResult<MovableCollateralRoute> {
|
|
25
|
+
messageId: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface InventoryExecutionResult extends ExecutionResult<InventoryRoute> {
|
|
29
|
+
messageId?: string;
|
|
30
|
+
amountSent?: bigint;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface IRebalancer<
|
|
34
|
+
R extends Route = Route,
|
|
35
|
+
E extends ExecutionResult<R> = ExecutionResult<R>,
|
|
36
|
+
> {
|
|
37
|
+
readonly rebalancerType: RebalancerType;
|
|
38
|
+
rebalance(routes: R[]): Promise<E[]>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type IMovableCollateralRebalancer = IRebalancer<
|
|
42
|
+
MovableCollateralRoute,
|
|
43
|
+
MovableCollateralExecutionResult
|
|
44
|
+
>;
|
|
45
|
+
|
|
46
|
+
export type IInventoryRebalancer = IRebalancer<
|
|
47
|
+
InventoryRoute,
|
|
48
|
+
InventoryExecutionResult
|
|
49
|
+
>;
|
|
17
50
|
|
|
18
51
|
export type PreparedTransaction = {
|
|
19
52
|
populatedTx: Awaited<
|
|
20
53
|
ReturnType<EvmMovableCollateralAdapter['populateRebalanceTx']>
|
|
21
54
|
>;
|
|
22
|
-
route:
|
|
55
|
+
route: MovableCollateralRoute & { intentId: string };
|
|
23
56
|
originTokenAmount: TokenAmount;
|
|
24
57
|
};
|
|
25
|
-
|
|
26
|
-
export type RebalanceMetrics = {
|
|
27
|
-
route: RebalanceRoute;
|
|
28
|
-
originTokenAmount: TokenAmount;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
export type RebalanceExecutionResult = {
|
|
32
|
-
route: RebalanceRoute;
|
|
33
|
-
success: boolean;
|
|
34
|
-
messageId?: string;
|
|
35
|
-
txHash?: string;
|
|
36
|
-
error?: string;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
export interface IRebalancer {
|
|
40
|
-
rebalance(routes: RebalanceRoute[]): Promise<RebalanceExecutionResult[]>;
|
|
41
|
-
}
|
|
@@ -1,26 +1,73 @@
|
|
|
1
1
|
import { type ChainMap, type ChainName } from '@hyperlane-xyz/sdk';
|
|
2
2
|
import type { Address } from '@hyperlane-xyz/utils';
|
|
3
3
|
|
|
4
|
+
import { ExternalBridgeType } from '../config/types.js';
|
|
5
|
+
import { ExecutionMethod } from '../tracking/types.js';
|
|
6
|
+
|
|
4
7
|
export type RawBalances = ChainMap<bigint>;
|
|
5
8
|
|
|
6
9
|
export interface Route {
|
|
7
10
|
origin: ChainName;
|
|
8
11
|
destination: ChainName;
|
|
9
12
|
amount: bigint;
|
|
13
|
+
/** Execution method used for this rebalance */
|
|
14
|
+
executionMethod?: ExecutionMethod;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface RouteWithContext extends Route {
|
|
18
|
+
/** For inventory intents: sum of complete inventory_deposit actions (message delivered) */
|
|
19
|
+
deliveredAmount?: bigint;
|
|
20
|
+
/** For inventory intents: sum of in_progress inventory_deposit actions (tx confirmed, message pending) */
|
|
21
|
+
awaitingDeliveryAmount?: bigint;
|
|
10
22
|
}
|
|
11
23
|
|
|
12
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Route using movable collateral execution (on-chain rebalance via bridge)
|
|
26
|
+
*/
|
|
27
|
+
export interface MovableCollateralRoute extends Route {
|
|
28
|
+
executionType: 'movableCollateral';
|
|
13
29
|
bridge: Address;
|
|
14
30
|
}
|
|
15
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Route using inventory execution (external bridge + transferRemote)
|
|
34
|
+
*/
|
|
35
|
+
export interface InventoryRoute extends Route {
|
|
36
|
+
executionType: 'inventory';
|
|
37
|
+
externalBridge: ExternalBridgeType;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Discriminated union of route types by executionType
|
|
42
|
+
*/
|
|
43
|
+
export type StrategyRoute = MovableCollateralRoute | InventoryRoute;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Type guard to check if a route is a MovableCollateralRoute
|
|
47
|
+
*/
|
|
48
|
+
export function isMovableCollateralRoute(
|
|
49
|
+
route: StrategyRoute,
|
|
50
|
+
): route is MovableCollateralRoute {
|
|
51
|
+
return route.executionType === 'movableCollateral';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Type guard to check if a route is an InventoryRoute
|
|
56
|
+
*/
|
|
57
|
+
export function isInventoryRoute(
|
|
58
|
+
route: StrategyRoute,
|
|
59
|
+
): route is InventoryRoute {
|
|
60
|
+
return route.executionType === 'inventory';
|
|
61
|
+
}
|
|
62
|
+
|
|
16
63
|
export type InflightContext = {
|
|
17
64
|
/**
|
|
18
65
|
* In-flight rebalances from ActionTracker.
|
|
19
66
|
* Uses Route[] because recovered intents (from Explorer startup recovery)
|
|
20
67
|
* don't have bridge information. Some routes may have bridge at runtime.
|
|
21
68
|
*/
|
|
22
|
-
pendingRebalances:
|
|
23
|
-
pendingTransfers:
|
|
69
|
+
pendingRebalances: RouteWithContext[];
|
|
70
|
+
pendingTransfers: RouteWithContext[];
|
|
24
71
|
/** Routes from earlier strategies - always have bridge */
|
|
25
72
|
proposedRebalances?: StrategyRoute[];
|
|
26
73
|
};
|
package/src/monitor/Monitor.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { type Logger } from 'pino';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import {
|
|
4
|
+
type ChainMap,
|
|
5
|
+
type ChainName,
|
|
6
|
+
type Token,
|
|
7
|
+
type WarpCore,
|
|
8
|
+
} from '@hyperlane-xyz/sdk';
|
|
9
|
+
import { Address, sleep } from '@hyperlane-xyz/utils';
|
|
5
10
|
|
|
6
11
|
import {
|
|
7
12
|
type ConfirmedBlockTag,
|
|
@@ -14,6 +19,14 @@ import {
|
|
|
14
19
|
} from '../interfaces/IMonitor.js';
|
|
15
20
|
import { getConfirmedBlockTag } from '../utils/blockTag.js';
|
|
16
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Configuration for the Monitor's inventory tracking.
|
|
24
|
+
*/
|
|
25
|
+
export interface InventoryMonitorConfig {
|
|
26
|
+
inventoryAddress: Address;
|
|
27
|
+
chains: ChainName[];
|
|
28
|
+
}
|
|
29
|
+
|
|
17
30
|
/**
|
|
18
31
|
* Simple monitor implementation that polls warp route collateral balances and emits them as MonitorEvent.
|
|
19
32
|
* Awaits the TokenInfo handler before starting the next cycle to prevent race conditions.
|
|
@@ -33,6 +46,7 @@ export class Monitor implements IMonitor {
|
|
|
33
46
|
private readonly checkFrequency: number,
|
|
34
47
|
private readonly warpCore: WarpCore,
|
|
35
48
|
private readonly logger: Logger,
|
|
49
|
+
private readonly inventoryConfig?: InventoryMonitorConfig,
|
|
36
50
|
) {}
|
|
37
51
|
|
|
38
52
|
private async computeConfirmedBlockTags(): Promise<ConfirmedBlockTags> {
|
|
@@ -123,6 +137,24 @@ export class Monitor implements IMonitor {
|
|
|
123
137
|
});
|
|
124
138
|
}
|
|
125
139
|
|
|
140
|
+
const inventoryBalances = await this.fetchInventoryBalances();
|
|
141
|
+
if (Object.keys(inventoryBalances).length > 0) {
|
|
142
|
+
event.inventoryBalances = inventoryBalances;
|
|
143
|
+
this.logger.info(
|
|
144
|
+
{
|
|
145
|
+
chainsMonitored: Object.keys(inventoryBalances).length,
|
|
146
|
+
balances: Object.entries(inventoryBalances).map(
|
|
147
|
+
([chain, balance]) => ({
|
|
148
|
+
chain,
|
|
149
|
+
balance: balance.toString(),
|
|
150
|
+
balanceEth: (Number(balance) / 1e18).toFixed(6),
|
|
151
|
+
}),
|
|
152
|
+
),
|
|
153
|
+
},
|
|
154
|
+
'Inventory balances fetched',
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
126
158
|
if (this.tokenInfoHandler) {
|
|
127
159
|
await this.tokenInfoHandler(event);
|
|
128
160
|
}
|
|
@@ -218,6 +250,53 @@ export class Monitor implements IMonitor {
|
|
|
218
250
|
return bridgedSupply;
|
|
219
251
|
}
|
|
220
252
|
|
|
253
|
+
private async fetchInventoryBalances(): Promise<ChainMap<bigint>> {
|
|
254
|
+
if (!this.inventoryConfig) return {};
|
|
255
|
+
|
|
256
|
+
const balances: ChainMap<bigint> = {};
|
|
257
|
+
|
|
258
|
+
const readPromises = this.inventoryConfig.chains.map(async (chainName) => {
|
|
259
|
+
const token = this.warpCore.tokens.find((t) => t.chainName === chainName);
|
|
260
|
+
if (!token) {
|
|
261
|
+
this.logger.warn(
|
|
262
|
+
{ chain: chainName },
|
|
263
|
+
'No token found for inventory chain',
|
|
264
|
+
);
|
|
265
|
+
return { chainName, balance: 0n };
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
try {
|
|
269
|
+
const adapter = token.getAdapter(this.warpCore.multiProvider);
|
|
270
|
+
const balance = await adapter.getBalance(
|
|
271
|
+
this.inventoryConfig!.inventoryAddress,
|
|
272
|
+
);
|
|
273
|
+
this.logger.debug(
|
|
274
|
+
{
|
|
275
|
+
chain: chainName,
|
|
276
|
+
token: token.addressOrDenom,
|
|
277
|
+
balance: balance.toString(),
|
|
278
|
+
},
|
|
279
|
+
'Read inventory balance',
|
|
280
|
+
);
|
|
281
|
+
return { chainName, balance };
|
|
282
|
+
} catch (error) {
|
|
283
|
+
this.logger.error(
|
|
284
|
+
{ chain: chainName, error: (error as Error).message },
|
|
285
|
+
'Failed to read inventory balance',
|
|
286
|
+
);
|
|
287
|
+
return { chainName, balance: 0n };
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
const results = await Promise.all(readPromises);
|
|
292
|
+
|
|
293
|
+
for (const { chainName, balance } of results) {
|
|
294
|
+
balances[chainName] = balance;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return balances;
|
|
298
|
+
}
|
|
299
|
+
|
|
221
300
|
stop(): Promise<void> {
|
|
222
301
|
if (!this.isMonitorRunning) return Promise.resolve();
|
|
223
302
|
|
package/src/service.ts
CHANGED
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Environment Variables:
|
|
10
10
|
* - REBALANCER_CONFIG_FILE: Path to the rebalancer configuration YAML file (required)
|
|
11
|
-
* -
|
|
11
|
+
* - HYP_REBALANCER_KEY: Private key for movable collateral rebalancing operations (preferred)
|
|
12
|
+
* - HYP_KEY: Fallback private key for HYP_REBALANCER_KEY (optional)
|
|
13
|
+
* - HYP_INVENTORY_KEY: Private key for inventory operations - LiFi bridges and transferRemote (optional)
|
|
12
14
|
* - COINGECKO_API_KEY: API key for CoinGecko price fetching (optional, for metrics)
|
|
13
15
|
* - CHECK_FREQUENCY: Balance check frequency in ms (default: 60000)
|
|
14
16
|
* - WITH_METRICS: Enable Prometheus metrics (default: "true")
|
|
@@ -19,13 +21,13 @@
|
|
|
19
21
|
*
|
|
20
22
|
* Usage:
|
|
21
23
|
* node dist/service.js
|
|
22
|
-
* REBALANCER_CONFIG_FILE=/config/rebalancer.yaml
|
|
24
|
+
* REBALANCER_CONFIG_FILE=/config/rebalancer.yaml HYP_REBALANCER_KEY=0x... HYP_INVENTORY_KEY=0x... node dist/service.js
|
|
23
25
|
*/
|
|
24
26
|
import { Wallet } from 'ethers';
|
|
25
27
|
|
|
26
28
|
import { DEFAULT_GITHUB_REGISTRY } from '@hyperlane-xyz/registry';
|
|
27
29
|
import { getRegistry } from '@hyperlane-xyz/registry/fs';
|
|
28
|
-
import {
|
|
30
|
+
import { MultiProvider } from '@hyperlane-xyz/sdk';
|
|
29
31
|
import {
|
|
30
32
|
applyRpcUrlOverridesFromEnv,
|
|
31
33
|
createServiceLogger,
|
|
@@ -44,12 +46,18 @@ async function main(): Promise<void> {
|
|
|
44
46
|
process.exit(1);
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
const rebalancerPrivateKey =
|
|
50
|
+
process.env.HYP_REBALANCER_KEY ?? process.env.HYP_KEY;
|
|
51
|
+
if (!rebalancerPrivateKey) {
|
|
52
|
+
rootLogger.error(
|
|
53
|
+
'HYP_REBALANCER_KEY (or HYP_KEY) environment variable is required',
|
|
54
|
+
);
|
|
50
55
|
process.exit(1);
|
|
51
56
|
}
|
|
52
57
|
|
|
58
|
+
// Optional: inventory key for inventory-based operations (LiFi bridges, transferRemote)
|
|
59
|
+
const inventoryPrivateKey = process.env.HYP_INVENTORY_KEY;
|
|
60
|
+
|
|
53
61
|
// Parse optional environment variables
|
|
54
62
|
let checkFrequency = 60_000;
|
|
55
63
|
if (process.env.CHECK_FREQUENCY) {
|
|
@@ -106,27 +114,60 @@ async function main(): Promise<void> {
|
|
|
106
114
|
);
|
|
107
115
|
|
|
108
116
|
// Apply RPC URL overrides from environment variables
|
|
109
|
-
|
|
110
|
-
|
|
117
|
+
applyRpcUrlOverridesFromEnv(chainMetadata);
|
|
118
|
+
|
|
119
|
+
// Create MultiProvider with signer
|
|
120
|
+
const multiProvider = new MultiProvider(chainMetadata);
|
|
121
|
+
const rebalancerSigner = new Wallet(rebalancerPrivateKey);
|
|
122
|
+
multiProvider.setSharedSigner(rebalancerSigner);
|
|
123
|
+
logger.info(
|
|
124
|
+
{ rebalancerAddress: rebalancerSigner.address },
|
|
125
|
+
'✅ Initialized MultiProvider with rebalancer signer',
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// Create inventory MultiProvider if inventory key is provided
|
|
129
|
+
let inventoryMultiProvider: MultiProvider | undefined;
|
|
130
|
+
if (inventoryPrivateKey) {
|
|
131
|
+
inventoryMultiProvider = new MultiProvider(chainMetadata, {
|
|
132
|
+
providers: multiProvider.providers,
|
|
133
|
+
});
|
|
134
|
+
const inventorySigner = new Wallet(inventoryPrivateKey);
|
|
135
|
+
inventoryMultiProvider.setSharedSigner(inventorySigner);
|
|
136
|
+
|
|
137
|
+
// Validate against config.inventorySigner if present
|
|
138
|
+
const inventoryAddress = inventorySigner.address;
|
|
139
|
+
if (
|
|
140
|
+
rebalancerConfig.inventorySigner &&
|
|
141
|
+
rebalancerConfig.inventorySigner.toLowerCase() !==
|
|
142
|
+
inventoryAddress.toLowerCase()
|
|
143
|
+
) {
|
|
144
|
+
throw new Error(
|
|
145
|
+
`inventorySigner mismatch: config has ${rebalancerConfig.inventorySigner} but HYP_INVENTORY_KEY derives to ${inventoryAddress}`,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
111
148
|
logger.info(
|
|
112
|
-
{
|
|
113
|
-
'
|
|
149
|
+
{ inventoryAddress },
|
|
150
|
+
'✅ Initialized inventory MultiProvider',
|
|
114
151
|
);
|
|
115
152
|
}
|
|
116
153
|
|
|
117
|
-
//
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
154
|
+
// Fail fast if config references inventorySigner but no HYP_INVENTORY_KEY is provided
|
|
155
|
+
// Without the matching key, inventory operations would silently use the wrong signer
|
|
156
|
+
if (rebalancerConfig.inventorySigner && !inventoryPrivateKey) {
|
|
157
|
+
logger.error(
|
|
158
|
+
{ inventorySigner: rebalancerConfig.inventorySigner },
|
|
159
|
+
'Config specifies inventorySigner but HYP_INVENTORY_KEY is not set. Provide the key or remove inventorySigner from config.',
|
|
160
|
+
);
|
|
161
|
+
process.exit(1);
|
|
162
|
+
}
|
|
122
163
|
|
|
123
|
-
//
|
|
124
|
-
const multiProtocolProvider =
|
|
125
|
-
logger.info('✅ Initialized MultiProtocolProvider');
|
|
164
|
+
// MultiProtocolProvider will be derived from multiProvider in factory
|
|
165
|
+
const multiProtocolProvider = undefined;
|
|
126
166
|
|
|
127
167
|
// Create the rebalancer service
|
|
128
168
|
const service = new RebalancerService(
|
|
129
169
|
multiProvider,
|
|
170
|
+
inventoryMultiProvider,
|
|
130
171
|
multiProtocolProvider,
|
|
131
172
|
registry,
|
|
132
173
|
rebalancerConfig,
|