@humanode-hyperlane/rebalancer 0.0.0-stage → 27.2.2
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/LICENSE.md +193 -0
- package/README.md +297 -2
- package/dist/bridges/LiFiBridge.d.ts +93 -0
- package/dist/bridges/LiFiBridge.d.ts.map +1 -0
- package/dist/bridges/LiFiBridge.js +527 -0
- package/dist/bridges/LiFiBridge.js.map +1 -0
- package/dist/bridges/LiFiBridge.test.d.ts +2 -0
- package/dist/bridges/LiFiBridge.test.d.ts.map +1 -0
- package/dist/bridges/LiFiBridge.test.js +504 -0
- package/dist/bridges/LiFiBridge.test.js.map +1 -0
- package/dist/config/RebalancerConfig.d.ts +20 -0
- package/dist/config/RebalancerConfig.d.ts.map +1 -0
- package/dist/config/RebalancerConfig.js +35 -0
- package/dist/config/RebalancerConfig.js.map +1 -0
- package/dist/config/RebalancerConfig.test.d.ts +2 -0
- package/dist/config/RebalancerConfig.test.d.ts.map +1 -0
- package/dist/config/RebalancerConfig.test.js +798 -0
- package/dist/config/RebalancerConfig.test.js.map +1 -0
- package/dist/config/types.d.ts +3008 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +451 -0
- package/dist/config/types.js.map +1 -0
- package/dist/core/InventoryRebalancer.d.ts +201 -0
- package/dist/core/InventoryRebalancer.d.ts.map +1 -0
- package/dist/core/InventoryRebalancer.js +994 -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 +1511 -0
- package/dist/core/InventoryRebalancer.test.js.map +1 -0
- package/dist/core/Rebalancer.d.ts +37 -0
- package/dist/core/Rebalancer.d.ts.map +1 -0
- package/dist/core/Rebalancer.js +433 -0
- package/dist/core/Rebalancer.js.map +1 -0
- package/dist/core/Rebalancer.test.d.ts +2 -0
- package/dist/core/Rebalancer.test.d.ts.map +1 -0
- package/dist/core/Rebalancer.test.js +424 -0
- package/dist/core/Rebalancer.test.js.map +1 -0
- package/dist/core/RebalancerOrchestrator.d.ts +56 -0
- package/dist/core/RebalancerOrchestrator.d.ts.map +1 -0
- package/dist/core/RebalancerOrchestrator.js +154 -0
- package/dist/core/RebalancerOrchestrator.js.map +1 -0
- 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 +721 -0
- package/dist/core/RebalancerOrchestrator.test.js.map +1 -0
- package/dist/core/RebalancerService.d.ts +127 -0
- package/dist/core/RebalancerService.d.ts.map +1 -0
- package/dist/core/RebalancerService.js +270 -0
- package/dist/core/RebalancerService.js.map +1 -0
- package/dist/core/RebalancerService.test.d.ts +2 -0
- package/dist/core/RebalancerService.test.d.ts.map +1 -0
- package/dist/core/RebalancerService.test.js +777 -0
- package/dist/core/RebalancerService.test.js.map +1 -0
- package/dist/e2e/collateral-deficit.e2e-test.d.ts +2 -0
- package/dist/e2e/collateral-deficit.e2e-test.d.ts.map +1 -0
- package/dist/e2e/collateral-deficit.e2e-test.js +227 -0
- package/dist/e2e/collateral-deficit.e2e-test.js.map +1 -0
- package/dist/e2e/composite.e2e-test.d.ts +2 -0
- package/dist/e2e/composite.e2e-test.d.ts.map +1 -0
- package/dist/e2e/composite.e2e-test.js +666 -0
- package/dist/e2e/composite.e2e-test.js.map +1 -0
- package/dist/e2e/fixtures/routes.d.ts +89 -0
- package/dist/e2e/fixtures/routes.d.ts.map +1 -0
- package/dist/e2e/fixtures/routes.js +420 -0
- package/dist/e2e/fixtures/routes.js.map +1 -0
- package/dist/e2e/harness/BaseLocalDeploymentManager.d.ts +38 -0
- package/dist/e2e/harness/BaseLocalDeploymentManager.d.ts.map +1 -0
- package/dist/e2e/harness/BaseLocalDeploymentManager.js +144 -0
- package/dist/e2e/harness/BaseLocalDeploymentManager.js.map +1 -0
- package/dist/e2e/harness/BridgeSetup.d.ts +11 -0
- package/dist/e2e/harness/BridgeSetup.d.ts.map +1 -0
- package/dist/e2e/harness/BridgeSetup.js +50 -0
- package/dist/e2e/harness/BridgeSetup.js.map +1 -0
- package/dist/e2e/harness/Erc20InventoryLocalDeploymentManager.d.ts +13 -0
- package/dist/e2e/harness/Erc20InventoryLocalDeploymentManager.d.ts.map +1 -0
- package/dist/e2e/harness/Erc20InventoryLocalDeploymentManager.js +105 -0
- package/dist/e2e/harness/Erc20InventoryLocalDeploymentManager.js.map +1 -0
- package/dist/e2e/harness/Erc20LocalDeploymentManager.d.ts +11 -0
- package/dist/e2e/harness/Erc20LocalDeploymentManager.d.ts.map +1 -0
- package/dist/e2e/harness/Erc20LocalDeploymentManager.js +105 -0
- package/dist/e2e/harness/Erc20LocalDeploymentManager.js.map +1 -0
- package/dist/e2e/harness/ForkIndexer.d.ts +23 -0
- package/dist/e2e/harness/ForkIndexer.d.ts.map +1 -0
- package/dist/e2e/harness/ForkIndexer.js +127 -0
- package/dist/e2e/harness/ForkIndexer.js.map +1 -0
- package/dist/e2e/harness/ForkIndexer.test.d.ts +2 -0
- package/dist/e2e/harness/ForkIndexer.test.d.ts.map +1 -0
- package/dist/e2e/harness/ForkIndexer.test.js +257 -0
- package/dist/e2e/harness/ForkIndexer.test.js.map +1 -0
- package/dist/e2e/harness/MixedLocalDeploymentManager.d.ts +21 -0
- package/dist/e2e/harness/MixedLocalDeploymentManager.d.ts.map +1 -0
- package/dist/e2e/harness/MixedLocalDeploymentManager.js +115 -0
- package/dist/e2e/harness/MixedLocalDeploymentManager.js.map +1 -0
- package/dist/e2e/harness/MockExplorerClient.d.ts +23 -0
- package/dist/e2e/harness/MockExplorerClient.d.ts.map +1 -0
- package/dist/e2e/harness/MockExplorerClient.js +57 -0
- package/dist/e2e/harness/MockExplorerClient.js.map +1 -0
- package/dist/e2e/harness/MockExternalBridge.d.ts +32 -0
- package/dist/e2e/harness/MockExternalBridge.d.ts.map +1 -0
- package/dist/e2e/harness/MockExternalBridge.js +247 -0
- package/dist/e2e/harness/MockExternalBridge.js.map +1 -0
- package/dist/e2e/harness/NativeLocalDeploymentManager.d.ts +13 -0
- package/dist/e2e/harness/NativeLocalDeploymentManager.d.ts.map +1 -0
- package/dist/e2e/harness/NativeLocalDeploymentManager.js +91 -0
- package/dist/e2e/harness/NativeLocalDeploymentManager.js.map +1 -0
- package/dist/e2e/harness/SnapshotHelper.d.ts +13 -0
- package/dist/e2e/harness/SnapshotHelper.d.ts.map +1 -0
- package/dist/e2e/harness/SnapshotHelper.js +36 -0
- package/dist/e2e/harness/SnapshotHelper.js.map +1 -0
- package/dist/e2e/harness/TestHelpers.d.ts +25 -0
- package/dist/e2e/harness/TestHelpers.d.ts.map +1 -0
- package/dist/e2e/harness/TestHelpers.js +103 -0
- package/dist/e2e/harness/TestHelpers.js.map +1 -0
- package/dist/e2e/harness/TestRebalancer.d.ts +85 -0
- package/dist/e2e/harness/TestRebalancer.d.ts.map +1 -0
- package/dist/e2e/harness/TestRebalancer.js +427 -0
- package/dist/e2e/harness/TestRebalancer.js.map +1 -0
- package/dist/e2e/harness/TransferHelper.d.ts +34 -0
- package/dist/e2e/harness/TransferHelper.d.ts.map +1 -0
- package/dist/e2e/harness/TransferHelper.js +93 -0
- package/dist/e2e/harness/TransferHelper.js.map +1 -0
- package/dist/e2e/inventory-erc20-minAmount.e2e-test.d.ts +2 -0
- package/dist/e2e/inventory-erc20-minAmount.e2e-test.d.ts.map +1 -0
- package/dist/e2e/inventory-erc20-minAmount.e2e-test.js +411 -0
- package/dist/e2e/inventory-erc20-minAmount.e2e-test.js.map +1 -0
- package/dist/e2e/inventory-erc20-weighted.e2e-test.d.ts +2 -0
- package/dist/e2e/inventory-erc20-weighted.e2e-test.d.ts.map +1 -0
- package/dist/e2e/inventory-erc20-weighted.e2e-test.js +408 -0
- package/dist/e2e/inventory-erc20-weighted.e2e-test.js.map +1 -0
- package/dist/e2e/inventory-minAmount.e2e-test.d.ts +2 -0
- package/dist/e2e/inventory-minAmount.e2e-test.d.ts.map +1 -0
- package/dist/e2e/inventory-minAmount.e2e-test.js +411 -0
- package/dist/e2e/inventory-minAmount.e2e-test.js.map +1 -0
- package/dist/e2e/inventory-weighted.e2e-test.d.ts +2 -0
- package/dist/e2e/inventory-weighted.e2e-test.d.ts.map +1 -0
- package/dist/e2e/inventory-weighted.e2e-test.js +409 -0
- package/dist/e2e/inventory-weighted.e2e-test.js.map +1 -0
- package/dist/e2e/minAmount.e2e-test.d.ts +2 -0
- package/dist/e2e/minAmount.e2e-test.d.ts.map +1 -0
- package/dist/e2e/minAmount.e2e-test.js +203 -0
- package/dist/e2e/minAmount.e2e-test.js.map +1 -0
- package/dist/e2e/mixed-weighted.e2e-test.d.ts +2 -0
- package/dist/e2e/mixed-weighted.e2e-test.d.ts.map +1 -0
- package/dist/e2e/mixed-weighted.e2e-test.js +189 -0
- package/dist/e2e/mixed-weighted.e2e-test.js.map +1 -0
- package/dist/e2e/weighted.e2e-test.d.ts +2 -0
- package/dist/e2e/weighted.e2e-test.d.ts.map +1 -0
- package/dist/e2e/weighted.e2e-test.js +198 -0
- package/dist/e2e/weighted.e2e-test.js.map +1 -0
- package/dist/factories/RebalancerContextFactory.d.ts +89 -0
- package/dist/factories/RebalancerContextFactory.d.ts.map +1 -0
- package/dist/factories/RebalancerContextFactory.js +420 -0
- package/dist/factories/RebalancerContextFactory.js.map +1 -0
- package/dist/factories/RebalancerContextFactory.test.d.ts +2 -0
- package/dist/factories/RebalancerContextFactory.test.d.ts.map +1 -0
- package/dist/factories/RebalancerContextFactory.test.js +270 -0
- package/dist/factories/RebalancerContextFactory.test.js.map +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/IExternalBridge.d.ts +103 -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/IMetrics.d.ts +5 -0
- package/dist/interfaces/IMetrics.d.ts.map +1 -0
- package/dist/interfaces/IMetrics.js +2 -0
- package/dist/interfaces/IMetrics.js.map +1 -0
- package/dist/interfaces/IMonitor.d.ts +50 -0
- package/dist/interfaces/IMonitor.d.ts.map +1 -0
- package/dist/interfaces/IMonitor.js +14 -0
- package/dist/interfaces/IMonitor.js.map +1 -0
- package/dist/interfaces/IRebalancer.d.ts +31 -0
- package/dist/interfaces/IRebalancer.d.ts.map +1 -0
- package/dist/interfaces/IRebalancer.js +2 -0
- package/dist/interfaces/IRebalancer.js.map +1 -0
- package/dist/interfaces/IStrategy.d.ts +60 -0
- package/dist/interfaces/IStrategy.d.ts.map +1 -0
- package/dist/interfaces/IStrategy.js +13 -0
- package/dist/interfaces/IStrategy.js.map +1 -0
- package/dist/metrics/Metrics.d.ts +25 -0
- package/dist/metrics/Metrics.d.ts.map +1 -0
- package/dist/metrics/Metrics.js +164 -0
- package/dist/metrics/Metrics.js.map +1 -0
- package/dist/metrics/PriceGetter.d.ts +10 -0
- package/dist/metrics/PriceGetter.d.ts.map +1 -0
- package/dist/metrics/PriceGetter.js +41 -0
- package/dist/metrics/PriceGetter.js.map +1 -0
- package/dist/metrics/scripts/metrics.d.ts +28 -0
- package/dist/metrics/scripts/metrics.d.ts.map +1 -0
- package/dist/metrics/scripts/metrics.js +61 -0
- package/dist/metrics/scripts/metrics.js.map +1 -0
- package/dist/monitor/Monitor.d.ts +40 -0
- package/dist/monitor/Monitor.d.ts.map +1 -0
- package/dist/monitor/Monitor.js +211 -0
- package/dist/monitor/Monitor.js.map +1 -0
- package/dist/service.d.ts +3 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +205 -0
- package/dist/service.js.map +1 -0
- package/dist/strategy/BaseStrategy.d.ts +86 -0
- package/dist/strategy/BaseStrategy.d.ts.map +1 -0
- package/dist/strategy/BaseStrategy.js +342 -0
- package/dist/strategy/BaseStrategy.js.map +1 -0
- package/dist/strategy/CollateralDeficitStrategy.d.ts +65 -0
- package/dist/strategy/CollateralDeficitStrategy.d.ts.map +1 -0
- package/dist/strategy/CollateralDeficitStrategy.js +253 -0
- package/dist/strategy/CollateralDeficitStrategy.js.map +1 -0
- package/dist/strategy/CollateralDeficitStrategy.test.d.ts +2 -0
- package/dist/strategy/CollateralDeficitStrategy.test.d.ts.map +1 -0
- package/dist/strategy/CollateralDeficitStrategy.test.js +497 -0
- package/dist/strategy/CollateralDeficitStrategy.test.js.map +1 -0
- package/dist/strategy/CompositeStrategy.d.ts +18 -0
- package/dist/strategy/CompositeStrategy.d.ts.map +1 -0
- package/dist/strategy/CompositeStrategy.js +63 -0
- package/dist/strategy/CompositeStrategy.js.map +1 -0
- package/dist/strategy/CompositeStrategy.test.d.ts +2 -0
- package/dist/strategy/CompositeStrategy.test.d.ts.map +1 -0
- package/dist/strategy/CompositeStrategy.test.js +278 -0
- package/dist/strategy/CompositeStrategy.test.js.map +1 -0
- package/dist/strategy/MinAmountStrategy.d.ts +35 -0
- package/dist/strategy/MinAmountStrategy.d.ts.map +1 -0
- package/dist/strategy/MinAmountStrategy.js +112 -0
- package/dist/strategy/MinAmountStrategy.js.map +1 -0
- package/dist/strategy/MinAmountStrategy.test.d.ts +2 -0
- package/dist/strategy/MinAmountStrategy.test.d.ts.map +1 -0
- package/dist/strategy/MinAmountStrategy.test.js +547 -0
- package/dist/strategy/MinAmountStrategy.test.js.map +1 -0
- package/dist/strategy/StrategyFactory.d.ts +27 -0
- package/dist/strategy/StrategyFactory.d.ts.map +1 -0
- package/dist/strategy/StrategyFactory.js +79 -0
- package/dist/strategy/StrategyFactory.js.map +1 -0
- package/dist/strategy/StrategyFactory.test.d.ts +2 -0
- package/dist/strategy/StrategyFactory.test.d.ts.map +1 -0
- package/dist/strategy/StrategyFactory.test.js +81 -0
- package/dist/strategy/StrategyFactory.test.js.map +1 -0
- package/dist/strategy/WeightedStrategy.d.ts +32 -0
- package/dist/strategy/WeightedStrategy.d.ts.map +1 -0
- package/dist/strategy/WeightedStrategy.js +73 -0
- package/dist/strategy/WeightedStrategy.js.map +1 -0
- package/dist/strategy/WeightedStrategy.test.d.ts +2 -0
- package/dist/strategy/WeightedStrategy.test.d.ts.map +1 -0
- package/dist/strategy/WeightedStrategy.test.js +403 -0
- package/dist/strategy/WeightedStrategy.test.js.map +1 -0
- package/dist/strategy/index.d.ts +7 -0
- package/dist/strategy/index.d.ts.map +1 -0
- package/dist/strategy/index.js +7 -0
- package/dist/strategy/index.js.map +1 -0
- package/dist/test/helpers.d.ts +99 -0
- package/dist/test/helpers.d.ts.map +1 -0
- package/dist/test/helpers.js +311 -0
- package/dist/test/helpers.js.map +1 -0
- package/dist/test/lifiMocks.d.ts +52 -0
- package/dist/test/lifiMocks.d.ts.map +1 -0
- package/dist/test/lifiMocks.js +160 -0
- package/dist/test/lifiMocks.js.map +1 -0
- package/dist/tracking/ActionTracker.d.ts +90 -0
- package/dist/tracking/ActionTracker.d.ts.map +1 -0
- package/dist/tracking/ActionTracker.js +678 -0
- package/dist/tracking/ActionTracker.js.map +1 -0
- package/dist/tracking/ActionTracker.test.d.ts +2 -0
- package/dist/tracking/ActionTracker.test.d.ts.map +1 -0
- package/dist/tracking/ActionTracker.test.js +1351 -0
- package/dist/tracking/ActionTracker.test.js.map +1 -0
- package/dist/tracking/IActionTracker.d.ts +157 -0
- package/dist/tracking/IActionTracker.d.ts.map +1 -0
- package/dist/tracking/IActionTracker.js +2 -0
- package/dist/tracking/IActionTracker.js.map +1 -0
- package/dist/tracking/InflightContextAdapter.d.ts +18 -0
- package/dist/tracking/InflightContextAdapter.d.ts.map +1 -0
- package/dist/tracking/InflightContextAdapter.js +52 -0
- package/dist/tracking/InflightContextAdapter.js.map +1 -0
- package/dist/tracking/InflightContextAdapter.test.d.ts +2 -0
- package/dist/tracking/InflightContextAdapter.test.d.ts.map +1 -0
- package/dist/tracking/InflightContextAdapter.test.js +175 -0
- package/dist/tracking/InflightContextAdapter.test.js.map +1 -0
- package/dist/tracking/store/IStore.d.ts +41 -0
- package/dist/tracking/store/IStore.d.ts.map +1 -0
- package/dist/tracking/store/IStore.js +2 -0
- package/dist/tracking/store/IStore.js.map +1 -0
- package/dist/tracking/store/InMemoryStore.d.ts +21 -0
- package/dist/tracking/store/InMemoryStore.d.ts.map +1 -0
- package/dist/tracking/store/InMemoryStore.js +40 -0
- package/dist/tracking/store/InMemoryStore.js.map +1 -0
- package/dist/tracking/store/InMemoryStore.test.d.ts +2 -0
- package/dist/tracking/store/InMemoryStore.test.d.ts.map +1 -0
- package/dist/tracking/store/InMemoryStore.test.js +290 -0
- package/dist/tracking/store/InMemoryStore.test.js.map +1 -0
- package/dist/tracking/store/index.d.ts +3 -0
- package/dist/tracking/store/index.d.ts.map +1 -0
- package/dist/tracking/store/index.js +2 -0
- package/dist/tracking/store/index.js.map +1 -0
- package/dist/tracking/types.d.ts +77 -0
- package/dist/tracking/types.d.ts.map +1 -0
- package/dist/tracking/types.js +2 -0
- package/dist/tracking/types.js.map +1 -0
- package/dist/utils/ExplorerClient.d.ts +68 -0
- package/dist/utils/ExplorerClient.d.ts.map +1 -0
- package/dist/utils/ExplorerClient.js +324 -0
- package/dist/utils/ExplorerClient.js.map +1 -0
- package/dist/utils/ExplorerClient.test.d.ts +2 -0
- package/dist/utils/ExplorerClient.test.d.ts.map +1 -0
- package/dist/utils/ExplorerClient.test.js +261 -0
- package/dist/utils/ExplorerClient.test.js.map +1 -0
- package/dist/utils/balanceUtils.d.ts +12 -0
- package/dist/utils/balanceUtils.d.ts.map +1 -0
- package/dist/utils/balanceUtils.js +40 -0
- package/dist/utils/balanceUtils.js.map +1 -0
- package/dist/utils/balanceUtils.test.d.ts +2 -0
- package/dist/utils/balanceUtils.test.d.ts.map +1 -0
- package/dist/utils/balanceUtils.test.js +55 -0
- package/dist/utils/balanceUtils.test.js.map +1 -0
- package/dist/utils/blockTag.d.ts +14 -0
- package/dist/utils/blockTag.d.ts.map +1 -0
- package/dist/utils/blockTag.js +46 -0
- package/dist/utils/blockTag.js.map +1 -0
- package/dist/utils/bridgeUtils.d.ts +40 -0
- package/dist/utils/bridgeUtils.d.ts.map +1 -0
- package/dist/utils/bridgeUtils.js +54 -0
- package/dist/utils/bridgeUtils.js.map +1 -0
- package/dist/utils/bridgeUtils.test.d.ts +2 -0
- package/dist/utils/bridgeUtils.test.d.ts.map +1 -0
- package/dist/utils/bridgeUtils.test.js +163 -0
- package/dist/utils/bridgeUtils.test.js.map +1 -0
- package/dist/utils/errors.d.ts +4 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +6 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/gasEstimation.d.ts +65 -0
- package/dist/utils/gasEstimation.d.ts.map +1 -0
- package/dist/utils/gasEstimation.js +206 -0
- package/dist/utils/gasEstimation.js.map +1 -0
- package/dist/utils/solanaKeyParser.d.ts +10 -0
- package/dist/utils/solanaKeyParser.d.ts.map +1 -0
- package/dist/utils/solanaKeyParser.js +87 -0
- package/dist/utils/solanaKeyParser.js.map +1 -0
- package/dist/utils/solanaKeyParser.test.d.ts +2 -0
- package/dist/utils/solanaKeyParser.test.d.ts.map +1 -0
- package/dist/utils/solanaKeyParser.test.js +71 -0
- package/dist/utils/solanaKeyParser.test.js.map +1 -0
- package/dist/utils/tokenUtils.d.ts +38 -0
- package/dist/utils/tokenUtils.d.ts.map +1 -0
- package/dist/utils/tokenUtils.js +65 -0
- package/dist/utils/tokenUtils.js.map +1 -0
- package/dist/utils/transactionUtils.d.ts +22 -0
- package/dist/utils/transactionUtils.d.ts.map +1 -0
- package/dist/utils/transactionUtils.js +26 -0
- package/dist/utils/transactionUtils.js.map +1 -0
- package/package.json +87 -3
- package/src/bridges/LiFiBridge.test.ts +615 -0
- package/src/bridges/LiFiBridge.ts +731 -0
- package/src/config/RebalancerConfig.test.ts +921 -0
- package/src/config/RebalancerConfig.ts +63 -0
- package/src/config/types.ts +613 -0
- package/src/core/InventoryRebalancer.test.ts +1858 -0
- package/src/core/InventoryRebalancer.ts +1455 -0
- package/src/core/Rebalancer.test.ts +686 -0
- package/src/core/Rebalancer.ts +686 -0
- package/src/core/RebalancerOrchestrator.test.ts +871 -0
- package/src/core/RebalancerOrchestrator.ts +273 -0
- package/src/core/RebalancerService.test.ts +1094 -0
- package/src/core/RebalancerService.ts +412 -0
- package/src/e2e/E2E_TEST_CONVENTIONS.md +222 -0
- package/src/e2e/collateral-deficit.e2e-test.ts +403 -0
- package/src/e2e/composite.e2e-test.ts +1008 -0
- package/src/e2e/fixtures/routes.ts +532 -0
- package/src/e2e/harness/BaseLocalDeploymentManager.ts +246 -0
- package/src/e2e/harness/BridgeSetup.ts +95 -0
- package/src/e2e/harness/Erc20InventoryLocalDeploymentManager.ts +183 -0
- package/src/e2e/harness/Erc20LocalDeploymentManager.ts +190 -0
- package/src/e2e/harness/ForkIndexer.test.ts +448 -0
- package/src/e2e/harness/ForkIndexer.ts +183 -0
- package/src/e2e/harness/MixedLocalDeploymentManager.ts +196 -0
- package/src/e2e/harness/MockExplorerClient.ts +94 -0
- package/src/e2e/harness/MockExternalBridge.ts +411 -0
- package/src/e2e/harness/NativeLocalDeploymentManager.ts +138 -0
- package/src/e2e/harness/SnapshotHelper.ts +56 -0
- package/src/e2e/harness/TestHelpers.ts +169 -0
- package/src/e2e/harness/TestRebalancer.ts +758 -0
- package/src/e2e/harness/TransferHelper.ts +188 -0
- package/src/e2e/inventory-erc20-minAmount.e2e-test.ts +735 -0
- package/src/e2e/inventory-erc20-weighted.e2e-test.ts +733 -0
- package/src/e2e/inventory-minAmount.e2e-test.ts +734 -0
- package/src/e2e/inventory-weighted.e2e-test.ts +734 -0
- package/src/e2e/minAmount.e2e-test.ts +313 -0
- package/src/e2e/mixed-weighted.e2e-test.ts +267 -0
- package/src/e2e/weighted.e2e-test.ts +304 -0
- package/src/factories/RebalancerContextFactory.test.ts +394 -0
- package/src/factories/RebalancerContextFactory.ts +771 -0
- package/src/index.ts +125 -0
- package/src/interfaces/IExternalBridge.ts +117 -0
- package/src/interfaces/IMetrics.ts +5 -0
- package/src/interfaces/IMonitor.ts +71 -0
- package/src/interfaces/IRebalancer.ts +57 -0
- package/src/interfaces/IStrategy.ts +81 -0
- package/src/metrics/Metrics.ts +335 -0
- package/src/metrics/PriceGetter.ts +74 -0
- package/src/metrics/scripts/metrics.ts +139 -0
- package/src/monitor/Monitor.ts +324 -0
- package/src/service.ts +285 -0
- package/src/strategy/BaseStrategy.ts +553 -0
- package/src/strategy/CollateralDeficitStrategy.test.ts +728 -0
- package/src/strategy/CollateralDeficitStrategy.ts +421 -0
- package/src/strategy/CompositeStrategy.test.ts +418 -0
- package/src/strategy/CompositeStrategy.ts +102 -0
- package/src/strategy/MinAmountStrategy.test.ts +732 -0
- package/src/strategy/MinAmountStrategy.ts +200 -0
- package/src/strategy/StrategyFactory.test.ts +108 -0
- package/src/strategy/StrategyFactory.ts +157 -0
- package/src/strategy/WeightedStrategy.test.ts +529 -0
- package/src/strategy/WeightedStrategy.ts +126 -0
- package/src/strategy/index.ts +6 -0
- package/src/test/helpers.ts +477 -0
- package/src/test/lifiMocks.ts +211 -0
- package/src/tracking/ActionTracker.test.ts +1625 -0
- package/src/tracking/ActionTracker.ts +1019 -0
- package/src/tracking/IActionTracker.ts +213 -0
- package/src/tracking/InflightContextAdapter.test.ts +206 -0
- package/src/tracking/InflightContextAdapter.ts +75 -0
- package/src/tracking/store/IStore.ts +48 -0
- package/src/tracking/store/InMemoryStore.test.ts +338 -0
- package/src/tracking/store/InMemoryStore.ts +59 -0
- package/src/tracking/store/index.ts +2 -0
- package/src/tracking/types.ts +118 -0
- package/src/utils/ExplorerClient.test.ts +386 -0
- package/src/utils/ExplorerClient.ts +439 -0
- package/src/utils/balanceUtils.test.ts +79 -0
- package/src/utils/balanceUtils.ts +65 -0
- package/src/utils/blockTag.ts +68 -0
- package/src/utils/bridgeUtils.test.ts +193 -0
- package/src/utils/bridgeUtils.ts +101 -0
- package/src/utils/errors.ts +5 -0
- package/src/utils/gasEstimation.ts +306 -0
- package/src/utils/solanaKeyParser.test.ts +103 -0
- package/src/utils/solanaKeyParser.ts +117 -0
- package/src/utils/tokenUtils.ts +89 -0
- package/src/utils/transactionUtils.ts +41 -0
|
@@ -0,0 +1,678 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
2
|
+
import { assert, parseWarpRouteMessage } from '@humanode-hyperlane/utils';
|
|
3
|
+
import { DEFAULT_MOVEMENT_STALENESS_MS } from '../config/types.js';
|
|
4
|
+
import { getConfirmedBlockTag } from '../utils/blockTag.js';
|
|
5
|
+
/**
|
|
6
|
+
* ActionTracker implementation managing the lifecycle of tracked entities.
|
|
7
|
+
*/
|
|
8
|
+
export class ActionTracker {
|
|
9
|
+
transferStore;
|
|
10
|
+
rebalanceIntentStore;
|
|
11
|
+
rebalanceActionStore;
|
|
12
|
+
explorerClient;
|
|
13
|
+
core;
|
|
14
|
+
config;
|
|
15
|
+
logger;
|
|
16
|
+
constructor(transferStore, rebalanceIntentStore, rebalanceActionStore, explorerClient, core, config, logger) {
|
|
17
|
+
this.transferStore = transferStore;
|
|
18
|
+
this.rebalanceIntentStore = rebalanceIntentStore;
|
|
19
|
+
this.rebalanceActionStore = rebalanceActionStore;
|
|
20
|
+
this.explorerClient = explorerClient;
|
|
21
|
+
this.core = core;
|
|
22
|
+
this.config = config;
|
|
23
|
+
this.logger = logger;
|
|
24
|
+
}
|
|
25
|
+
// === Lifecycle ===
|
|
26
|
+
async initialize() {
|
|
27
|
+
this.logger.info('ActionTracker initializing');
|
|
28
|
+
// Log config for debugging
|
|
29
|
+
this.logger.debug({
|
|
30
|
+
routersByDomain: this.config.routersByDomain,
|
|
31
|
+
bridges: this.config.bridges,
|
|
32
|
+
rebalancerAddress: this.config.rebalancerAddress,
|
|
33
|
+
}, 'ActionTracker config');
|
|
34
|
+
// 1. Startup recovery: query Explorer for inflight rebalance messages
|
|
35
|
+
const inflightMessages = await this.explorerClient.getInflightRebalanceActions({
|
|
36
|
+
bridges: this.config.bridges,
|
|
37
|
+
routersByDomain: this.config.routersByDomain,
|
|
38
|
+
rebalancerAddress: this.config.rebalancerAddress,
|
|
39
|
+
inventorySignerAddresses: this.config.inventorySignerAddresses,
|
|
40
|
+
}, this.logger);
|
|
41
|
+
this.logger.info({ count: inflightMessages.length }, 'Found inflight rebalance messages during startup');
|
|
42
|
+
// 2. For each message, create synthetic intent + action
|
|
43
|
+
for (const msg of inflightMessages) {
|
|
44
|
+
await this.recoverAction(msg);
|
|
45
|
+
}
|
|
46
|
+
// 3. Sync all stores
|
|
47
|
+
await this.syncTransfers();
|
|
48
|
+
await this.syncRebalanceIntents();
|
|
49
|
+
await this.syncRebalanceActions();
|
|
50
|
+
// Log store contents for debugging
|
|
51
|
+
await this.logStoreContents();
|
|
52
|
+
this.logger.info('ActionTracker initialized');
|
|
53
|
+
}
|
|
54
|
+
// === Sync Operations ===
|
|
55
|
+
async syncTransfers(confirmedBlockTags) {
|
|
56
|
+
this.logger.debug('Syncing transfers');
|
|
57
|
+
// Build list of addresses to exclude (rebalancer + optional inventory signer)
|
|
58
|
+
const excludeTxSenders = [this.config.rebalancerAddress];
|
|
59
|
+
if (this.config.inventorySignerAddresses) {
|
|
60
|
+
excludeTxSenders.push(...this.config.inventorySignerAddresses);
|
|
61
|
+
}
|
|
62
|
+
const inflightMessages = await this.explorerClient.getInflightUserTransfers({
|
|
63
|
+
routersByDomain: this.config.routersByDomain,
|
|
64
|
+
excludeTxSenders,
|
|
65
|
+
}, this.logger);
|
|
66
|
+
this.logger.debug({ count: inflightMessages.length }, 'Received inflight user transfers from Explorer');
|
|
67
|
+
let newTransfers = 0;
|
|
68
|
+
let completedTransfers = 0;
|
|
69
|
+
for (const msg of inflightMessages) {
|
|
70
|
+
const transfer = await this.transferStore.get(msg.msg_id);
|
|
71
|
+
if (!transfer) {
|
|
72
|
+
this.logger.debug({
|
|
73
|
+
msgId: msg.msg_id,
|
|
74
|
+
origin: msg.origin_domain_id,
|
|
75
|
+
destination: msg.destination_domain_id,
|
|
76
|
+
sender: msg.sender,
|
|
77
|
+
recipient: msg.recipient,
|
|
78
|
+
messageBodyLength: msg.message_body?.length,
|
|
79
|
+
messageBodyPreview: msg.message_body?.substring(0, 66),
|
|
80
|
+
}, 'Processing new transfer message');
|
|
81
|
+
try {
|
|
82
|
+
const { amount } = parseWarpRouteMessage(msg.message_body);
|
|
83
|
+
const newTransfer = {
|
|
84
|
+
id: msg.msg_id,
|
|
85
|
+
status: 'in_progress',
|
|
86
|
+
messageId: msg.msg_id,
|
|
87
|
+
origin: msg.origin_domain_id,
|
|
88
|
+
destination: msg.destination_domain_id,
|
|
89
|
+
sender: msg.sender,
|
|
90
|
+
recipient: msg.recipient,
|
|
91
|
+
amount,
|
|
92
|
+
createdAt: Date.now(),
|
|
93
|
+
updatedAt: Date.now(),
|
|
94
|
+
};
|
|
95
|
+
await this.transferStore.save(newTransfer);
|
|
96
|
+
newTransfers++;
|
|
97
|
+
this.logger.debug({ id: newTransfer.id, amount: amount.toString() }, 'Created new transfer');
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
this.logger.warn({
|
|
101
|
+
msgId: msg.msg_id,
|
|
102
|
+
messageBody: msg.message_body,
|
|
103
|
+
messageBodyLength: msg.message_body?.length,
|
|
104
|
+
origin: msg.origin_domain_id,
|
|
105
|
+
destination: msg.destination_domain_id,
|
|
106
|
+
error: error instanceof Error ? error.message : String(error),
|
|
107
|
+
}, 'Failed to parse message body, skipping transfer');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const existingTransfers = await this.getInProgressTransfers();
|
|
112
|
+
for (const transfer of existingTransfers) {
|
|
113
|
+
const blockTag = await this.getConfirmedBlockTag(transfer.destination, confirmedBlockTags);
|
|
114
|
+
const delivered = await this.isMessageDelivered(transfer.messageId, transfer.destination, blockTag);
|
|
115
|
+
if (delivered) {
|
|
116
|
+
await this.transferStore.update(transfer.id, { status: 'complete' });
|
|
117
|
+
completedTransfers++;
|
|
118
|
+
this.logger.debug({ id: transfer.id }, 'Transfer completed');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const inProgressCount = (await this.getInProgressTransfers()).length;
|
|
122
|
+
this.logger.info({
|
|
123
|
+
newTransfers,
|
|
124
|
+
completed: completedTransfers,
|
|
125
|
+
inProgress: inProgressCount,
|
|
126
|
+
}, 'Transfers synced');
|
|
127
|
+
}
|
|
128
|
+
async syncRebalanceIntents() {
|
|
129
|
+
this.logger.debug('Syncing rebalance intents');
|
|
130
|
+
// Check in_progress intents for completion or TTL expiry
|
|
131
|
+
const inProgressIntents = await this.rebalanceIntentStore.getByStatus('in_progress');
|
|
132
|
+
const allInProgressActions = await this.rebalanceActionStore.getByStatus('in_progress');
|
|
133
|
+
const now = Date.now();
|
|
134
|
+
for (const intent of inProgressIntents) {
|
|
135
|
+
const completedAmount = await this.getCompletedAmountForIntent(intent.id);
|
|
136
|
+
if (completedAmount >= intent.amount) {
|
|
137
|
+
await this.rebalanceIntentStore.update(intent.id, {
|
|
138
|
+
status: 'complete',
|
|
139
|
+
});
|
|
140
|
+
this.logger.debug({ id: intent.id }, 'RebalanceIntent completed');
|
|
141
|
+
}
|
|
142
|
+
else if (now - intent.createdAt > this.config.intentTTL) {
|
|
143
|
+
await this.rebalanceIntentStore.update(intent.id, {
|
|
144
|
+
status: 'failed',
|
|
145
|
+
});
|
|
146
|
+
// Fail any in-progress actions associated with the expired intent
|
|
147
|
+
for (const action of allInProgressActions) {
|
|
148
|
+
if (action.intentId === intent.id) {
|
|
149
|
+
await this.rebalanceActionStore.update(action.id, {
|
|
150
|
+
status: 'failed',
|
|
151
|
+
});
|
|
152
|
+
this.logger.warn({ actionId: action.id, intentId: intent.id }, 'RebalanceAction failed due to parent intent TTL expiry');
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
this.logger.debug({
|
|
156
|
+
id: intent.id,
|
|
157
|
+
origin: intent.origin,
|
|
158
|
+
destination: intent.destination,
|
|
159
|
+
amount: intent.amount.toString(),
|
|
160
|
+
ageMs: now - intent.createdAt,
|
|
161
|
+
ttlMs: this.config.intentTTL,
|
|
162
|
+
}, 'RebalanceIntent TTL expiry details');
|
|
163
|
+
this.logger.warn({ id: intent.id }, 'RebalanceIntent expired due to TTL');
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
this.logger.debug('Rebalance intents synced');
|
|
167
|
+
}
|
|
168
|
+
async syncRebalanceActions(confirmedBlockTags) {
|
|
169
|
+
this.logger.debug('Syncing rebalance actions');
|
|
170
|
+
let discoveredActions = 0;
|
|
171
|
+
let completedActions = 0;
|
|
172
|
+
const inflightMessages = await this.explorerClient.getInflightRebalanceActions({
|
|
173
|
+
bridges: this.config.bridges,
|
|
174
|
+
routersByDomain: this.config.routersByDomain,
|
|
175
|
+
rebalancerAddress: this.config.rebalancerAddress,
|
|
176
|
+
inventorySignerAddresses: this.config.inventorySignerAddresses,
|
|
177
|
+
}, this.logger);
|
|
178
|
+
this.logger.debug({ count: inflightMessages.length }, 'Found inflight rebalance actions from Explorer');
|
|
179
|
+
const allActions = await this.rebalanceActionStore.getAll();
|
|
180
|
+
for (const msg of inflightMessages) {
|
|
181
|
+
const existingAction = allActions.find((a) => a.messageId === msg.msg_id);
|
|
182
|
+
if (!existingAction) {
|
|
183
|
+
this.logger.info({
|
|
184
|
+
msgId: msg.msg_id,
|
|
185
|
+
origin: msg.origin_domain_id,
|
|
186
|
+
destination: msg.destination_domain_id,
|
|
187
|
+
}, 'Discovered new rebalance action, recovering...');
|
|
188
|
+
await this.recoverAction(msg);
|
|
189
|
+
discoveredActions++;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// Check delivery status for all in-progress actions in our store
|
|
193
|
+
// Only check delivery for actions that have a messageId (rebalance_message, inventory_deposit)
|
|
194
|
+
// inventory_movement actions are synced separately via LiFi status API
|
|
195
|
+
const inProgressActions = await this.rebalanceActionStore.getByStatus('in_progress');
|
|
196
|
+
for (const action of inProgressActions) {
|
|
197
|
+
// Skip actions without messageId (e.g., inventory_movement)
|
|
198
|
+
if (!action.messageId) {
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
const blockTag = await this.getConfirmedBlockTag(action.destination, confirmedBlockTags);
|
|
202
|
+
const delivered = await this.isMessageDelivered(action.messageId, action.destination, blockTag);
|
|
203
|
+
if (delivered) {
|
|
204
|
+
await this.completeRebalanceAction(action.id);
|
|
205
|
+
completedActions++;
|
|
206
|
+
this.logger.debug({ id: action.id }, 'RebalanceAction completed');
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
const inProgressCount = (await this.rebalanceActionStore.getByStatus('in_progress')).length;
|
|
210
|
+
this.logger.info({
|
|
211
|
+
discovered: discoveredActions,
|
|
212
|
+
completed: completedActions,
|
|
213
|
+
inProgress: inProgressCount,
|
|
214
|
+
}, 'Actions synced');
|
|
215
|
+
}
|
|
216
|
+
// === Transfer Queries ===
|
|
217
|
+
async getTransfer(id) {
|
|
218
|
+
return this.transferStore.get(id);
|
|
219
|
+
}
|
|
220
|
+
async getInProgressTransfers() {
|
|
221
|
+
return this.transferStore.getByStatus('in_progress');
|
|
222
|
+
}
|
|
223
|
+
async getTransfersByDestination(destination) {
|
|
224
|
+
return this.transferStore.getByDestination(destination);
|
|
225
|
+
}
|
|
226
|
+
// === RebalanceIntent Queries ===
|
|
227
|
+
async getRebalanceIntent(id) {
|
|
228
|
+
return this.rebalanceIntentStore.get(id);
|
|
229
|
+
}
|
|
230
|
+
async getActiveRebalanceIntents() {
|
|
231
|
+
// Only return in_progress intents - their origin tx is confirmed
|
|
232
|
+
// so simulation only needs to add to destination (origin already deducted on-chain)
|
|
233
|
+
return this.rebalanceIntentStore.getByStatus('in_progress');
|
|
234
|
+
}
|
|
235
|
+
async getRebalanceIntentsByDestination(destination) {
|
|
236
|
+
return this.rebalanceIntentStore.getByDestination(destination);
|
|
237
|
+
}
|
|
238
|
+
// === RebalanceIntent Management ===
|
|
239
|
+
async createRebalanceIntent(params) {
|
|
240
|
+
const intent = {
|
|
241
|
+
id: uuidv4(),
|
|
242
|
+
status: 'not_started',
|
|
243
|
+
origin: params.origin,
|
|
244
|
+
destination: params.destination,
|
|
245
|
+
amount: params.amount,
|
|
246
|
+
bridge: params.bridge,
|
|
247
|
+
priority: params.priority,
|
|
248
|
+
strategyType: params.strategyType,
|
|
249
|
+
executionMethod: params.executionMethod,
|
|
250
|
+
externalBridge: params.externalBridge,
|
|
251
|
+
createdAt: Date.now(),
|
|
252
|
+
updatedAt: Date.now(),
|
|
253
|
+
};
|
|
254
|
+
await this.rebalanceIntentStore.save(intent);
|
|
255
|
+
this.logger.debug({
|
|
256
|
+
id: intent.id,
|
|
257
|
+
origin: intent.origin,
|
|
258
|
+
destination: intent.destination,
|
|
259
|
+
executionMethod: intent.executionMethod,
|
|
260
|
+
}, 'Created RebalanceIntent');
|
|
261
|
+
return intent;
|
|
262
|
+
}
|
|
263
|
+
async completeRebalanceIntent(id) {
|
|
264
|
+
await this.rebalanceIntentStore.update(id, { status: 'complete' });
|
|
265
|
+
this.logger.info({ id }, 'Intent completed');
|
|
266
|
+
}
|
|
267
|
+
async cancelRebalanceIntent(id) {
|
|
268
|
+
await this.rebalanceIntentStore.update(id, { status: 'cancelled' });
|
|
269
|
+
this.logger.debug({ id }, 'Cancelled RebalanceIntent');
|
|
270
|
+
}
|
|
271
|
+
async failRebalanceIntent(id) {
|
|
272
|
+
await this.rebalanceIntentStore.update(id, { status: 'failed' });
|
|
273
|
+
this.logger.info({ id }, 'Intent failed');
|
|
274
|
+
}
|
|
275
|
+
// === RebalanceAction Queries ===
|
|
276
|
+
async getRebalanceAction(id) {
|
|
277
|
+
return this.rebalanceActionStore.get(id);
|
|
278
|
+
}
|
|
279
|
+
async getInProgressActions() {
|
|
280
|
+
return this.rebalanceActionStore.getByStatus('in_progress');
|
|
281
|
+
}
|
|
282
|
+
// === RebalanceAction Management ===
|
|
283
|
+
async createRebalanceAction(params) {
|
|
284
|
+
const action = {
|
|
285
|
+
id: uuidv4(),
|
|
286
|
+
status: 'in_progress',
|
|
287
|
+
type: params.type,
|
|
288
|
+
intentId: params.intentId,
|
|
289
|
+
messageId: params.messageId,
|
|
290
|
+
txHash: params.txHash,
|
|
291
|
+
externalBridgeTransferId: params.externalBridgeTransferId,
|
|
292
|
+
externalBridgeId: params.externalBridgeId,
|
|
293
|
+
origin: params.origin,
|
|
294
|
+
destination: params.destination,
|
|
295
|
+
amount: params.amount,
|
|
296
|
+
createdAt: Date.now(),
|
|
297
|
+
updatedAt: Date.now(),
|
|
298
|
+
};
|
|
299
|
+
await this.rebalanceActionStore.save(action);
|
|
300
|
+
// Transition parent intent from not_started to in_progress
|
|
301
|
+
const intent = await this.rebalanceIntentStore.get(params.intentId);
|
|
302
|
+
if (intent && intent.status === 'not_started') {
|
|
303
|
+
await this.rebalanceIntentStore.update(intent.id, {
|
|
304
|
+
status: 'in_progress',
|
|
305
|
+
});
|
|
306
|
+
this.logger.debug({ intentId: intent.id }, 'Transitioned RebalanceIntent to in_progress');
|
|
307
|
+
}
|
|
308
|
+
this.logger.debug({ id: action.id, intentId: action.intentId, type: action.type }, 'Created RebalanceAction');
|
|
309
|
+
return action;
|
|
310
|
+
}
|
|
311
|
+
async completeRebalanceAction(id) {
|
|
312
|
+
const action = await this.rebalanceActionStore.get(id);
|
|
313
|
+
if (!action) {
|
|
314
|
+
throw new Error(`RebalanceAction ${id} not found`);
|
|
315
|
+
}
|
|
316
|
+
await this.rebalanceActionStore.update(id, { status: 'complete' });
|
|
317
|
+
// Check if parent intent is now complete (derive from action states)
|
|
318
|
+
await this.checkAndCompleteIntent(action.intentId);
|
|
319
|
+
this.logger.info({ id, intentId: action.intentId, type: action.type }, 'Action completed');
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Check if an intent is fully fulfilled based on completed action amounts.
|
|
323
|
+
* Only `inventory_deposit` and `rebalance_message` actions count toward fulfillment.
|
|
324
|
+
*/
|
|
325
|
+
async checkAndCompleteIntent(intentId) {
|
|
326
|
+
const intent = await this.rebalanceIntentStore.get(intentId);
|
|
327
|
+
if (!intent || intent.status === 'complete')
|
|
328
|
+
return;
|
|
329
|
+
const completedAmount = await this.getCompletedAmountForIntent(intentId);
|
|
330
|
+
if (completedAmount >= intent.amount) {
|
|
331
|
+
await this.rebalanceIntentStore.update(intentId, { status: 'complete' });
|
|
332
|
+
this.logger.debug({ intentId, completedAmount: completedAmount.toString() }, 'RebalanceIntent fully fulfilled');
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Get the total completed amount for an intent from its actions.
|
|
337
|
+
* Only `inventory_deposit` and `rebalance_message` actions count.
|
|
338
|
+
*/
|
|
339
|
+
async getCompletedAmountForIntent(intentId) {
|
|
340
|
+
const actions = await this.getActionsForIntent(intentId);
|
|
341
|
+
return actions
|
|
342
|
+
.filter((a) => a.status === 'complete' &&
|
|
343
|
+
(a.type === 'inventory_deposit' || a.type === 'rebalance_message'))
|
|
344
|
+
.reduce((sum, a) => sum + a.amount, 0n);
|
|
345
|
+
}
|
|
346
|
+
async failRebalanceAction(id) {
|
|
347
|
+
await this.rebalanceActionStore.update(id, { status: 'failed' });
|
|
348
|
+
this.logger.info({ id }, 'Action failed');
|
|
349
|
+
}
|
|
350
|
+
// === RebalanceAction Queries ===
|
|
351
|
+
async getActionsByType(type) {
|
|
352
|
+
const allActions = await this.rebalanceActionStore.getAll();
|
|
353
|
+
return allActions.filter((action) => action.type === type);
|
|
354
|
+
}
|
|
355
|
+
async getInflightInventoryMovements(origin) {
|
|
356
|
+
const allActions = await this.rebalanceActionStore.getAll();
|
|
357
|
+
const inflightMovements = allActions.filter((action) => action.type === 'inventory_movement' &&
|
|
358
|
+
action.status === 'in_progress' &&
|
|
359
|
+
action.origin === origin);
|
|
360
|
+
return inflightMovements.reduce((sum, action) => sum + action.amount, BigInt(0));
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Get inventory intents that are in_progress or not_started but not fully settled.
|
|
364
|
+
* Intents with in-flight deposits are included but flagged via hasInflightDeposit.
|
|
365
|
+
* Returns enriched data with computed values derived from action states.
|
|
366
|
+
*
|
|
367
|
+
* NOTE: We include 'not_started' intents because they may have been created
|
|
368
|
+
* but failed to execute (e.g., all bridges failed viability check). Without
|
|
369
|
+
* checking for these, we would create duplicate intents every polling cycle.
|
|
370
|
+
*/
|
|
371
|
+
async getPartiallyFulfilledInventoryIntents() {
|
|
372
|
+
// Query both in_progress AND not_started intents
|
|
373
|
+
// not_started intents may exist if execution failed before any action was created
|
|
374
|
+
const [inProgressIntents, notStartedIntents] = await Promise.all([
|
|
375
|
+
this.rebalanceIntentStore.getByStatus('in_progress'),
|
|
376
|
+
this.rebalanceIntentStore.getByStatus('not_started'),
|
|
377
|
+
]);
|
|
378
|
+
const allActiveIntents = [...inProgressIntents, ...notStartedIntents];
|
|
379
|
+
const partialIntents = [];
|
|
380
|
+
for (const intent of allActiveIntents) {
|
|
381
|
+
// Only inventory execution method
|
|
382
|
+
if (intent.executionMethod !== 'inventory')
|
|
383
|
+
continue;
|
|
384
|
+
const actions = await this.getActionsForIntent(intent.id);
|
|
385
|
+
// Check for in-flight inventory_movement actions
|
|
386
|
+
// Skip intents with active bridge movement(s). Movements still `pending` on
|
|
387
|
+
// the bridge are kept alive regardless of age. Non-pending movements are only
|
|
388
|
+
// failed after they've been in a non-pending state for the staleness window
|
|
389
|
+
// (nonPendingSince), preventing premature failure from transient not_found polls.
|
|
390
|
+
// `undefined` status (pre-deploy data) falls back to createdAt for staleness.
|
|
391
|
+
const inflightMovements = actions.filter((a) => a.status === 'in_progress' && a.type === 'inventory_movement');
|
|
392
|
+
const now = Date.now();
|
|
393
|
+
const staleMovements = inflightMovements.filter((a) => {
|
|
394
|
+
if (a.lastBridgeStatus === 'pending')
|
|
395
|
+
return false;
|
|
396
|
+
// Use nonPendingSince when available; fall back to createdAt for
|
|
397
|
+
// pre-deploy data that lacks the field.
|
|
398
|
+
const nonPendingStart = a.nonPendingSince ?? a.createdAt;
|
|
399
|
+
return now - nonPendingStart >= DEFAULT_MOVEMENT_STALENESS_MS;
|
|
400
|
+
});
|
|
401
|
+
const staleMovementIds = new Set(staleMovements.map((a) => a.id));
|
|
402
|
+
const hasBlockingInflightMovement = inflightMovements.some((a) => !staleMovementIds.has(a.id));
|
|
403
|
+
if (hasBlockingInflightMovement) {
|
|
404
|
+
this.logger.debug({ intentId: intent.id }, 'Skipping partial intent - has in-flight inventory movement');
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
// Fail stale movements so the intent can proceed
|
|
408
|
+
for (const movement of staleMovements) {
|
|
409
|
+
await this.failRebalanceAction(movement.id);
|
|
410
|
+
this.logger.warn({
|
|
411
|
+
actionId: movement.id,
|
|
412
|
+
age: now - movement.createdAt,
|
|
413
|
+
nonPendingDuration: now - (movement.nonPendingSince ?? movement.createdAt),
|
|
414
|
+
intentId: intent.id,
|
|
415
|
+
lastBridgeStatus: movement.lastBridgeStatus,
|
|
416
|
+
}, 'Failing stale inventory movement to unblock intent');
|
|
417
|
+
}
|
|
418
|
+
// Compute amounts from action states
|
|
419
|
+
const completedAmount = actions
|
|
420
|
+
.filter((a) => a.status === 'complete' && a.type === 'inventory_deposit')
|
|
421
|
+
.reduce((sum, a) => sum + a.amount, 0n);
|
|
422
|
+
const inflightAmount = actions
|
|
423
|
+
.filter((a) => a.status === 'in_progress' && a.type === 'inventory_deposit')
|
|
424
|
+
.reduce((sum, a) => sum + a.amount, 0n);
|
|
425
|
+
const remaining = intent.amount - completedAmount - inflightAmount;
|
|
426
|
+
if (remaining > 0n || inflightAmount > 0n) {
|
|
427
|
+
partialIntents.push({
|
|
428
|
+
intent,
|
|
429
|
+
completedAmount,
|
|
430
|
+
remaining,
|
|
431
|
+
hasInflightDeposit: inflightAmount > 0n,
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return partialIntents;
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Get all actions associated with a specific intent.
|
|
439
|
+
*/
|
|
440
|
+
async getActionsForIntent(intentId) {
|
|
441
|
+
const allActions = await this.rebalanceActionStore.getAll();
|
|
442
|
+
return allActions.filter((a) => a.intentId === intentId);
|
|
443
|
+
}
|
|
444
|
+
async syncInventoryMovementActions(externalBridgeRegistry) {
|
|
445
|
+
this.logger.debug('Syncing inventory movement actions');
|
|
446
|
+
let completed = 0;
|
|
447
|
+
let failed = 0;
|
|
448
|
+
// Get all in-progress inventory_movement actions
|
|
449
|
+
const inProgressActions = await this.rebalanceActionStore.getByStatus('in_progress');
|
|
450
|
+
const inventoryMovements = inProgressActions.filter((a) => a.type === 'inventory_movement');
|
|
451
|
+
this.logger.debug({ count: inventoryMovements.length }, 'Found in-progress inventory movements');
|
|
452
|
+
for (const action of inventoryMovements) {
|
|
453
|
+
// Skip if no txHash (shouldn't happen but be safe)
|
|
454
|
+
if (!action.txHash) {
|
|
455
|
+
this.logger.warn({ actionId: action.id }, 'Inventory movement action has no txHash');
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
// Skip if no externalBridgeId (shouldn't happen but be safe)
|
|
459
|
+
if (!action.externalBridgeId) {
|
|
460
|
+
this.logger.warn({ actionId: action.id }, 'Inventory movement action has no externalBridgeId');
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
const externalBridge = externalBridgeRegistry[action.externalBridgeId];
|
|
464
|
+
if (!externalBridge) {
|
|
465
|
+
this.logger.warn({ actionId: action.id, bridgeId: action.externalBridgeId }, 'Bridge not found in registry');
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
try {
|
|
469
|
+
const status = await externalBridge.getStatus(action.txHash, action.origin, action.destination);
|
|
470
|
+
if (status.status === 'complete') {
|
|
471
|
+
await this.completeRebalanceAction(action.id);
|
|
472
|
+
completed++;
|
|
473
|
+
this.logger.info({
|
|
474
|
+
actionId: action.id,
|
|
475
|
+
txHash: action.txHash,
|
|
476
|
+
receivedAmount: status.receivedAmount?.toString(),
|
|
477
|
+
}, 'Inventory movement completed');
|
|
478
|
+
}
|
|
479
|
+
else if (status.status === 'failed') {
|
|
480
|
+
await this.failRebalanceAction(action.id);
|
|
481
|
+
failed++;
|
|
482
|
+
this.logger.warn({
|
|
483
|
+
actionId: action.id,
|
|
484
|
+
txHash: action.txHash,
|
|
485
|
+
error: status.error,
|
|
486
|
+
}, 'Inventory movement failed');
|
|
487
|
+
}
|
|
488
|
+
else if (status.status === 'pending') {
|
|
489
|
+
await this.rebalanceActionStore.update(action.id, {
|
|
490
|
+
lastBridgeStatus: 'pending',
|
|
491
|
+
nonPendingSince: undefined,
|
|
492
|
+
});
|
|
493
|
+
this.logger.debug({
|
|
494
|
+
actionId: action.id,
|
|
495
|
+
txHash: action.txHash,
|
|
496
|
+
substatus: status.substatus,
|
|
497
|
+
}, 'Inventory movement still pending');
|
|
498
|
+
}
|
|
499
|
+
else if (status.status === 'not_found') {
|
|
500
|
+
await this.rebalanceActionStore.update(action.id, {
|
|
501
|
+
lastBridgeStatus: 'not_found',
|
|
502
|
+
nonPendingSince: action.nonPendingSince ?? Date.now(),
|
|
503
|
+
});
|
|
504
|
+
this.logger.debug({
|
|
505
|
+
actionId: action.id,
|
|
506
|
+
txHash: action.txHash,
|
|
507
|
+
}, 'Inventory movement not found');
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
catch (error) {
|
|
511
|
+
this.logger.debug({
|
|
512
|
+
actionId: action.id,
|
|
513
|
+
txHash: action.txHash,
|
|
514
|
+
error: error.message,
|
|
515
|
+
}, 'Failed to get inventory movement status');
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
if (inventoryMovements.length > 0) {
|
|
519
|
+
this.logger.info({
|
|
520
|
+
completed,
|
|
521
|
+
failed,
|
|
522
|
+
pending: inventoryMovements.length - completed - failed,
|
|
523
|
+
}, 'Inventory movements synced');
|
|
524
|
+
}
|
|
525
|
+
return { completed, failed };
|
|
526
|
+
}
|
|
527
|
+
// === Debug Helpers ===
|
|
528
|
+
/**
|
|
529
|
+
* Log the contents of all stores.
|
|
530
|
+
* Logs each item separately for full visibility (avoids [Object] truncation).
|
|
531
|
+
*/
|
|
532
|
+
async logStoreContents() {
|
|
533
|
+
const transfers = await this.transferStore.getAll();
|
|
534
|
+
const intents = await this.rebalanceIntentStore.getAll();
|
|
535
|
+
const actions = await this.rebalanceActionStore.getAll();
|
|
536
|
+
const activeIntents = intents.filter((i) => ['not_started', 'in_progress'].includes(i.status));
|
|
537
|
+
const inProgressTransfers = transfers.filter((t) => t.status === 'in_progress');
|
|
538
|
+
const inProgressActions = actions.filter((a) => a.status === 'in_progress');
|
|
539
|
+
// Log summary
|
|
540
|
+
this.logger.info({
|
|
541
|
+
transfers: inProgressTransfers.length,
|
|
542
|
+
intents: activeIntents.length,
|
|
543
|
+
actions: inProgressActions.length,
|
|
544
|
+
}, 'Store summary');
|
|
545
|
+
// Log each transfer separately
|
|
546
|
+
for (const t of inProgressTransfers) {
|
|
547
|
+
this.logger.info({
|
|
548
|
+
type: 'transfer',
|
|
549
|
+
origin: t.origin,
|
|
550
|
+
destination: t.destination,
|
|
551
|
+
amount: t.amount.toString(),
|
|
552
|
+
messageId: t.messageId,
|
|
553
|
+
}, 'In-progress transfer');
|
|
554
|
+
}
|
|
555
|
+
// Log each intent separately
|
|
556
|
+
for (const i of activeIntents) {
|
|
557
|
+
this.logger.info({
|
|
558
|
+
type: 'intent',
|
|
559
|
+
id: i.id,
|
|
560
|
+
origin: i.origin,
|
|
561
|
+
destination: i.destination,
|
|
562
|
+
amount: i.amount.toString(),
|
|
563
|
+
status: i.status,
|
|
564
|
+
bridge: i.bridge,
|
|
565
|
+
}, 'Active intent');
|
|
566
|
+
}
|
|
567
|
+
// Log each action separately
|
|
568
|
+
for (const a of inProgressActions) {
|
|
569
|
+
this.logger.info({
|
|
570
|
+
type: 'action',
|
|
571
|
+
id: a.id,
|
|
572
|
+
origin: a.origin,
|
|
573
|
+
destination: a.destination,
|
|
574
|
+
amount: a.amount.toString(),
|
|
575
|
+
messageId: a.messageId,
|
|
576
|
+
intentId: a.intentId,
|
|
577
|
+
}, 'In-progress action');
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
// === Private Helpers ===
|
|
581
|
+
/**
|
|
582
|
+
* Get the confirmed block tag for delivery checks.
|
|
583
|
+
* Uses cached value from Monitor event if available, otherwise computes on-demand.
|
|
584
|
+
*/
|
|
585
|
+
async getConfirmedBlockTag(destination, confirmedBlockTags) {
|
|
586
|
+
const chainName = this.core.multiProvider.getChainName(destination);
|
|
587
|
+
// If tags provided (from Monitor event), use cached value
|
|
588
|
+
if (confirmedBlockTags) {
|
|
589
|
+
return confirmedBlockTags[chainName];
|
|
590
|
+
}
|
|
591
|
+
// Otherwise compute on-demand (e.g., during initialize())
|
|
592
|
+
return getConfirmedBlockTag(this.core.multiProvider, chainName, this.logger);
|
|
593
|
+
}
|
|
594
|
+
async isMessageDelivered(messageId, destination, blockTag) {
|
|
595
|
+
try {
|
|
596
|
+
const chainName = this.core.multiProvider.getChainName(destination);
|
|
597
|
+
const delivered = await this.core
|
|
598
|
+
.adapter(chainName)
|
|
599
|
+
.isDelivered(messageId, blockTag);
|
|
600
|
+
this.logger.debug({ messageId, destination: chainName, delivered, blockTag }, 'Checked message delivery');
|
|
601
|
+
return delivered;
|
|
602
|
+
}
|
|
603
|
+
catch (error) {
|
|
604
|
+
this.logger.warn({ messageId, destination, error }, 'Failed to check message delivery status');
|
|
605
|
+
return false;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
async recoverAction(msg) {
|
|
609
|
+
// Check if action already exists
|
|
610
|
+
const existing = await this.rebalanceActionStore.get(msg.msg_id);
|
|
611
|
+
if (existing) {
|
|
612
|
+
this.logger.debug({ id: msg.msg_id }, 'Action already exists, skipping');
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
this.logger.debug({
|
|
616
|
+
msgId: msg.msg_id,
|
|
617
|
+
origin: msg.origin_domain_id,
|
|
618
|
+
destination: msg.destination_domain_id,
|
|
619
|
+
sender: msg.sender,
|
|
620
|
+
recipient: msg.recipient,
|
|
621
|
+
txHash: msg.origin_tx_hash,
|
|
622
|
+
messageBodyLength: msg.message_body?.length,
|
|
623
|
+
messageBodyPreview: msg.message_body?.substring(0, 66),
|
|
624
|
+
}, 'Recovering rebalance action');
|
|
625
|
+
try {
|
|
626
|
+
// Create synthetic intent
|
|
627
|
+
const { amount } = parseWarpRouteMessage(msg.message_body);
|
|
628
|
+
// Hasura returns block timestamps as UTC without 'Z' suffix (e.g. "2024-01-15T12:30:45").
|
|
629
|
+
// Null when the scraper hasn't indexed the origin block yet, so fall back to now.
|
|
630
|
+
// Note: when null, TTL effectively extends by scraper lag since recoverAction won't update createdAt later.
|
|
631
|
+
const createdAt = msg.send_occurred_at
|
|
632
|
+
? new Date(msg.send_occurred_at + 'Z').getTime()
|
|
633
|
+
: Date.now();
|
|
634
|
+
assert(!isNaN(createdAt), `Invalid send_occurred_at timestamp: ${msg.send_occurred_at}`);
|
|
635
|
+
const intent = {
|
|
636
|
+
id: uuidv4(),
|
|
637
|
+
status: 'in_progress',
|
|
638
|
+
origin: msg.origin_domain_id,
|
|
639
|
+
destination: msg.destination_domain_id,
|
|
640
|
+
amount,
|
|
641
|
+
priority: undefined,
|
|
642
|
+
strategyType: undefined,
|
|
643
|
+
createdAt,
|
|
644
|
+
updatedAt: Date.now(),
|
|
645
|
+
};
|
|
646
|
+
await this.rebalanceIntentStore.save(intent);
|
|
647
|
+
this.logger.debug({ id: intent.id, amount: amount.toString(), createdAt }, 'Created synthetic RebalanceIntent');
|
|
648
|
+
// Create action (recovered actions are always rebalance_message type)
|
|
649
|
+
const action = {
|
|
650
|
+
id: msg.msg_id,
|
|
651
|
+
status: 'in_progress',
|
|
652
|
+
type: 'rebalance_message',
|
|
653
|
+
intentId: intent.id,
|
|
654
|
+
messageId: msg.msg_id,
|
|
655
|
+
txHash: msg.origin_tx_hash,
|
|
656
|
+
origin: msg.origin_domain_id,
|
|
657
|
+
destination: msg.destination_domain_id,
|
|
658
|
+
amount,
|
|
659
|
+
createdAt,
|
|
660
|
+
updatedAt: Date.now(),
|
|
661
|
+
};
|
|
662
|
+
await this.rebalanceActionStore.save(action);
|
|
663
|
+
this.logger.debug({ id: action.id, intentId: action.intentId, amount: amount.toString() }, 'Recovered RebalanceAction');
|
|
664
|
+
}
|
|
665
|
+
catch (error) {
|
|
666
|
+
this.logger.warn({
|
|
667
|
+
msgId: msg.msg_id,
|
|
668
|
+
messageBody: msg.message_body,
|
|
669
|
+
messageBodyLength: msg.message_body?.length,
|
|
670
|
+
origin: msg.origin_domain_id,
|
|
671
|
+
destination: msg.destination_domain_id,
|
|
672
|
+
txHash: msg.origin_tx_hash,
|
|
673
|
+
error: error instanceof Error ? error.message : String(error),
|
|
674
|
+
}, 'Failed to parse message body during recovery, skipping action');
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
//# sourceMappingURL=ActionTracker.js.map
|