@humanode-hyperlane/rebalancer 27.2.2 → 1029.1.1
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 +3 -3
- package/dist/bridges/LiFiBridge.d.ts.map +1 -1
- package/dist/bridges/LiFiBridge.js +66 -55
- package/dist/bridges/LiFiBridge.js.map +1 -1
- package/dist/bridges/LiFiBridge.test.js +213 -0
- package/dist/bridges/LiFiBridge.test.js.map +1 -1
- package/dist/config/RebalancerConfig.d.ts +4 -3
- package/dist/config/RebalancerConfig.d.ts.map +1 -1
- package/dist/config/RebalancerConfig.js +7 -5
- package/dist/config/RebalancerConfig.js.map +1 -1
- package/dist/config/RebalancerConfig.test.js +278 -2
- package/dist/config/RebalancerConfig.test.js.map +1 -1
- package/dist/config/types.d.ts +707 -2664
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +62 -7
- package/dist/config/types.js.map +1 -1
- package/dist/core/InventoryRebalancer.d.ts +13 -19
- package/dist/core/InventoryRebalancer.d.ts.map +1 -1
- package/dist/core/InventoryRebalancer.js +404 -276
- package/dist/core/InventoryRebalancer.js.map +1 -1
- package/dist/core/InventoryRebalancer.test.js +814 -25
- package/dist/core/InventoryRebalancer.test.js.map +1 -1
- package/dist/core/Rebalancer.d.ts.map +1 -1
- package/dist/core/Rebalancer.js +12 -6
- package/dist/core/Rebalancer.js.map +1 -1
- package/dist/core/Rebalancer.test.js +51 -0
- package/dist/core/Rebalancer.test.js.map +1 -1
- package/dist/core/RebalancerOrchestrator.d.ts.map +1 -1
- package/dist/core/RebalancerOrchestrator.test.js +0 -1
- package/dist/core/RebalancerOrchestrator.test.js.map +1 -1
- package/dist/core/RebalancerService.d.ts.map +1 -1
- package/dist/core/RebalancerService.js +3 -2
- package/dist/core/RebalancerService.js.map +1 -1
- package/dist/core/RebalancerService.test.js +24 -0
- package/dist/core/RebalancerService.test.js.map +1 -1
- package/dist/e2e/fixtures/routes.d.ts +3 -3
- package/dist/e2e/fixtures/routes.d.ts.map +1 -1
- package/dist/e2e/harness/BaseLocalDeploymentManager.d.ts.map +1 -1
- package/dist/e2e/harness/BaseLocalDeploymentManager.js +3 -2
- package/dist/e2e/harness/BaseLocalDeploymentManager.js.map +1 -1
- package/dist/e2e/harness/Erc20InventoryLocalDeploymentManager.d.ts.map +1 -1
- package/dist/e2e/harness/Erc20InventoryLocalDeploymentManager.js.map +1 -1
- package/dist/e2e/harness/Erc20LocalDeploymentManager.d.ts.map +1 -1
- package/dist/e2e/harness/ForkIndexer.d.ts.map +1 -1
- package/dist/e2e/harness/ForkIndexer.js.map +1 -1
- package/dist/e2e/harness/LocalProvider.d.ts +3 -0
- package/dist/e2e/harness/LocalProvider.d.ts.map +1 -0
- package/dist/e2e/harness/LocalProvider.js +9 -0
- package/dist/e2e/harness/LocalProvider.js.map +1 -0
- package/dist/e2e/harness/LocalProvider.test.d.ts +2 -0
- package/dist/e2e/harness/LocalProvider.test.d.ts.map +1 -0
- package/dist/e2e/harness/LocalProvider.test.js +14 -0
- package/dist/e2e/harness/LocalProvider.test.js.map +1 -0
- package/dist/e2e/harness/MixedLocalDeploymentManager.d.ts.map +1 -1
- package/dist/e2e/harness/MixedLocalDeploymentManager.js.map +1 -1
- package/dist/e2e/harness/MockExplorerClient.d.ts.map +1 -1
- package/dist/e2e/harness/MockExplorerClient.js.map +1 -1
- package/dist/e2e/harness/MockExternalBridge.d.ts.map +1 -1
- package/dist/e2e/harness/MockExternalBridge.js.map +1 -1
- package/dist/e2e/harness/NativeLocalDeploymentManager.d.ts.map +1 -1
- package/dist/e2e/harness/NativeLocalDeploymentManager.js +2 -1
- package/dist/e2e/harness/NativeLocalDeploymentManager.js.map +1 -1
- package/dist/e2e/harness/SnapshotHelper.d.ts.map +1 -1
- package/dist/e2e/harness/SnapshotHelper.js +3 -5
- package/dist/e2e/harness/SnapshotHelper.js.map +1 -1
- package/dist/e2e/harness/TestHelpers.js +1 -2
- package/dist/e2e/harness/TestHelpers.js.map +1 -1
- package/dist/e2e/harness/TestRebalancer.d.ts +5 -1
- package/dist/e2e/harness/TestRebalancer.d.ts.map +1 -1
- package/dist/e2e/harness/TestRebalancer.js +14 -4
- package/dist/e2e/harness/TestRebalancer.js.map +1 -1
- package/dist/e2e/relayer-denylist.e2e-test.d.ts +2 -0
- package/dist/e2e/relayer-denylist.e2e-test.d.ts.map +1 -0
- package/dist/e2e/relayer-denylist.e2e-test.js +388 -0
- package/dist/e2e/relayer-denylist.e2e-test.js.map +1 -0
- package/dist/factories/RebalancerContextFactory.d.ts.map +1 -1
- package/dist/factories/RebalancerContextFactory.js +61 -31
- package/dist/factories/RebalancerContextFactory.js.map +1 -1
- package/dist/factories/RebalancerContextFactory.test.js +336 -5
- package/dist/factories/RebalancerContextFactory.test.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/interfaces/IRebalancer.d.ts +4 -2
- package/dist/interfaces/IRebalancer.d.ts.map +1 -1
- package/dist/metrics/Metrics.d.ts.map +1 -1
- package/dist/metrics/Metrics.js +1 -1
- package/dist/metrics/Metrics.js.map +1 -1
- package/dist/metrics/PriceGetter.d.ts +1 -0
- package/dist/metrics/PriceGetter.d.ts.map +1 -1
- package/dist/metrics/PriceGetter.js +14 -4
- package/dist/metrics/PriceGetter.js.map +1 -1
- package/dist/metrics/PriceGetter.test.d.ts +2 -0
- package/dist/metrics/PriceGetter.test.d.ts.map +1 -0
- package/dist/metrics/PriceGetter.test.js +84 -0
- package/dist/metrics/PriceGetter.test.js.map +1 -0
- package/dist/metrics/scripts/metrics.d.ts +4 -4
- package/dist/monitor/Monitor.d.ts +1 -0
- package/dist/monitor/Monitor.d.ts.map +1 -1
- package/dist/monitor/Monitor.js +55 -46
- package/dist/monitor/Monitor.js.map +1 -1
- package/dist/monitor/Monitor.test.d.ts +2 -0
- package/dist/monitor/Monitor.test.d.ts.map +1 -0
- package/dist/monitor/Monitor.test.js +346 -0
- package/dist/monitor/Monitor.test.js.map +1 -0
- package/dist/service.d.ts +1 -1
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +9 -5
- package/dist/service.js.map +1 -1
- package/dist/strategy/BaseStrategy.d.ts.map +1 -1
- package/dist/strategy/BaseStrategy.js +13 -11
- package/dist/strategy/BaseStrategy.js.map +1 -1
- package/dist/strategy/CollateralDeficitStrategy.d.ts.map +1 -1
- package/dist/strategy/CollateralDeficitStrategy.js +2 -2
- package/dist/strategy/CollateralDeficitStrategy.js.map +1 -1
- package/dist/strategy/CompositeStrategy.d.ts.map +1 -1
- package/dist/strategy/CompositeStrategy.js.map +1 -1
- package/dist/strategy/CompositeStrategy.test.js.map +1 -1
- package/dist/strategy/MinAmountStrategy.d.ts +1 -0
- package/dist/strategy/MinAmountStrategy.d.ts.map +1 -1
- package/dist/strategy/MinAmountStrategy.js +12 -8
- package/dist/strategy/MinAmountStrategy.js.map +1 -1
- package/dist/strategy/MinAmountStrategy.test.js +189 -2
- package/dist/strategy/MinAmountStrategy.test.js.map +1 -1
- package/dist/strategy/StrategyFactory.d.ts.map +1 -1
- package/dist/strategy/StrategyFactory.js.map +1 -1
- package/dist/strategy/WeightedStrategy.d.ts.map +1 -1
- package/dist/test/helpers.d.ts +11 -3
- package/dist/test/helpers.d.ts.map +1 -1
- package/dist/test/helpers.js +9 -11
- package/dist/test/helpers.js.map +1 -1
- package/dist/test/lifiMocks.d.ts +6 -6
- package/dist/test/lifiMocks.d.ts.map +1 -1
- package/dist/test/lifiMocks.js +5 -2
- package/dist/test/lifiMocks.js.map +1 -1
- package/dist/test/pendingTransferMiddlewareMocks.d.ts +17 -0
- package/dist/test/pendingTransferMiddlewareMocks.d.ts.map +1 -0
- package/dist/test/pendingTransferMiddlewareMocks.js +43 -0
- package/dist/test/pendingTransferMiddlewareMocks.js.map +1 -0
- package/dist/tracking/ActionTracker.d.ts +4 -0
- package/dist/tracking/ActionTracker.d.ts.map +1 -1
- package/dist/tracking/ActionTracker.js +76 -9
- package/dist/tracking/ActionTracker.js.map +1 -1
- package/dist/tracking/ActionTracker.test.js +418 -1
- package/dist/tracking/ActionTracker.test.js.map +1 -1
- package/dist/tracking/IActionTracker.d.ts +4 -2
- package/dist/tracking/IActionTracker.d.ts.map +1 -1
- package/dist/tracking/InflightContextAdapter.d.ts.map +1 -1
- package/dist/tracking/InflightContextAdapter.js.map +1 -1
- package/dist/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.d.ts +64 -0
- package/dist/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.d.ts.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.js +474 -0
- package/dist/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.js.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.test.d.ts +2 -0
- package/dist/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.test.d.ts.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.test.js +1168 -0
- package/dist/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.test.js.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.d.ts +61 -0
- package/dist/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.d.ts.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.js +160 -0
- package/dist/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.js.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.test.d.ts +2 -0
- package/dist/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.test.d.ts.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.test.js +369 -0
- package/dist/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.test.js.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.d.ts +60 -0
- package/dist/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.d.ts.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.js +288 -0
- package/dist/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.js.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.test.d.ts +2 -0
- package/dist/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.test.d.ts.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.test.js +606 -0
- package/dist/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.test.js.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.d.ts +25 -0
- package/dist/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.d.ts.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.js +41 -0
- package/dist/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.js.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.test.d.ts +2 -0
- package/dist/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.test.d.ts.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.test.js +141 -0
- package/dist/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.test.js.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/matchBody.d.ts +12 -0
- package/dist/tracking/pendingTransferMiddlewares/matchBody.d.ts.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/matchBody.js +29 -0
- package/dist/tracking/pendingTransferMiddlewares/matchBody.js.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/relayerDenylistRules.d.ts +59 -0
- package/dist/tracking/pendingTransferMiddlewares/relayerDenylistRules.d.ts.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/relayerDenylistRules.js +661 -0
- package/dist/tracking/pendingTransferMiddlewares/relayerDenylistRules.js.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/relayerDenylistRules.test.d.ts +2 -0
- package/dist/tracking/pendingTransferMiddlewares/relayerDenylistRules.test.d.ts.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/relayerDenylistRules.test.js +1093 -0
- package/dist/tracking/pendingTransferMiddlewares/relayerDenylistRules.test.js.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/types.d.ts +2 -0
- package/dist/tracking/pendingTransferMiddlewares/types.d.ts.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/types.js +4 -0
- package/dist/tracking/pendingTransferMiddlewares/types.js.map +1 -0
- package/dist/tracking/store/InMemoryStore.d.ts.map +1 -1
- package/dist/utils/ExplorerClient.d.ts +1 -0
- package/dist/utils/ExplorerClient.d.ts.map +1 -1
- package/dist/utils/ExplorerClient.js +8 -3
- package/dist/utils/ExplorerClient.js.map +1 -1
- package/dist/utils/ExplorerClient.test.js +25 -0
- package/dist/utils/ExplorerClient.test.js.map +1 -1
- package/dist/utils/balanceUtils.d.ts +7 -1
- package/dist/utils/balanceUtils.d.ts.map +1 -1
- package/dist/utils/balanceUtils.js +39 -1
- package/dist/utils/balanceUtils.js.map +1 -1
- package/dist/utils/balanceUtils.test.js +55 -1
- package/dist/utils/balanceUtils.test.js.map +1 -1
- package/dist/utils/blockTag.d.ts +5 -4
- package/dist/utils/blockTag.d.ts.map +1 -1
- package/dist/utils/blockTag.js +26 -10
- package/dist/utils/blockTag.js.map +1 -1
- package/dist/utils/blockTag.test.d.ts +2 -0
- package/dist/utils/blockTag.test.d.ts.map +1 -0
- package/dist/utils/blockTag.test.js +132 -0
- package/dist/utils/blockTag.test.js.map +1 -0
- package/dist/utils/bridgeUtils.js.map +1 -1
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/gasEstimation.d.ts +2 -1
- package/dist/utils/gasEstimation.d.ts.map +1 -1
- package/dist/utils/gasEstimation.js +16 -13
- package/dist/utils/gasEstimation.js.map +1 -1
- package/dist/utils/gasEstimation.test.d.ts +2 -0
- package/dist/utils/gasEstimation.test.d.ts.map +1 -0
- package/dist/utils/gasEstimation.test.js +104 -0
- package/dist/utils/gasEstimation.test.js.map +1 -0
- package/dist/utils/tokenUtils.d.ts.map +1 -1
- package/dist/utils/tokenUtils.js +5 -2
- package/dist/utils/tokenUtils.js.map +1 -1
- package/package.json +23 -22
- package/src/bridges/LiFiBridge.test.ts +270 -0
- package/src/bridges/LiFiBridge.ts +90 -71
- package/src/config/RebalancerConfig.test.ts +315 -1
- package/src/config/RebalancerConfig.ts +6 -3
- package/src/config/types.ts +98 -8
- package/src/core/InventoryRebalancer.test.ts +1092 -38
- package/src/core/InventoryRebalancer.ts +584 -351
- package/src/core/Rebalancer.test.ts +84 -0
- package/src/core/Rebalancer.ts +22 -6
- package/src/core/RebalancerOrchestrator.test.ts +0 -1
- package/src/core/RebalancerService.test.ts +35 -0
- package/src/core/RebalancerService.ts +3 -2
- package/src/e2e/harness/BaseLocalDeploymentManager.ts +3 -1
- package/src/e2e/harness/LocalProvider.test.ts +16 -0
- package/src/e2e/harness/LocalProvider.ts +10 -0
- package/src/e2e/harness/NativeLocalDeploymentManager.ts +2 -1
- package/src/e2e/harness/SnapshotHelper.ts +2 -5
- package/src/e2e/harness/TestHelpers.ts +3 -3
- package/src/e2e/harness/TestRebalancer.ts +21 -1
- package/src/e2e/relayer-denylist.e2e-test.ts +558 -0
- package/src/factories/RebalancerContextFactory.test.ts +489 -8
- package/src/factories/RebalancerContextFactory.ts +86 -36
- package/src/index.ts +2 -0
- package/src/interfaces/IRebalancer.ts +4 -1
- package/src/metrics/Metrics.ts +1 -0
- package/src/metrics/PriceGetter.test.ts +113 -0
- package/src/metrics/PriceGetter.ts +18 -3
- package/src/monitor/Monitor.test.ts +408 -0
- package/src/monitor/Monitor.ts +123 -97
- package/src/service.ts +14 -8
- package/src/strategy/BaseStrategy.ts +18 -15
- package/src/strategy/CollateralDeficitStrategy.ts +4 -3
- package/src/strategy/MinAmountStrategy.test.ts +238 -2
- package/src/strategy/MinAmountStrategy.ts +29 -17
- package/src/test/helpers.ts +13 -12
- package/src/test/lifiMocks.ts +5 -2
- package/src/test/pendingTransferMiddlewareMocks.ts +65 -0
- package/src/tracking/ActionTracker.test.ts +635 -3
- package/src/tracking/ActionTracker.ts +92 -12
- package/src/tracking/IActionTracker.ts +4 -2
- package/src/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.test.ts +1693 -0
- package/src/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.ts +784 -0
- package/src/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.test.ts +554 -0
- package/src/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.ts +233 -0
- package/src/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.test.ts +894 -0
- package/src/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.ts +404 -0
- package/src/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.test.ts +205 -0
- package/src/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.ts +89 -0
- package/src/tracking/pendingTransferMiddlewares/fixtures/mainnet-denylist.json +3556 -0
- package/src/tracking/pendingTransferMiddlewares/matchBody.ts +43 -0
- package/src/tracking/pendingTransferMiddlewares/relayerDenylistRules.test.ts +1220 -0
- package/src/tracking/pendingTransferMiddlewares/relayerDenylistRules.ts +934 -0
- package/src/tracking/pendingTransferMiddlewares/types.ts +3 -0
- package/src/utils/ExplorerClient.test.ts +42 -0
- package/src/utils/ExplorerClient.ts +14 -3
- package/src/utils/balanceUtils.test.ts +87 -1
- package/src/utils/balanceUtils.ts +73 -2
- package/src/utils/blockTag.test.ts +173 -0
- package/src/utils/blockTag.ts +35 -15
- package/src/utils/gasEstimation.test.ts +192 -0
- package/src/utils/gasEstimation.ts +17 -11
- package/src/utils/tokenUtils.ts +5 -2
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { HyperlaneCore,
|
|
2
|
-
import { ProtocolType, assert, ensure0x,
|
|
1
|
+
import { HyperlaneCore, ProviderType, SealevelCoreAdapter, TOKEN_COLLATERALIZED_STANDARDS, WarpTxCategory, getSignerForChain, } from '@humanode-hyperlane/sdk';
|
|
2
|
+
import { ProtocolType, assert, ensure0x, fromWei, isEVMLike, } from '@humanode-hyperlane/utils';
|
|
3
3
|
import { MIN_VIABLE_COST_MULTIPLIER, calculateTransferCosts, } from '../utils/gasEstimation.js';
|
|
4
4
|
import { getExternalBridgeTokenAddress, isNativeTokenStandard, } from '../utils/tokenUtils.js';
|
|
5
5
|
import { parseSolanaPrivateKey } from '../utils/solanaKeyParser.js';
|
|
6
6
|
import { toProtocolTransaction } from '../utils/transactionUtils.js';
|
|
7
|
+
import { alignLocalToCanonical, denormalizeToLocal, normalizeToCanonical, } from '../utils/balanceUtils.js';
|
|
7
8
|
/**
|
|
8
9
|
* Buffer percentage to add when bridging inventory.
|
|
9
10
|
* Bridges (amount * (100 + BRIDGE_BUFFER_PERCENT)) / 100 to account for slippage.
|
|
@@ -21,6 +22,46 @@ const GAS_COST_MULTIPLIER = 20n;
|
|
|
21
22
|
* If gas exceeds this threshold, the bridge is not economically worthwhile.
|
|
22
23
|
*/
|
|
23
24
|
const MAX_GAS_PERCENT_THRESHOLD = 10n;
|
|
25
|
+
const RECOVERABLE_MAX_TRANSFER_ERROR_MESSAGES = [
|
|
26
|
+
'balance may be insufficient',
|
|
27
|
+
'transfer amount exceeds balance',
|
|
28
|
+
'insufficient balance',
|
|
29
|
+
];
|
|
30
|
+
function hasRecoverableMaxTransferErrorMessage(message) {
|
|
31
|
+
const normalized = message.toLowerCase();
|
|
32
|
+
return (normalized.includes('unpredictable_gas_limit') ||
|
|
33
|
+
RECOVERABLE_MAX_TRANSFER_ERROR_MESSAGES.some((pattern) => normalized.includes(pattern)));
|
|
34
|
+
}
|
|
35
|
+
function isRecoverableMaxTransferProbeError(error) {
|
|
36
|
+
const seen = new Set();
|
|
37
|
+
const stack = [error];
|
|
38
|
+
while (stack.length > 0) {
|
|
39
|
+
const current = stack.pop();
|
|
40
|
+
if (current == null)
|
|
41
|
+
continue;
|
|
42
|
+
if (typeof current === 'string') {
|
|
43
|
+
if (hasRecoverableMaxTransferErrorMessage(current))
|
|
44
|
+
return true;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (typeof current !== 'object')
|
|
48
|
+
continue;
|
|
49
|
+
if (seen.has(current))
|
|
50
|
+
continue;
|
|
51
|
+
seen.add(current);
|
|
52
|
+
const candidate = current;
|
|
53
|
+
if (typeof candidate.code === 'string' &&
|
|
54
|
+
candidate.code.toUpperCase() === 'UNPREDICTABLE_GAS_LIMIT') {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
if (typeof candidate.message === 'string' &&
|
|
58
|
+
hasRecoverableMaxTransferErrorMessage(candidate.message)) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
stack.push(candidate.cause, candidate.error);
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
24
65
|
/**
|
|
25
66
|
* Executes inventory-based rebalances for chains that don't support MovableCollateralRouter.
|
|
26
67
|
*
|
|
@@ -169,6 +210,9 @@ export class InventoryRebalancer {
|
|
|
169
210
|
}
|
|
170
211
|
return total;
|
|
171
212
|
}
|
|
213
|
+
formatLocalAmount(amount, token) {
|
|
214
|
+
return fromWei(amount.toString(), token.decimals);
|
|
215
|
+
}
|
|
172
216
|
/**
|
|
173
217
|
* Get the effective available inventory for a chain, accounting for
|
|
174
218
|
* inventory already consumed during this execution cycle.
|
|
@@ -351,45 +395,96 @@ export class InventoryRebalancer {
|
|
|
351
395
|
this.logger.info({
|
|
352
396
|
strategyRoute: `${origin} (surplus) → ${destination} (deficit)`,
|
|
353
397
|
executionRoute: `transferRemote FROM ${destination} TO ${origin}`,
|
|
354
|
-
|
|
398
|
+
canonicalAmount: amount.toString(),
|
|
355
399
|
intentId: intent.id,
|
|
356
400
|
}, 'Executing inventory route');
|
|
401
|
+
const sourceToken = this.getTokenForChain(destination);
|
|
402
|
+
assert(sourceToken, `No token found for source chain: ${destination}`);
|
|
403
|
+
const requestedLocalAmount = denormalizeToLocal(amount, sourceToken);
|
|
404
|
+
const executionSender = this.getInventorySignerAddress(destination);
|
|
405
|
+
const executionRecipient = this.getInventorySignerAddress(origin);
|
|
357
406
|
// Check available inventory on the DESTINATION (deficit) chain
|
|
358
407
|
// We need inventory here because transferRemote is called FROM this chain
|
|
359
408
|
const availableInventory = this.getEffectiveAvailableInventory(destination);
|
|
360
409
|
this.logger.info({
|
|
361
410
|
checkingChain: destination,
|
|
362
411
|
availableInventory: availableInventory.toString(),
|
|
363
|
-
|
|
364
|
-
requiredAmount:
|
|
365
|
-
|
|
412
|
+
availableInventoryFormatted: this.formatLocalAmount(availableInventory, sourceToken),
|
|
413
|
+
requiredAmount: requestedLocalAmount.toString(),
|
|
414
|
+
requiredAmountFormatted: this.formatLocalAmount(requestedLocalAmount, sourceToken),
|
|
366
415
|
}, 'Checking effective inventory on destination (deficit) chain');
|
|
367
416
|
// Calculate transfer costs including max transferable and min viable amounts
|
|
368
417
|
// transferRemote is called FROM destination TO origin (swapped direction)
|
|
369
418
|
const costs = await calculateTransferCosts(destination, // FROM chain (where transferRemote is called)
|
|
370
419
|
origin, // TO chain (where Hyperlane message goes)
|
|
371
|
-
availableInventory,
|
|
372
|
-
const {
|
|
420
|
+
availableInventory, requestedLocalAmount, this.multiProvider, this.warpCore.multiProvider, this.getTokenForChain.bind(this), executionSender, isNativeTokenStandard, this.logger);
|
|
421
|
+
const { minViableTransfer } = costs;
|
|
422
|
+
let maxTransferable = costs.maxTransferable;
|
|
423
|
+
if (!isNativeTokenStandard(sourceToken.standard)) {
|
|
424
|
+
if (availableInventory === 0n) {
|
|
425
|
+
maxTransferable = 0n;
|
|
426
|
+
this.logger.debug({
|
|
427
|
+
fromChain: destination,
|
|
428
|
+
toChain: origin,
|
|
429
|
+
requestedAmount: requestedLocalAmount.toString(),
|
|
430
|
+
}, 'Skipping fee-aware max transferable probe because destination inventory is zero');
|
|
431
|
+
}
|
|
432
|
+
else {
|
|
433
|
+
try {
|
|
434
|
+
const feeAwareMaxTransfer = await this.warpCore.getMaxTransferAmount({
|
|
435
|
+
balance: sourceToken.amount(availableInventory),
|
|
436
|
+
destination: origin,
|
|
437
|
+
sender: executionSender,
|
|
438
|
+
recipient: executionRecipient,
|
|
439
|
+
});
|
|
440
|
+
maxTransferable =
|
|
441
|
+
feeAwareMaxTransfer.amount < requestedLocalAmount
|
|
442
|
+
? feeAwareMaxTransfer.amount
|
|
443
|
+
: requestedLocalAmount;
|
|
444
|
+
this.logger.debug({
|
|
445
|
+
fromChain: destination,
|
|
446
|
+
toChain: origin,
|
|
447
|
+
availableInventory: availableInventory.toString(),
|
|
448
|
+
requestedAmount: requestedLocalAmount.toString(),
|
|
449
|
+
feeAwareMaxTransferable: maxTransferable.toString(),
|
|
450
|
+
}, 'Calculated fee-aware max transferable amount for non-native route');
|
|
451
|
+
}
|
|
452
|
+
catch (error) {
|
|
453
|
+
if (!isRecoverableMaxTransferProbeError(error)) {
|
|
454
|
+
throw error;
|
|
455
|
+
}
|
|
456
|
+
maxTransferable = 0n;
|
|
457
|
+
this.logger.warn({
|
|
458
|
+
fromChain: destination,
|
|
459
|
+
toChain: origin,
|
|
460
|
+
availableInventory: availableInventory.toString(),
|
|
461
|
+
requestedAmount: requestedLocalAmount.toString(),
|
|
462
|
+
error: error instanceof Error ? error.message : String(error),
|
|
463
|
+
intentId: intent.id,
|
|
464
|
+
}, 'Fee-aware max transferable probe failed due to insufficient balance, falling back to external bridge');
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
373
468
|
// Calculate total inventory across all chains
|
|
374
469
|
// Note: consumedInventory tracking is handled separately within this cycle
|
|
375
470
|
const totalInventory = this.getTotalInventory([]);
|
|
376
471
|
this.logger.info({
|
|
377
472
|
fromChain: destination,
|
|
378
473
|
toChain: origin,
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
canFullyFulfill: maxTransferable >= amount,
|
|
474
|
+
availableInventoryFormatted: this.formatLocalAmount(availableInventory, sourceToken),
|
|
475
|
+
requestedAmountFormatted: this.formatLocalAmount(requestedLocalAmount, sourceToken),
|
|
476
|
+
maxTransferableFormatted: this.formatLocalAmount(maxTransferable, sourceToken),
|
|
477
|
+
minViableTransferFormatted: this.formatLocalAmount(minViableTransfer, sourceToken),
|
|
478
|
+
canFullyFulfill: maxTransferable >= requestedLocalAmount,
|
|
385
479
|
canPartialFulfill: maxTransferable >= minViableTransfer,
|
|
480
|
+
totalInventory: totalInventory.toString(),
|
|
386
481
|
}, 'Calculated max transferable amount with cost-based threshold');
|
|
387
482
|
// Early exit: If remaining amount is below minViableTransfer, complete the intent
|
|
388
483
|
// This prevents infinite loops when the remaining amount is too small to economically bridge
|
|
389
|
-
if (
|
|
484
|
+
if (requestedLocalAmount < minViableTransfer) {
|
|
390
485
|
this.logger.info({
|
|
391
486
|
intentId: intent.id,
|
|
392
|
-
amount:
|
|
487
|
+
amount: requestedLocalAmount.toString(),
|
|
393
488
|
minViableTransfer: minViableTransfer.toString(),
|
|
394
489
|
}, 'Remaining amount below minViableTransfer, completing intent with acceptable loss');
|
|
395
490
|
await this.actionTracker.completeRebalanceIntent(intent.id);
|
|
@@ -405,156 +500,199 @@ export class InventoryRebalancer {
|
|
|
405
500
|
...route,
|
|
406
501
|
origin: destination, // transferRemote called FROM here
|
|
407
502
|
destination: origin, // Hyperlane message goes TO here
|
|
503
|
+
amount: requestedLocalAmount,
|
|
408
504
|
};
|
|
409
|
-
if (maxTransferable >=
|
|
505
|
+
if (maxTransferable >= requestedLocalAmount) {
|
|
410
506
|
// Sufficient inventory on destination - execute transferRemote directly
|
|
411
|
-
const
|
|
507
|
+
const fulfilledCanonicalAmount = normalizeToCanonical(requestedLocalAmount, sourceToken);
|
|
508
|
+
const result = await this.executeTransferRemote(swappedRoute, intent, fulfilledCanonicalAmount);
|
|
412
509
|
// Return original strategy route in result (not the swapped execution route)
|
|
413
510
|
return { ...result, route };
|
|
414
511
|
}
|
|
415
512
|
else if (maxTransferable > 0n && maxTransferable >= minViableTransfer) {
|
|
416
513
|
// Partial transfer: Transfer available inventory when economically viable
|
|
417
|
-
const
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
};
|
|
421
|
-
const result = await this.executeTransferRemote(partialSwappedRoute, intent, costs.gasQuote);
|
|
422
|
-
this.logger.info({
|
|
423
|
-
intentId: intent.id,
|
|
424
|
-
partialAmount: maxTransferable.toString(),
|
|
425
|
-
requestedAmount: amount.toString(),
|
|
426
|
-
remainingAmount: (amount - maxTransferable).toString(),
|
|
427
|
-
}, 'Executed partial inventory deposit, remaining will be handled in future cycles');
|
|
428
|
-
// Return original strategy route in result (not the swapped execution route)
|
|
429
|
-
return { ...result, route };
|
|
430
|
-
}
|
|
431
|
-
else {
|
|
432
|
-
// Inventory below cost-based threshold - trigger ExternalBridge movement TO destination chain
|
|
433
|
-
this.logger.info({
|
|
434
|
-
targetChain: destination,
|
|
435
|
-
maxTransferable: maxTransferable.toString(),
|
|
436
|
-
minViableTransfer: minViableTransfer.toString(),
|
|
437
|
-
costMultiplier: MIN_VIABLE_COST_MULTIPLIER.toString(),
|
|
438
|
-
intentId: intent.id,
|
|
439
|
-
}, 'Inventory below cost-based threshold on destination, triggering LiFi movement');
|
|
440
|
-
// Get all available source chains with raw inventory
|
|
441
|
-
const allSources = this.selectAllSourceChains(destination);
|
|
442
|
-
if (allSources.length === 0) {
|
|
443
|
-
this.logger.warn({
|
|
444
|
-
origin,
|
|
445
|
-
destination,
|
|
446
|
-
amount: amount.toString(),
|
|
514
|
+
const alignedExecution = alignLocalToCanonical(maxTransferable, sourceToken);
|
|
515
|
+
if (alignedExecution.messageAmount === 0n) {
|
|
516
|
+
this.logger.info({
|
|
447
517
|
intentId: intent.id,
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
route,
|
|
451
|
-
success: false,
|
|
452
|
-
error: 'No inventory available on any monitored chain',
|
|
453
|
-
};
|
|
518
|
+
maxTransferable: maxTransferable.toString(),
|
|
519
|
+
}, 'Skipping partial transferRemote because available local amount cannot produce canonical progress');
|
|
454
520
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
const maxViable = await this.calculateMaxViableBridgeAmount(source.chain, destination, source.availableAmount, route.externalBridge);
|
|
460
|
-
if (maxViable > 0n) {
|
|
461
|
-
viableSources.push({ chain: source.chain, maxViable });
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
// Sort by max viable descending (bridge from largest sources first)
|
|
465
|
-
viableSources.sort((a, b) => (a.maxViable > b.maxViable ? -1 : 1));
|
|
466
|
-
if (viableSources.length === 0) {
|
|
467
|
-
this.logger.warn({
|
|
468
|
-
targetChain: destination,
|
|
469
|
-
sourcesChecked: allSources.length,
|
|
470
|
-
intentId: intent.id,
|
|
471
|
-
}, 'No viable bridge sources - all chains have insufficient inventory or high gas costs');
|
|
472
|
-
return {
|
|
473
|
-
route,
|
|
474
|
-
success: false,
|
|
475
|
-
error: 'No viable bridge sources available',
|
|
521
|
+
else {
|
|
522
|
+
const partialSwappedRoute = {
|
|
523
|
+
...swappedRoute,
|
|
524
|
+
amount: alignedExecution.localAmount,
|
|
476
525
|
};
|
|
526
|
+
const result = await this.executeTransferRemote(partialSwappedRoute, intent, alignedExecution.messageAmount);
|
|
527
|
+
this.logger.info({
|
|
528
|
+
intentId: intent.id,
|
|
529
|
+
partialAmount: alignedExecution.localAmount.toString(),
|
|
530
|
+
partialAmountCanonical: alignedExecution.messageAmount.toString(),
|
|
531
|
+
requestedAmount: requestedLocalAmount.toString(),
|
|
532
|
+
requestedAmountCanonical: amount.toString(),
|
|
533
|
+
remainingAmountCanonical: (amount > alignedExecution.messageAmount
|
|
534
|
+
? amount - alignedExecution.messageAmount
|
|
535
|
+
: 0n).toString(),
|
|
536
|
+
}, 'Executed partial inventory deposit, remaining will be handled in future cycles');
|
|
537
|
+
// Return original strategy route in result (not the swapped execution route)
|
|
538
|
+
return { ...result, route };
|
|
477
539
|
}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
540
|
+
}
|
|
541
|
+
// Inventory below cost-based threshold - trigger ExternalBridge movement TO destination chain
|
|
542
|
+
this.logger.info({
|
|
543
|
+
targetChain: destination,
|
|
544
|
+
maxTransferable: maxTransferable.toString(),
|
|
545
|
+
minViableTransfer: minViableTransfer.toString(),
|
|
546
|
+
costMultiplier: MIN_VIABLE_COST_MULTIPLIER.toString(),
|
|
547
|
+
intentId: intent.id,
|
|
548
|
+
}, 'Inventory below cost-based threshold on destination, triggering LiFi movement');
|
|
549
|
+
// Get all available source chains with raw inventory
|
|
550
|
+
const allSources = this.selectAllSourceChains(destination);
|
|
551
|
+
if (allSources.length === 0) {
|
|
552
|
+
this.logger.warn({
|
|
553
|
+
origin,
|
|
554
|
+
destination,
|
|
555
|
+
amount: requestedLocalAmount.toString(),
|
|
556
|
+
intentId: intent.id,
|
|
557
|
+
}, 'No inventory available on any monitored chain');
|
|
558
|
+
return {
|
|
559
|
+
route,
|
|
560
|
+
success: false,
|
|
561
|
+
error: 'No inventory available on any monitored chain',
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
// Calculate source capacities in destination-local units.
|
|
565
|
+
const viableSources = [];
|
|
566
|
+
for (const source of allSources) {
|
|
567
|
+
const capacity = await this.calculateBridgeCapacity(source.chain, destination, source.availableAmount, route.externalBridge);
|
|
568
|
+
if (capacity.maxTargetOutput > 0n) {
|
|
569
|
+
viableSources.push({ chain: source.chain, ...capacity });
|
|
492
570
|
}
|
|
493
|
-
|
|
571
|
+
}
|
|
572
|
+
// Sort by destination output descending.
|
|
573
|
+
viableSources.sort((a, b) => a.maxTargetOutput > b.maxTargetOutput ? -1 : 1);
|
|
574
|
+
if (viableSources.length === 0) {
|
|
575
|
+
this.logger.warn({
|
|
494
576
|
targetChain: destination,
|
|
495
|
-
|
|
496
|
-
chain: s.chain,
|
|
497
|
-
maxViable: s.maxViable.toString(),
|
|
498
|
-
maxViableEth: (Number(s.maxViable) / 1e18).toFixed(6),
|
|
499
|
-
})),
|
|
500
|
-
bridgePlans: bridgePlans.map((p) => ({
|
|
501
|
-
chain: p.chain,
|
|
502
|
-
amount: p.amount.toString(),
|
|
503
|
-
amountEth: (Number(p.amount) / 1e18).toFixed(6),
|
|
504
|
-
})),
|
|
505
|
-
totalPlanned: totalPlanned.toString(),
|
|
506
|
-
targetWithBuffer: targetWithBuffer.toString(),
|
|
577
|
+
sourcesChecked: allSources.length,
|
|
507
578
|
intentId: intent.id,
|
|
508
|
-
}, '
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
579
|
+
}, 'No viable bridge sources - all chains have insufficient inventory or high gas costs');
|
|
580
|
+
return {
|
|
581
|
+
route,
|
|
582
|
+
success: false,
|
|
583
|
+
error: 'No viable bridge sources available',
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
// Create bridge plans using destination-local output amounts.
|
|
587
|
+
const shortfall = requestedLocalAmount > availableInventory
|
|
588
|
+
? requestedLocalAmount - availableInventory
|
|
589
|
+
: 0n;
|
|
590
|
+
const targetWithBuffer = ((shortfall + costs.totalCost) * (100n + BRIDGE_BUFFER_PERCENT)) / 100n;
|
|
591
|
+
const bridgePlans = [];
|
|
592
|
+
let totalPlanned = 0n;
|
|
593
|
+
for (const source of viableSources) {
|
|
594
|
+
if (totalPlanned >= targetWithBuffer)
|
|
595
|
+
break;
|
|
596
|
+
const remaining = targetWithBuffer - totalPlanned;
|
|
597
|
+
const targetOutput = source.maxTargetOutput >= remaining
|
|
598
|
+
? remaining
|
|
599
|
+
: source.maxTargetOutput;
|
|
600
|
+
const quoteMode = source.maxTargetOutput > remaining ? 'reverse' : 'forward';
|
|
601
|
+
bridgePlans.push({
|
|
602
|
+
chain: source.chain,
|
|
603
|
+
maxSourceInput: source.maxSourceInput,
|
|
604
|
+
targetOutput,
|
|
605
|
+
quoteMode,
|
|
606
|
+
});
|
|
607
|
+
totalPlanned += targetOutput;
|
|
608
|
+
}
|
|
609
|
+
this.logger.info({
|
|
610
|
+
targetChain: destination,
|
|
611
|
+
viableSources: viableSources.map((s) => ({
|
|
612
|
+
chain: s.chain,
|
|
613
|
+
maxSourceInput: s.maxSourceInput.toString(),
|
|
614
|
+
maxTargetOutput: s.maxTargetOutput.toString(),
|
|
615
|
+
})),
|
|
616
|
+
bridgePlans: bridgePlans.map((p) => ({
|
|
617
|
+
chain: p.chain,
|
|
618
|
+
maxSourceInput: p.maxSourceInput.toString(),
|
|
619
|
+
targetOutput: p.targetOutput.toString(),
|
|
620
|
+
quoteMode: p.quoteMode,
|
|
621
|
+
})),
|
|
622
|
+
totalPlanned: totalPlanned.toString(),
|
|
623
|
+
shortfall: shortfall.toString(),
|
|
624
|
+
targetWithBuffer: targetWithBuffer.toString(),
|
|
625
|
+
intentId: intent.id,
|
|
626
|
+
}, 'Created bridge plans using gas-adjusted viable amounts');
|
|
627
|
+
// Execute all bridges in parallel
|
|
628
|
+
const bridgeResults = await Promise.allSettled(bridgePlans.map((plan) => this.executeInventoryMovement(plan.chain, destination, plan.targetOutput, plan.maxSourceInput, plan.quoteMode, intent, route.externalBridge)));
|
|
629
|
+
// Process results
|
|
630
|
+
let successCount = 0;
|
|
631
|
+
let totalQuotedOutputMin = 0n;
|
|
632
|
+
const failedErrors = [];
|
|
633
|
+
for (let i = 0; i < bridgeResults.length; i++) {
|
|
634
|
+
const result = bridgeResults[i];
|
|
635
|
+
const plan = bridgePlans[i];
|
|
636
|
+
if (result.status === 'fulfilled' && result.value.success) {
|
|
637
|
+
successCount++;
|
|
638
|
+
totalQuotedOutputMin += result.value.quotedOutputMin;
|
|
639
|
+
this.logger.info({
|
|
640
|
+
sourceChain: plan.chain,
|
|
641
|
+
plannedTargetOutput: plan.targetOutput.toString(),
|
|
642
|
+
quotedOutput: result.value.quotedOutput.toString(),
|
|
643
|
+
quotedOutputMin: result.value.quotedOutputMin.toString(),
|
|
644
|
+
quoteModeUsed: result.value.quoteModeUsed,
|
|
645
|
+
txHash: result.value.txHash,
|
|
646
|
+
}, 'Inventory movement succeeded');
|
|
647
|
+
}
|
|
648
|
+
else {
|
|
649
|
+
let error;
|
|
650
|
+
if (result.status === 'rejected') {
|
|
651
|
+
if (result.reason instanceof Error) {
|
|
652
|
+
error = result.reason.message;
|
|
653
|
+
}
|
|
654
|
+
else if (typeof result.reason === 'string') {
|
|
655
|
+
error = result.reason;
|
|
656
|
+
}
|
|
657
|
+
else {
|
|
658
|
+
try {
|
|
659
|
+
error = JSON.stringify(result.reason) ?? String(result.reason);
|
|
660
|
+
}
|
|
661
|
+
catch {
|
|
662
|
+
error = String(result.reason);
|
|
663
|
+
}
|
|
533
664
|
}
|
|
534
|
-
this.logger.warn({
|
|
535
|
-
sourceChain: plan.chain,
|
|
536
|
-
amount: plan.amount.toString(),
|
|
537
|
-
error,
|
|
538
|
-
}, 'Inventory movement failed');
|
|
539
665
|
}
|
|
666
|
+
else if (!result.value.success) {
|
|
667
|
+
error = result.value.error;
|
|
668
|
+
}
|
|
669
|
+
if (error) {
|
|
670
|
+
failedErrors.push(`${plan.chain}: ${error}`);
|
|
671
|
+
}
|
|
672
|
+
this.logger.warn({
|
|
673
|
+
sourceChain: plan.chain,
|
|
674
|
+
plannedTargetOutput: plan.targetOutput.toString(),
|
|
675
|
+
error,
|
|
676
|
+
}, 'Inventory movement failed');
|
|
540
677
|
}
|
|
541
|
-
if (successCount === 0) {
|
|
542
|
-
const errorDetails = failedErrors.length > 0 ? ` (${failedErrors.join('; ')})` : '';
|
|
543
|
-
return {
|
|
544
|
-
route,
|
|
545
|
-
success: false,
|
|
546
|
-
error: `All inventory movements failed${errorDetails}`,
|
|
547
|
-
};
|
|
548
|
-
}
|
|
549
|
-
this.logger.info({
|
|
550
|
-
targetChain: destination,
|
|
551
|
-
successCount,
|
|
552
|
-
totalBridged: totalBridged.toString(),
|
|
553
|
-
targetAmount: amount.toString(),
|
|
554
|
-
intentId: intent.id,
|
|
555
|
-
}, 'Parallel inventory movements completed, transferRemote will execute after bridges complete');
|
|
556
|
-
return { route, success: true };
|
|
557
678
|
}
|
|
679
|
+
if (successCount === 0) {
|
|
680
|
+
const errorDetails = failedErrors.length > 0 ? ` (${failedErrors.join('; ')})` : '';
|
|
681
|
+
return {
|
|
682
|
+
route,
|
|
683
|
+
success: false,
|
|
684
|
+
error: `All inventory movements failed${errorDetails}`,
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
this.logger.info({
|
|
688
|
+
targetChain: destination,
|
|
689
|
+
successCount,
|
|
690
|
+
totalQuotedOutputMin: totalQuotedOutputMin.toString(),
|
|
691
|
+
targetAmount: requestedLocalAmount.toString(),
|
|
692
|
+
targetAmountCanonical: amount.toString(),
|
|
693
|
+
intentId: intent.id,
|
|
694
|
+
}, 'Parallel inventory movements completed, transferRemote will execute after bridges complete');
|
|
695
|
+
return { route, success: true };
|
|
558
696
|
}
|
|
559
697
|
/**
|
|
560
698
|
* Execute a transferRemote to deposit collateral.
|
|
@@ -569,48 +707,21 @@ export class InventoryRebalancer {
|
|
|
569
707
|
*
|
|
570
708
|
* @param route - The transfer route (swapped direction)
|
|
571
709
|
* @param intent - The rebalance intent being executed
|
|
572
|
-
* @param gasQuote - Pre-calculated gas quote from calculateTransferCosts
|
|
573
710
|
*/
|
|
574
|
-
async executeTransferRemote(route, intent,
|
|
711
|
+
async executeTransferRemote(route, intent, fulfilledCanonicalAmount) {
|
|
575
712
|
const { origin, destination, amount } = route;
|
|
576
713
|
const originToken = this.getTokenForChain(origin);
|
|
577
714
|
if (!originToken) {
|
|
578
715
|
throw new Error(`No token found for origin chain: ${origin}`);
|
|
579
716
|
}
|
|
580
717
|
const destinationDomain = this.multiProvider.getDomainId(destination);
|
|
581
|
-
this.logger.debug({
|
|
582
|
-
origin,
|
|
583
|
-
destination,
|
|
584
|
-
amount: amount.toString(),
|
|
585
|
-
gasQuote: {
|
|
586
|
-
igpQuote: gasQuote.igpQuote.amount.toString(),
|
|
587
|
-
tokenFeeQuote: gasQuote.tokenFeeQuote?.amount?.toString() ?? 'none',
|
|
588
|
-
},
|
|
589
|
-
}, 'Using pre-calculated gas quote for transferRemote');
|
|
590
|
-
// Convert pre-calculated gas quote to TokenAmount for WarpCore
|
|
591
|
-
const originChainMetadata = this.multiProvider.getChainMetadata(origin);
|
|
592
|
-
const igpAddressOrDenom = gasQuote.igpQuote.addressOrDenom;
|
|
593
|
-
const igpToken = !igpAddressOrDenom || isZeroishAddress(igpAddressOrDenom)
|
|
594
|
-
? Token.FromChainMetadataNativeToken(originChainMetadata)
|
|
595
|
-
: this.warpCore.findToken(origin, igpAddressOrDenom);
|
|
596
|
-
assert(igpToken, `IGP fee token ${igpAddressOrDenom} is unknown`);
|
|
597
|
-
const interchainFee = new TokenAmount(gasQuote.igpQuote.amount, igpToken);
|
|
598
|
-
let tokenFeeQuote;
|
|
599
|
-
if (gasQuote.tokenFeeQuote?.amount) {
|
|
600
|
-
const feeAddress = gasQuote.tokenFeeQuote.addressOrDenom;
|
|
601
|
-
const feeToken = !feeAddress || isZeroishAddress(feeAddress)
|
|
602
|
-
? Token.FromChainMetadataNativeToken(originChainMetadata)
|
|
603
|
-
: originToken;
|
|
604
|
-
tokenFeeQuote = new TokenAmount(gasQuote.tokenFeeQuote.amount, feeToken);
|
|
605
|
-
}
|
|
718
|
+
this.logger.debug({ origin, destination, amount: amount.toString() }, 'Building transferRemote transactions for exact execution amount');
|
|
606
719
|
const originTokenAmount = originToken.amount(amount);
|
|
607
720
|
const transferTxs = await this.warpCore.getTransferRemoteTxs({
|
|
608
721
|
originTokenAmount,
|
|
609
722
|
destination,
|
|
610
723
|
sender: this.getInventorySignerAddress(origin),
|
|
611
724
|
recipient: this.getInventorySignerAddress(destination),
|
|
612
|
-
interchainFee,
|
|
613
|
-
tokenFeeQuote,
|
|
614
725
|
});
|
|
615
726
|
assert(transferTxs.length > 0, 'Expected at least one transaction from WarpCore');
|
|
616
727
|
this.logger.info({
|
|
@@ -651,7 +762,7 @@ export class InventoryRebalancer {
|
|
|
651
762
|
intentId: intent.id,
|
|
652
763
|
origin: this.multiProvider.getDomainId(origin),
|
|
653
764
|
destination: destinationDomain,
|
|
654
|
-
amount,
|
|
765
|
+
amount: fulfilledCanonicalAmount,
|
|
655
766
|
type: 'inventory_deposit',
|
|
656
767
|
txHash: transferTxHash,
|
|
657
768
|
messageId,
|
|
@@ -680,6 +791,7 @@ export class InventoryRebalancer {
|
|
|
680
791
|
buildSignerAccountConfig(protocol, key, chain) {
|
|
681
792
|
void chain;
|
|
682
793
|
switch (protocol) {
|
|
794
|
+
case ProtocolType.Tron:
|
|
683
795
|
case ProtocolType.Ethereum:
|
|
684
796
|
return { protocol, privateKey: ensure0x(key) };
|
|
685
797
|
case ProtocolType.Sealevel:
|
|
@@ -707,7 +819,7 @@ export class InventoryRebalancer {
|
|
|
707
819
|
async getTransactionReceipt(origin, txHash) {
|
|
708
820
|
try {
|
|
709
821
|
const protocol = this.getProtocolForChain(origin);
|
|
710
|
-
if (protocol
|
|
822
|
+
if (isEVMLike(protocol)) {
|
|
711
823
|
const provider = this.warpCore.multiProvider.getEthersV5Provider(origin);
|
|
712
824
|
const receipt = await provider.getTransactionReceipt(txHash);
|
|
713
825
|
if (!receipt)
|
|
@@ -718,7 +830,8 @@ export class InventoryRebalancer {
|
|
|
718
830
|
const provider = this.warpCore.multiProvider.getSolanaWeb3Provider(origin);
|
|
719
831
|
const receipt = await provider.getTransaction(txHash, {
|
|
720
832
|
commitment: 'confirmed',
|
|
721
|
-
maxSupportedTransactionVersion:
|
|
833
|
+
maxSupportedTransactionVersion: this.warpCore.multiProvider.getChainMetadata(origin)
|
|
834
|
+
.maxSupportedTransactionVersion ?? 0,
|
|
722
835
|
});
|
|
723
836
|
if (!receipt)
|
|
724
837
|
return undefined;
|
|
@@ -753,39 +866,26 @@ export class InventoryRebalancer {
|
|
|
753
866
|
return sources.sort((a, b) => a.availableAmount > b.availableAmount ? -1 : 1);
|
|
754
867
|
}
|
|
755
868
|
/**
|
|
756
|
-
* Calculate the
|
|
757
|
-
* Uses LiFi
|
|
758
|
-
*
|
|
759
|
-
*
|
|
760
|
-
* This is the key method for the gas-aware planning approach:
|
|
761
|
-
* - Gets a quote for the full raw inventory to determine actual gas costs
|
|
762
|
-
* - Applies conservative 20x buffer (LiFi underestimates by ~14x historically)
|
|
763
|
-
* - Returns 0 if gas > 10% of inventory (not worth bridging)
|
|
764
|
-
* - Returns inventory - estimatedGas if viable
|
|
869
|
+
* Calculate the bridge capacity from a source chain in destination-local units.
|
|
870
|
+
* Uses LiFi quotes to conservatively estimate the destination output available
|
|
871
|
+
* from the source chain's current local inventory.
|
|
765
872
|
*
|
|
766
|
-
*
|
|
767
|
-
*
|
|
768
|
-
* @param rawInventory - Raw available inventory on source chain
|
|
769
|
-
* @param externalBridgeType - External bridge type to use
|
|
770
|
-
* @returns Maximum viable bridge amount (0 if not viable)
|
|
873
|
+
* For native-token sources, gas is reserved from the source inventory and the
|
|
874
|
+
* output capacity is re-quoted from the remaining source input.
|
|
771
875
|
*/
|
|
772
|
-
async
|
|
876
|
+
async calculateBridgeCapacity(sourceChain, targetChain, rawInventory, externalBridgeType) {
|
|
773
877
|
const sourceToken = this.getTokenForChain(sourceChain);
|
|
774
878
|
const targetToken = this.getTokenForChain(targetChain);
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
// Only applies to native tokens (need gas from same balance)
|
|
778
|
-
if (!isNativeTokenStandard(sourceToken.standard)) {
|
|
779
|
-
return rawInventory; // ERC20s don't compete with gas
|
|
780
|
-
}
|
|
879
|
+
assert(sourceToken, `No token found for source chain: ${sourceChain}`);
|
|
880
|
+
assert(targetToken, `No token found for target chain: ${targetChain}`);
|
|
781
881
|
// Convert HypNative token addresses to the external bridge's native token representation
|
|
782
|
-
const fromTokenAddress = this.getNativeTokenAddress(
|
|
882
|
+
const fromTokenAddress = getExternalBridgeTokenAddress(sourceToken, externalBridgeType, this.getNativeTokenAddress.bind(this));
|
|
783
883
|
const toTokenAddress = getExternalBridgeTokenAddress(targetToken, externalBridgeType, this.getNativeTokenAddress.bind(this));
|
|
784
884
|
const sourceChainId = Number(this.multiProvider.getChainId(sourceChain));
|
|
785
885
|
const targetChainId = Number(this.multiProvider.getChainId(targetChain));
|
|
786
886
|
try {
|
|
787
887
|
const externalBridge = this.getExternalBridge(externalBridgeType);
|
|
788
|
-
const
|
|
888
|
+
const initialQuote = await externalBridge.quote({
|
|
789
889
|
fromChain: sourceChainId,
|
|
790
890
|
toChain: targetChainId,
|
|
791
891
|
fromToken: fromTokenAddress,
|
|
@@ -794,62 +894,74 @@ export class InventoryRebalancer {
|
|
|
794
894
|
fromAddress: this.getInventorySignerAddress(sourceChain),
|
|
795
895
|
toAddress: this.getInventorySignerAddress(targetChain),
|
|
796
896
|
});
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
}
|
|
813
|
-
|
|
897
|
+
let maxSourceInput = rawInventory;
|
|
898
|
+
let outputQuote = initialQuote;
|
|
899
|
+
if (isNativeTokenStandard(sourceToken.standard)) {
|
|
900
|
+
const estimatedGas = initialQuote.gasCosts * GAS_COST_MULTIPLIER;
|
|
901
|
+
const maxGasThreshold = rawInventory / MAX_GAS_PERCENT_THRESHOLD;
|
|
902
|
+
if (estimatedGas > maxGasThreshold) {
|
|
903
|
+
this.logger.info({
|
|
904
|
+
sourceChain,
|
|
905
|
+
targetChain,
|
|
906
|
+
rawInventory: rawInventory.toString(),
|
|
907
|
+
quotedGas: initialQuote.gasCosts.toString(),
|
|
908
|
+
estimatedGas: estimatedGas.toString(),
|
|
909
|
+
maxGasThreshold: maxGasThreshold.toString(),
|
|
910
|
+
}, 'Bridge not viable - gas cost exceeds 10% of inventory');
|
|
911
|
+
return { maxSourceInput: 0n, maxTargetOutput: 0n };
|
|
912
|
+
}
|
|
913
|
+
maxSourceInput = rawInventory - estimatedGas;
|
|
914
|
+
if (maxSourceInput <= 0n) {
|
|
915
|
+
return { maxSourceInput: 0n, maxTargetOutput: 0n };
|
|
916
|
+
}
|
|
917
|
+
outputQuote = await externalBridge.quote({
|
|
918
|
+
fromChain: sourceChainId,
|
|
919
|
+
toChain: targetChainId,
|
|
920
|
+
fromToken: fromTokenAddress,
|
|
921
|
+
toToken: toTokenAddress,
|
|
922
|
+
fromAmount: maxSourceInput,
|
|
923
|
+
fromAddress: this.getInventorySignerAddress(sourceChain),
|
|
924
|
+
toAddress: this.getInventorySignerAddress(targetChain),
|
|
925
|
+
});
|
|
814
926
|
}
|
|
815
|
-
// Max viable = inventory minus estimated gas
|
|
816
|
-
const maxViable = rawInventory - estimatedGas;
|
|
817
927
|
this.logger.info({
|
|
818
928
|
sourceChain,
|
|
819
929
|
targetChain,
|
|
820
930
|
rawInventory: rawInventory.toString(),
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
}
|
|
828
|
-
return maxViable;
|
|
931
|
+
maxSourceInput: maxSourceInput.toString(),
|
|
932
|
+
maxTargetOutput: outputQuote.toAmountMin.toString(),
|
|
933
|
+
}, 'Calculated bridge capacity');
|
|
934
|
+
return {
|
|
935
|
+
maxSourceInput,
|
|
936
|
+
maxTargetOutput: outputQuote.toAmountMin,
|
|
937
|
+
};
|
|
829
938
|
}
|
|
830
939
|
catch (error) {
|
|
831
940
|
this.logger.warn({
|
|
832
941
|
sourceChain,
|
|
833
942
|
targetChain,
|
|
834
943
|
error: error.message,
|
|
835
|
-
}, 'Failed to calculate
|
|
836
|
-
return 0n;
|
|
944
|
+
}, 'Failed to calculate bridge capacity, skipping chain');
|
|
945
|
+
return { maxSourceInput: 0n, maxTargetOutput: 0n };
|
|
837
946
|
}
|
|
838
947
|
}
|
|
839
948
|
/**
|
|
840
949
|
* Execute inventory movement from source chain to target chain via LiFi bridge.
|
|
841
950
|
*
|
|
842
|
-
*
|
|
843
|
-
*
|
|
951
|
+
* Quote mode is chosen during planning:
|
|
952
|
+
* - `reverse`: request an exact target-chain output when the source has headroom
|
|
953
|
+
* - `forward`: spend the source cap directly when source inventory is the limiter
|
|
844
954
|
*
|
|
845
955
|
* @param sourceChain - Chain to move inventory from
|
|
846
956
|
* @param targetChain - Chain to move inventory to (origin chain for rebalancing)
|
|
847
|
-
* @param
|
|
957
|
+
* @param targetOutputAmount - Destination-local amount to receive
|
|
958
|
+
* @param maxSourceInput - Maximum source-local amount available for this plan
|
|
959
|
+
* @param quoteMode - Whether to execute this bridge plan as exact-input or exact-output
|
|
848
960
|
* @param intent - Rebalance intent for tracking
|
|
849
961
|
* @param externalBridgeType - External bridge type to use
|
|
850
962
|
* @returns Result with success status and optional txHash/error
|
|
851
963
|
*/
|
|
852
|
-
async executeInventoryMovement(sourceChain, targetChain,
|
|
964
|
+
async executeInventoryMovement(sourceChain, targetChain, targetOutputAmount, maxSourceInput, quoteMode, intent, externalBridgeType) {
|
|
853
965
|
const sourceToken = this.getTokenForChain(sourceChain);
|
|
854
966
|
if (!sourceToken) {
|
|
855
967
|
return {
|
|
@@ -878,59 +990,65 @@ export class InventoryRebalancer {
|
|
|
878
990
|
fromTokenAddress,
|
|
879
991
|
toTokenAddress,
|
|
880
992
|
}, 'Resolved token addresses for LiFi bridge');
|
|
881
|
-
// Calculate minViableTransfer for the target chain
|
|
882
|
-
// If bridging less than this, the received amount won't be enough to execute transferRemote
|
|
883
|
-
// So we over-bridge to ensure we can complete the intent in the next cycle
|
|
884
|
-
const costs = await calculateTransferCosts(targetChain, // FROM chain for transferRemote (the target of this bridge)
|
|
885
|
-
sourceChain, // TO chain for transferRemote (Hyperlane message destination)
|
|
886
|
-
amount, // availableInventory (not used for minViableTransfer calculation)
|
|
887
|
-
amount, // requestedAmount
|
|
888
|
-
this.multiProvider, this.warpCore.multiProvider, this.getTokenForChain.bind(this), this.getInventorySignerAddress(targetChain), isNativeTokenStandard, this.logger);
|
|
889
|
-
const { minViableTransfer } = costs;
|
|
890
|
-
// If the requested amount is below minViableTransfer, adjust it up
|
|
891
|
-
// This ensures we bridge enough to actually complete the final transferRemote
|
|
892
|
-
const effectiveAmount = amount < minViableTransfer ? minViableTransfer : amount;
|
|
893
|
-
if (effectiveAmount !== amount) {
|
|
894
|
-
this.logger.info({
|
|
895
|
-
originalAmount: amount.toString(),
|
|
896
|
-
effectiveAmount: effectiveAmount.toString(),
|
|
897
|
-
minViableTransfer: minViableTransfer.toString(),
|
|
898
|
-
originalAmountEth: (Number(amount) / 1e18).toFixed(6),
|
|
899
|
-
effectiveAmountEth: (Number(effectiveAmount) / 1e18).toFixed(6),
|
|
900
|
-
minViableTransferEth: (Number(minViableTransfer) / 1e18).toFixed(6),
|
|
901
|
-
adjustedUp: true,
|
|
902
|
-
intentId: intent.id,
|
|
903
|
-
}, 'Over-bridging to minViableTransfer to ensure final transferRemote can complete');
|
|
904
|
-
}
|
|
905
993
|
try {
|
|
906
994
|
const externalBridge = this.getExternalBridge(externalBridgeType);
|
|
907
|
-
const
|
|
995
|
+
const fromAddress = this.getInventorySignerAddress(sourceChain);
|
|
996
|
+
const toAddress = this.getInventorySignerAddress(targetChain);
|
|
997
|
+
const quoteWithMode = async (mode) => externalBridge.quote({
|
|
908
998
|
fromChain: sourceChainId,
|
|
909
999
|
toChain: targetChainId,
|
|
910
1000
|
fromToken: fromTokenAddress,
|
|
911
1001
|
toToken: toTokenAddress,
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
1002
|
+
...(mode === 'forward'
|
|
1003
|
+
? { fromAmount: maxSourceInput }
|
|
1004
|
+
: { toAmount: targetOutputAmount }),
|
|
1005
|
+
fromAddress,
|
|
1006
|
+
toAddress,
|
|
915
1007
|
});
|
|
1008
|
+
let quoteModeUsed = quoteMode;
|
|
1009
|
+
let quote = await quoteWithMode(quoteModeUsed);
|
|
1010
|
+
if (quoteModeUsed === 'reverse' && quote.fromAmount > maxSourceInput) {
|
|
1011
|
+
this.logger.warn({
|
|
1012
|
+
sourceChain,
|
|
1013
|
+
targetChain,
|
|
1014
|
+
plannedQuoteMode: quoteMode,
|
|
1015
|
+
requestedTargetOutput: targetOutputAmount.toString(),
|
|
1016
|
+
quotedInput: quote.fromAmount.toString(),
|
|
1017
|
+
maxSourceInput: maxSourceInput.toString(),
|
|
1018
|
+
intentId: intent.id,
|
|
1019
|
+
}, 'Reverse bridge quote exceeded source capacity, retrying with forward quote');
|
|
1020
|
+
// Spend the full source cap on fallback; minor output drift is acceptable
|
|
1021
|
+
// and will be reconciled by later cycles rather than risking livelock.
|
|
1022
|
+
quoteModeUsed = 'forward';
|
|
1023
|
+
quote = await quoteWithMode(quoteModeUsed);
|
|
1024
|
+
}
|
|
916
1025
|
const inputRequired = quote.fromAmount;
|
|
1026
|
+
if (inputRequired > maxSourceInput) {
|
|
1027
|
+
return {
|
|
1028
|
+
success: false,
|
|
1029
|
+
error: `Bridge input ${inputRequired} exceeded planned source capacity ${maxSourceInput}`,
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
917
1032
|
this.logger.info({
|
|
918
1033
|
sourceChain,
|
|
919
1034
|
targetChain,
|
|
920
1035
|
sourceChainId,
|
|
921
1036
|
targetChainId,
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
1037
|
+
requestedTargetOutput: targetOutputAmount.toString(),
|
|
1038
|
+
requestedTargetOutputFormatted: this.formatLocalAmount(targetOutputAmount, targetToken),
|
|
1039
|
+
quoteModePlanned: quoteMode,
|
|
1040
|
+
quoteModeUsed,
|
|
1041
|
+
retriedAsForward: quoteMode === 'reverse' && quoteModeUsed === 'forward',
|
|
926
1042
|
inputRequired: inputRequired.toString(),
|
|
927
|
-
|
|
928
|
-
|
|
1043
|
+
inputRequiredFormatted: this.formatLocalAmount(inputRequired, sourceToken),
|
|
1044
|
+
quotedOutput: quote.toAmount.toString(),
|
|
1045
|
+
quotedOutputMin: quote.toAmountMin.toString(),
|
|
1046
|
+
quotedOutputFormatted: this.formatLocalAmount(quote.toAmount, targetToken),
|
|
1047
|
+
quotedOutputMinFormatted: this.formatLocalAmount(quote.toAmountMin, targetToken),
|
|
929
1048
|
gasCosts: quote.gasCosts.toString(),
|
|
930
1049
|
feeCosts: quote.feeCosts.toString(),
|
|
931
1050
|
intentId: intent.id,
|
|
932
|
-
|
|
933
|
-
}, 'Executing inventory movement via LiFi with pre-validated amount');
|
|
1051
|
+
}, 'Executing inventory movement via bridge quote');
|
|
934
1052
|
this.logger.debug({
|
|
935
1053
|
quoteId: quote.id,
|
|
936
1054
|
tool: quote.tool,
|
|
@@ -957,6 +1075,8 @@ export class InventoryRebalancer {
|
|
|
957
1075
|
txHash: result.txHash,
|
|
958
1076
|
intentId: intent.id,
|
|
959
1077
|
}, 'Inventory movement transaction executed');
|
|
1078
|
+
// Keep bridge consumption in source-local units; intent fulfillment only
|
|
1079
|
+
// advances from canonical inventory_deposit amounts after transferRemote.
|
|
960
1080
|
await this.actionTracker.createRebalanceAction({
|
|
961
1081
|
intentId: intent.id,
|
|
962
1082
|
origin: this.multiProvider.getDomainId(sourceChain),
|
|
@@ -974,19 +1094,27 @@ export class InventoryRebalancer {
|
|
|
974
1094
|
amountConsumed: inputRequired.toString(),
|
|
975
1095
|
totalConsumed: (currentConsumed + inputRequired).toString(),
|
|
976
1096
|
}, 'Updated consumed inventory after LiFi bridge');
|
|
977
|
-
return {
|
|
1097
|
+
return {
|
|
1098
|
+
success: true,
|
|
1099
|
+
txHash: result.txHash,
|
|
1100
|
+
inputRequired,
|
|
1101
|
+
quotedOutput: quote.toAmount,
|
|
1102
|
+
quotedOutputMin: quote.toAmountMin,
|
|
1103
|
+
quoteModeUsed,
|
|
1104
|
+
};
|
|
978
1105
|
}
|
|
979
1106
|
catch (error) {
|
|
1107
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
980
1108
|
this.logger.error({
|
|
981
1109
|
sourceChain,
|
|
982
1110
|
targetChain,
|
|
983
|
-
amount:
|
|
1111
|
+
amount: targetOutputAmount.toString(),
|
|
984
1112
|
intentId: intent.id,
|
|
985
|
-
error:
|
|
1113
|
+
error: errorMessage,
|
|
986
1114
|
}, 'Failed to execute inventory movement');
|
|
987
1115
|
return {
|
|
988
1116
|
success: false,
|
|
989
|
-
error:
|
|
1117
|
+
error: errorMessage,
|
|
990
1118
|
};
|
|
991
1119
|
}
|
|
992
1120
|
}
|