@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,439 @@
|
|
|
1
|
+
import type { Logger } from 'pino';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
addressToByteHexString,
|
|
5
|
+
isEVMLike,
|
|
6
|
+
ProtocolType,
|
|
7
|
+
} from '@humanode-hyperlane/utils';
|
|
8
|
+
|
|
9
|
+
export type InflightRebalanceQueryParams = {
|
|
10
|
+
bridges: string[];
|
|
11
|
+
routersByDomain: Record<number, string>; // Domain ID → router address (derive routers and domains from this)
|
|
12
|
+
txSender: string;
|
|
13
|
+
limit?: number;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type UserTransferQueryParams = {
|
|
17
|
+
routersByDomain: Record<number, string>; // Domain ID → router address (derive routers and domains from this)
|
|
18
|
+
excludeTxSenders: string[]; // Addresses to exclude (rebalancer + inventory signer)
|
|
19
|
+
limit?: number;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type RebalanceActionQueryParams = {
|
|
23
|
+
bridges: string[]; // Bridge contract addresses
|
|
24
|
+
routersByDomain: Record<number, string>; // Domain ID → router address (derive routers and domains from this)
|
|
25
|
+
rebalancerAddress: string; // Include rebalancer's txs
|
|
26
|
+
inventorySignerAddresses?: string[]; // Optional: also include inventory signers' txs (for inventory_deposit actions)
|
|
27
|
+
limit?: number;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export type ExplorerMessage = {
|
|
31
|
+
msg_id: string;
|
|
32
|
+
origin_domain_id: number;
|
|
33
|
+
destination_domain_id: number;
|
|
34
|
+
sender: string;
|
|
35
|
+
recipient: string;
|
|
36
|
+
origin_tx_hash: string;
|
|
37
|
+
origin_tx_sender: string;
|
|
38
|
+
origin_tx_recipient: string;
|
|
39
|
+
is_delivered: boolean;
|
|
40
|
+
message_body: string;
|
|
41
|
+
send_occurred_at: string | null;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export interface IExplorerClient {
|
|
45
|
+
getInflightUserTransfers(
|
|
46
|
+
params: UserTransferQueryParams,
|
|
47
|
+
logger: Logger,
|
|
48
|
+
): Promise<ExplorerMessage[]>;
|
|
49
|
+
getInflightRebalanceActions(
|
|
50
|
+
params: RebalanceActionQueryParams,
|
|
51
|
+
logger: Logger,
|
|
52
|
+
): Promise<ExplorerMessage[]>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export class ExplorerClient implements IExplorerClient {
|
|
56
|
+
constructor(
|
|
57
|
+
private readonly baseUrl: string,
|
|
58
|
+
private readonly getProtocol: (domainId: number) => ProtocolType,
|
|
59
|
+
) {}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Convert an address to PostgreSQL bytea format (\\x-prefixed hex).
|
|
63
|
+
* When `domain` is provided, resolves the chain's protocol to encode
|
|
64
|
+
* non-EVM addresses (e.g. base58 Solana, 32-byte Starknet) correctly
|
|
65
|
+
* via `addressToByteHexString`. Without `domain`, assumes the address
|
|
66
|
+
* is already EVM-format hex (used for bridges, txSender, etc. which
|
|
67
|
+
* are always EVM addresses).
|
|
68
|
+
*/
|
|
69
|
+
private toBytea(addr: string, domain?: number): string {
|
|
70
|
+
const protocol =
|
|
71
|
+
domain !== undefined ? this.getProtocol(domain) : undefined;
|
|
72
|
+
if (protocol && !isEVMLike(protocol)) {
|
|
73
|
+
const hex = addressToByteHexString(addr, protocol);
|
|
74
|
+
return hex.replace(/^0x/i, '\\x').toLowerCase();
|
|
75
|
+
}
|
|
76
|
+
return addr.replace(/^0x/i, '\\x').toLowerCase();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Normalize all hex fields in Explorer response from PostgreSQL bytea format (\\x) to standard hex (0x)
|
|
81
|
+
*/
|
|
82
|
+
private normalizeExplorerMessage(msg: any): ExplorerMessage {
|
|
83
|
+
const normalizeHex = (hex: string): string => {
|
|
84
|
+
if (!hex) return hex;
|
|
85
|
+
return hex.startsWith('\\x') ? '0x' + hex.slice(2) : hex;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
msg_id: normalizeHex(msg.msg_id),
|
|
90
|
+
origin_domain_id: msg.origin_domain_id,
|
|
91
|
+
destination_domain_id: msg.destination_domain_id,
|
|
92
|
+
sender: normalizeHex(msg.sender),
|
|
93
|
+
recipient: normalizeHex(msg.recipient),
|
|
94
|
+
origin_tx_hash: normalizeHex(msg.origin_tx_hash),
|
|
95
|
+
origin_tx_sender: normalizeHex(msg.origin_tx_sender),
|
|
96
|
+
origin_tx_recipient: normalizeHex(msg.origin_tx_recipient),
|
|
97
|
+
is_delivered: msg.is_delivered,
|
|
98
|
+
message_body: normalizeHex(msg.message_body),
|
|
99
|
+
send_occurred_at: msg.send_occurred_at ?? null,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async hasUndeliveredRebalance(
|
|
104
|
+
params: InflightRebalanceQueryParams,
|
|
105
|
+
logger: Logger,
|
|
106
|
+
): Promise<boolean> {
|
|
107
|
+
const { bridges, routersByDomain, txSender, limit = 5 } = params;
|
|
108
|
+
|
|
109
|
+
// Derive routers and domains from routersByDomain
|
|
110
|
+
const routerEntries = Object.entries(routersByDomain);
|
|
111
|
+
const domains = Object.keys(routersByDomain).map(Number);
|
|
112
|
+
|
|
113
|
+
const variables = {
|
|
114
|
+
// NOTE: bridges are always EVM addresses; pass domain here if non-EVM bridges are added
|
|
115
|
+
senders: bridges.map((a) => this.toBytea(a)),
|
|
116
|
+
// NOTE: bridges are always EVM addresses; pass domain here if non-EVM bridges are added
|
|
117
|
+
recipients: bridges.map((a) => this.toBytea(a)),
|
|
118
|
+
originTxRecipients: routerEntries.map(([domain, addr]) =>
|
|
119
|
+
this.toBytea(addr, Number(domain)),
|
|
120
|
+
),
|
|
121
|
+
originDomains: domains,
|
|
122
|
+
destDomains: domains,
|
|
123
|
+
// NOTE: txSender is always an EVM address (rebalancer signer)
|
|
124
|
+
txSenders: [this.toBytea(txSender)],
|
|
125
|
+
limit,
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
logger.debug({ variables }, 'Explorer query variables');
|
|
129
|
+
|
|
130
|
+
const query = `
|
|
131
|
+
query InflightRebalancesForRoute(
|
|
132
|
+
$senders: [bytea!],
|
|
133
|
+
$recipients: [bytea!],
|
|
134
|
+
$originTxRecipients: [bytea!],
|
|
135
|
+
$originDomains: [Int!],
|
|
136
|
+
$destDomains: [Int!],
|
|
137
|
+
$txSenders: [bytea!],
|
|
138
|
+
$limit: Int = 25
|
|
139
|
+
) {
|
|
140
|
+
message_view(
|
|
141
|
+
where: {
|
|
142
|
+
_and: [
|
|
143
|
+
{ is_delivered: { _eq: false } },
|
|
144
|
+
{ sender: { _in: $senders } },
|
|
145
|
+
{ recipient: { _in: $recipients } },
|
|
146
|
+
{ origin_tx_recipient: { _in: $originTxRecipients } },
|
|
147
|
+
{ origin_domain_id: { _in: $originDomains } },
|
|
148
|
+
{ destination_domain_id: { _in: $destDomains } },
|
|
149
|
+
{ origin_tx_sender: { _in: $txSenders } }
|
|
150
|
+
]
|
|
151
|
+
}
|
|
152
|
+
order_by: { origin_tx_id: desc }
|
|
153
|
+
limit: $limit
|
|
154
|
+
) {
|
|
155
|
+
msg_id
|
|
156
|
+
origin_domain_id
|
|
157
|
+
destination_domain_id
|
|
158
|
+
sender
|
|
159
|
+
recipient
|
|
160
|
+
origin_tx_hash
|
|
161
|
+
origin_tx_sender
|
|
162
|
+
origin_tx_recipient
|
|
163
|
+
is_delivered
|
|
164
|
+
message_body
|
|
165
|
+
}
|
|
166
|
+
}`;
|
|
167
|
+
|
|
168
|
+
const res = await fetch(this.baseUrl, {
|
|
169
|
+
method: 'POST',
|
|
170
|
+
headers: { 'Content-Type': 'application/json' },
|
|
171
|
+
body: JSON.stringify({ query, variables }),
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
logger.debug({ status: res.status }, 'Explorer query response');
|
|
175
|
+
|
|
176
|
+
if (!res.ok) {
|
|
177
|
+
let errorDetails: string;
|
|
178
|
+
try {
|
|
179
|
+
const errorJson = await res.json();
|
|
180
|
+
errorDetails = JSON.stringify(errorJson);
|
|
181
|
+
} catch (_e) {
|
|
182
|
+
try {
|
|
183
|
+
// Fallback to text if JSON parsing fails
|
|
184
|
+
errorDetails = await res.text();
|
|
185
|
+
} catch (_textError) {
|
|
186
|
+
errorDetails = 'Unable to read response body';
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
throw new Error(`Explorer query failed: ${res.status} ${errorDetails}`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const json = await res.json();
|
|
193
|
+
const rows = json?.data?.message_view ?? [];
|
|
194
|
+
|
|
195
|
+
logger.debug({ rows }, 'Explorer query rows');
|
|
196
|
+
|
|
197
|
+
// Post-query validation: verify each message's origin domain matches the expected router
|
|
198
|
+
const validatedRows = rows.filter((msg: any) => {
|
|
199
|
+
const expectedRouter = routersByDomain[msg.origin_domain_id];
|
|
200
|
+
if (!expectedRouter) return false;
|
|
201
|
+
const protocol = this.getProtocol(msg.origin_domain_id);
|
|
202
|
+
const normalizedMsgRouter = msg.origin_tx_recipient?.startsWith('\\x')
|
|
203
|
+
? '0x' + msg.origin_tx_recipient.slice(2)
|
|
204
|
+
: msg.origin_tx_recipient;
|
|
205
|
+
if (protocol && !isEVMLike(protocol)) {
|
|
206
|
+
const expectedHex = addressToByteHexString(
|
|
207
|
+
expectedRouter,
|
|
208
|
+
protocol,
|
|
209
|
+
).toLowerCase();
|
|
210
|
+
return normalizedMsgRouter?.toLowerCase() === expectedHex;
|
|
211
|
+
}
|
|
212
|
+
return (
|
|
213
|
+
normalizedMsgRouter?.toLowerCase() === expectedRouter.toLowerCase()
|
|
214
|
+
);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
logger.debug(
|
|
218
|
+
{ totalRows: rows.length, validatedRows: validatedRows.length },
|
|
219
|
+
'Post-query validation results',
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
return validatedRows.length > 0;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Query inflight user transfers from the Explorer.
|
|
227
|
+
* Returns transfers where sender/recipient are routers, excluding rebalancer's own transactions.
|
|
228
|
+
*/
|
|
229
|
+
async getInflightUserTransfers(
|
|
230
|
+
params: UserTransferQueryParams,
|
|
231
|
+
logger: Logger,
|
|
232
|
+
): Promise<ExplorerMessage[]> {
|
|
233
|
+
const { routersByDomain, excludeTxSenders, limit = 100 } = params;
|
|
234
|
+
|
|
235
|
+
// Derive routers and domains from routersByDomain
|
|
236
|
+
const routerEntries = Object.entries(routersByDomain);
|
|
237
|
+
const domains = Object.keys(routersByDomain).map(Number);
|
|
238
|
+
|
|
239
|
+
const variables = {
|
|
240
|
+
senders: routerEntries.map(([domain, addr]) =>
|
|
241
|
+
this.toBytea(addr, Number(domain)),
|
|
242
|
+
),
|
|
243
|
+
recipients: routerEntries.map(([domain, addr]) =>
|
|
244
|
+
this.toBytea(addr, Number(domain)),
|
|
245
|
+
),
|
|
246
|
+
originDomains: domains,
|
|
247
|
+
destDomains: domains,
|
|
248
|
+
// NOTE: excludeTxSenders are always EVM addresses
|
|
249
|
+
excludeTxSenders: excludeTxSenders.map((a) => this.toBytea(a)),
|
|
250
|
+
limit,
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
logger.debug({ variables }, 'Explorer getInflightUserTransfers query');
|
|
254
|
+
|
|
255
|
+
const query = `
|
|
256
|
+
query InflightUserTransfers(
|
|
257
|
+
$senders: [bytea!],
|
|
258
|
+
$recipients: [bytea!],
|
|
259
|
+
$originDomains: [Int!],
|
|
260
|
+
$destDomains: [Int!],
|
|
261
|
+
$excludeTxSenders: [bytea!],
|
|
262
|
+
$limit: Int = 100
|
|
263
|
+
) {
|
|
264
|
+
message_view(
|
|
265
|
+
where: {
|
|
266
|
+
_and: [
|
|
267
|
+
{ is_delivered: { _eq: false } },
|
|
268
|
+
{ sender: { _in: $senders } },
|
|
269
|
+
{ recipient: { _in: $recipients } },
|
|
270
|
+
{ origin_domain_id: { _in: $originDomains } },
|
|
271
|
+
{ destination_domain_id: { _in: $destDomains } },
|
|
272
|
+
{ origin_tx_sender: { _nin: $excludeTxSenders } }
|
|
273
|
+
]
|
|
274
|
+
}
|
|
275
|
+
order_by: { origin_tx_id: desc }
|
|
276
|
+
limit: $limit
|
|
277
|
+
) {
|
|
278
|
+
msg_id
|
|
279
|
+
origin_domain_id
|
|
280
|
+
destination_domain_id
|
|
281
|
+
sender
|
|
282
|
+
recipient
|
|
283
|
+
origin_tx_hash
|
|
284
|
+
origin_tx_sender
|
|
285
|
+
origin_tx_recipient
|
|
286
|
+
is_delivered
|
|
287
|
+
message_body
|
|
288
|
+
send_occurred_at
|
|
289
|
+
}
|
|
290
|
+
}`;
|
|
291
|
+
|
|
292
|
+
const res = await fetch(this.baseUrl, {
|
|
293
|
+
method: 'POST',
|
|
294
|
+
headers: { 'Content-Type': 'application/json' },
|
|
295
|
+
body: JSON.stringify({ query, variables }),
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
logger.debug(
|
|
299
|
+
{ status: res.status },
|
|
300
|
+
'Explorer getInflightUserTransfers response',
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
if (!res.ok) {
|
|
304
|
+
let errorDetails: string;
|
|
305
|
+
try {
|
|
306
|
+
const errorJson = await res.json();
|
|
307
|
+
errorDetails = JSON.stringify(errorJson);
|
|
308
|
+
} catch (_e) {
|
|
309
|
+
try {
|
|
310
|
+
errorDetails = await res.text();
|
|
311
|
+
} catch (_textError) {
|
|
312
|
+
errorDetails = 'Unable to read response body';
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
throw new Error(`Explorer query failed: ${res.status} ${errorDetails}`);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const json = await res.json();
|
|
319
|
+
const messages = json?.data?.message_view ?? [];
|
|
320
|
+
return messages.map((msg: any) => this.normalizeExplorerMessage(msg));
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Query inflight rebalance actions from the Explorer.
|
|
325
|
+
* Returns messages where sender/recipient are bridges, tx sender is the rebalancer,
|
|
326
|
+
* and origin_tx_recipient is one of this warp route's routers.
|
|
327
|
+
*/
|
|
328
|
+
async getInflightRebalanceActions(
|
|
329
|
+
params: RebalanceActionQueryParams,
|
|
330
|
+
logger: Logger,
|
|
331
|
+
): Promise<ExplorerMessage[]> {
|
|
332
|
+
const {
|
|
333
|
+
bridges,
|
|
334
|
+
routersByDomain,
|
|
335
|
+
rebalancerAddress,
|
|
336
|
+
inventorySignerAddresses,
|
|
337
|
+
limit = 100,
|
|
338
|
+
} = params;
|
|
339
|
+
|
|
340
|
+
// Derive routers and domains from routersByDomain
|
|
341
|
+
const routerEntries = Object.entries(routersByDomain);
|
|
342
|
+
const domains = Object.keys(routersByDomain).map(Number);
|
|
343
|
+
|
|
344
|
+
// Build list of tx senders to include (rebalancer + optional inventory signer)
|
|
345
|
+
// NOTE: rebalancerAddress is always an EVM address (rebalancer signer)
|
|
346
|
+
const txSenders = [this.toBytea(rebalancerAddress)];
|
|
347
|
+
if (inventorySignerAddresses) {
|
|
348
|
+
for (const addr of inventorySignerAddresses) {
|
|
349
|
+
// NOTE: inventorySignerAddresses are filtered to ProtocolType.Ethereum
|
|
350
|
+
txSenders.push(this.toBytea(addr));
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const variables = {
|
|
355
|
+
// NOTE: bridges are always EVM addresses; pass domain here if non-EVM bridges are added
|
|
356
|
+
senders: bridges.map((a) => this.toBytea(a)),
|
|
357
|
+
// NOTE: bridges are always EVM addresses; pass domain here if non-EVM bridges are added
|
|
358
|
+
recipients: bridges.map((a) => this.toBytea(a)),
|
|
359
|
+
originTxRecipients: routerEntries.map(([domain, addr]) =>
|
|
360
|
+
this.toBytea(addr, Number(domain)),
|
|
361
|
+
),
|
|
362
|
+
originDomains: domains,
|
|
363
|
+
destDomains: domains,
|
|
364
|
+
txSenders,
|
|
365
|
+
limit,
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
logger.debug({ variables }, 'Explorer getInflightRebalanceActions query');
|
|
369
|
+
|
|
370
|
+
const query = `
|
|
371
|
+
query InflightRebalanceActions(
|
|
372
|
+
$senders: [bytea!],
|
|
373
|
+
$recipients: [bytea!],
|
|
374
|
+
$originTxRecipients: [bytea!],
|
|
375
|
+
$originDomains: [Int!],
|
|
376
|
+
$destDomains: [Int!],
|
|
377
|
+
$txSenders: [bytea!],
|
|
378
|
+
$limit: Int = 100
|
|
379
|
+
) {
|
|
380
|
+
message_view(
|
|
381
|
+
where: {
|
|
382
|
+
_and: [
|
|
383
|
+
{ is_delivered: { _eq: false } },
|
|
384
|
+
{ sender: { _in: $senders } },
|
|
385
|
+
{ recipient: { _in: $recipients } },
|
|
386
|
+
{ origin_tx_recipient: { _in: $originTxRecipients } },
|
|
387
|
+
{ origin_domain_id: { _in: $originDomains } },
|
|
388
|
+
{ destination_domain_id: { _in: $destDomains } },
|
|
389
|
+
{ origin_tx_sender: { _in: $txSenders } }
|
|
390
|
+
]
|
|
391
|
+
}
|
|
392
|
+
order_by: { origin_tx_id: desc }
|
|
393
|
+
limit: $limit
|
|
394
|
+
) {
|
|
395
|
+
msg_id
|
|
396
|
+
origin_domain_id
|
|
397
|
+
destination_domain_id
|
|
398
|
+
sender
|
|
399
|
+
recipient
|
|
400
|
+
origin_tx_hash
|
|
401
|
+
origin_tx_sender
|
|
402
|
+
origin_tx_recipient
|
|
403
|
+
is_delivered
|
|
404
|
+
message_body
|
|
405
|
+
send_occurred_at
|
|
406
|
+
}
|
|
407
|
+
}`;
|
|
408
|
+
|
|
409
|
+
const res = await fetch(this.baseUrl, {
|
|
410
|
+
method: 'POST',
|
|
411
|
+
headers: { 'Content-Type': 'application/json' },
|
|
412
|
+
body: JSON.stringify({ query, variables }),
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
logger.debug(
|
|
416
|
+
{ status: res.status },
|
|
417
|
+
'Explorer getInflightRebalanceActions response',
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
if (!res.ok) {
|
|
421
|
+
let errorDetails: string;
|
|
422
|
+
try {
|
|
423
|
+
const errorJson = await res.json();
|
|
424
|
+
errorDetails = JSON.stringify(errorJson);
|
|
425
|
+
} catch (_e) {
|
|
426
|
+
try {
|
|
427
|
+
errorDetails = await res.text();
|
|
428
|
+
} catch (_textError) {
|
|
429
|
+
errorDetails = 'Unable to read response body';
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
throw new Error(`Explorer query failed: ${res.status} ${errorDetails}`);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const json = await res.json();
|
|
436
|
+
const messages = json?.data?.message_view ?? [];
|
|
437
|
+
return messages.map((msg: any) => this.normalizeExplorerMessage(msg));
|
|
438
|
+
}
|
|
439
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { expect } from 'chai';
|
|
2
|
+
import { pino } from 'pino';
|
|
3
|
+
import sinon from 'sinon';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
type ChainName,
|
|
7
|
+
type Token,
|
|
8
|
+
TokenStandard,
|
|
9
|
+
} from '@humanode-hyperlane/sdk';
|
|
10
|
+
|
|
11
|
+
import { type MonitorEvent } from '../interfaces/IMonitor.js';
|
|
12
|
+
|
|
13
|
+
import { getRawBalances } from './balanceUtils.js';
|
|
14
|
+
|
|
15
|
+
const testLogger = pino({ level: 'silent' });
|
|
16
|
+
|
|
17
|
+
describe('getRawBalances', () => {
|
|
18
|
+
let chains: ChainName[];
|
|
19
|
+
let token: Token;
|
|
20
|
+
let tokenBridgedSupply: bigint;
|
|
21
|
+
let event: MonitorEvent;
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
chains = ['mainnet'];
|
|
25
|
+
|
|
26
|
+
token = {
|
|
27
|
+
chainName: 'mainnet',
|
|
28
|
+
addressOrDenom: '0xAddress',
|
|
29
|
+
isCollateralized: sinon.stub().returns(true),
|
|
30
|
+
standard: TokenStandard.EvmHypCollateral,
|
|
31
|
+
} as unknown as Token;
|
|
32
|
+
|
|
33
|
+
tokenBridgedSupply = 100n;
|
|
34
|
+
|
|
35
|
+
event = {
|
|
36
|
+
tokensInfo: [
|
|
37
|
+
{
|
|
38
|
+
token,
|
|
39
|
+
bridgedSupply: tokenBridgedSupply,
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
confirmedBlockTags: { mainnet: 1000 },
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should return the bridged supply for the token (EvmHypCollateral)', () => {
|
|
47
|
+
expect(getRawBalances(chains, event, testLogger)).to.deep.equal({
|
|
48
|
+
mainnet: tokenBridgedSupply,
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should return the bridged supply for the token (EvmHypNative)', () => {
|
|
53
|
+
token.standard = TokenStandard.EvmHypNative;
|
|
54
|
+
|
|
55
|
+
expect(getRawBalances(chains, event, testLogger)).to.deep.equal({
|
|
56
|
+
mainnet: tokenBridgedSupply,
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should ignore non supported token standards', () => {
|
|
61
|
+
token.standard = TokenStandard.EvmHypOwnerCollateral;
|
|
62
|
+
|
|
63
|
+
expect(getRawBalances(chains, event, testLogger)).to.deep.equal({});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should ignore tokens that are not in the chains list', () => {
|
|
67
|
+
chains = [];
|
|
68
|
+
|
|
69
|
+
expect(getRawBalances(chains, event, testLogger)).to.deep.equal({});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should throw if the bridged supply is undefined', () => {
|
|
73
|
+
delete event.tokensInfo[0].bridgedSupply;
|
|
74
|
+
|
|
75
|
+
expect(() => getRawBalances(chains, event, testLogger)).to.throw(
|
|
76
|
+
'bridgedSupply should not be undefined for collateralized token 0xAddress',
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { type Logger } from 'pino';
|
|
2
|
+
|
|
3
|
+
import { type ChainName } from '@humanode-hyperlane/sdk';
|
|
4
|
+
|
|
5
|
+
import { type MonitorEvent } from '../interfaces/IMonitor.js';
|
|
6
|
+
import { type RawBalances } from '../interfaces/IStrategy.js';
|
|
7
|
+
|
|
8
|
+
import { isCollateralizedTokenEligibleForRebalancing } from './tokenUtils.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Returns the raw balances required by the strategies from the monitor event
|
|
12
|
+
* @param chains - The chains that should be included in the raw balances (e.g. the chains in the rebalancer config)
|
|
13
|
+
* @param event - The monitor event to extract the raw balances from
|
|
14
|
+
* @returns An object mapping chain names to their raw balances.
|
|
15
|
+
*/
|
|
16
|
+
export function getRawBalances(
|
|
17
|
+
chains: ChainName[],
|
|
18
|
+
event: MonitorEvent,
|
|
19
|
+
logger: Logger,
|
|
20
|
+
): RawBalances {
|
|
21
|
+
const balances: RawBalances = {};
|
|
22
|
+
const chainSet = new Set(chains);
|
|
23
|
+
|
|
24
|
+
for (const tokenInfo of event.tokensInfo) {
|
|
25
|
+
const { token, bridgedSupply } = tokenInfo;
|
|
26
|
+
|
|
27
|
+
// Ignore tokens that are not in the provided chains list
|
|
28
|
+
if (!chainSet.has(token.chainName)) {
|
|
29
|
+
logger.debug(
|
|
30
|
+
{
|
|
31
|
+
context: getRawBalances.name,
|
|
32
|
+
chain: token.chainName,
|
|
33
|
+
tokenSymbol: token.symbol,
|
|
34
|
+
tokenAddress: token.addressOrDenom,
|
|
35
|
+
},
|
|
36
|
+
'Skipping token: not in configured chains list',
|
|
37
|
+
);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Ignore tokens that are not collateralized or are otherwise ineligible
|
|
42
|
+
if (!isCollateralizedTokenEligibleForRebalancing(token)) {
|
|
43
|
+
logger.debug(
|
|
44
|
+
{
|
|
45
|
+
context: getRawBalances.name,
|
|
46
|
+
chain: token.chainName,
|
|
47
|
+
tokenSymbol: token.symbol,
|
|
48
|
+
tokenAddress: token.addressOrDenom,
|
|
49
|
+
},
|
|
50
|
+
'Skipping token: not collateralized or ineligible for rebalancing',
|
|
51
|
+
);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (bridgedSupply === undefined) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`bridgedSupply should not be undefined for collateralized token ${token.addressOrDenom}`,
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
balances[token.chainName] = bridgedSupply;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return balances;
|
|
65
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { Logger } from 'pino';
|
|
2
|
+
|
|
3
|
+
import { providers } from 'ethers';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
EthJsonRpcBlockParameterTag,
|
|
7
|
+
MultiProtocolProvider,
|
|
8
|
+
} from '@humanode-hyperlane/sdk';
|
|
9
|
+
import { ProtocolType } from '@humanode-hyperlane/utils';
|
|
10
|
+
import type { ConfirmedBlockTag } from '../interfaces/IMonitor.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Get the confirmed block tag for a chain, accounting for reorg period.
|
|
14
|
+
* Returns a block number that is safe from reorgs, or a named tag like 'finalized'.
|
|
15
|
+
*
|
|
16
|
+
* @param multiProvider - MultiProtocolProvider instance
|
|
17
|
+
* @param chainName - Name of the chain
|
|
18
|
+
* @param logger - Optional logger for warnings
|
|
19
|
+
* @returns Confirmed block tag (number, named tag, or undefined on error)
|
|
20
|
+
*/
|
|
21
|
+
export async function getConfirmedBlockTag(
|
|
22
|
+
multiProvider: MultiProtocolProvider,
|
|
23
|
+
chainName: string,
|
|
24
|
+
logger?: Logger,
|
|
25
|
+
): Promise<ConfirmedBlockTag> {
|
|
26
|
+
try {
|
|
27
|
+
const metadata = multiProvider.getChainMetadata(chainName);
|
|
28
|
+
|
|
29
|
+
// Only EVM chains support block tag queries
|
|
30
|
+
if (metadata.protocol !== ProtocolType.Ethereum) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const reorgPeriod = metadata.blocks?.reorgPeriod ?? 32;
|
|
35
|
+
if (typeof reorgPeriod === 'string') {
|
|
36
|
+
return reorgPeriod as EthJsonRpcBlockParameterTag;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const provider = multiProvider.getEthersV5Provider(chainName);
|
|
40
|
+
let latestBlock: number;
|
|
41
|
+
if (provider instanceof providers.JsonRpcProvider) {
|
|
42
|
+
const latestBlockHex = await provider.send('eth_blockNumber', []);
|
|
43
|
+
if (
|
|
44
|
+
typeof latestBlockHex !== 'string' ||
|
|
45
|
+
!/^0x[0-9a-fA-F]+$/.test(latestBlockHex)
|
|
46
|
+
) {
|
|
47
|
+
throw new Error(
|
|
48
|
+
`Invalid eth_blockNumber response for ${chainName}: ${latestBlockHex}`,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
latestBlock = parseInt(latestBlockHex, 16);
|
|
52
|
+
if (!Number.isFinite(latestBlock)) {
|
|
53
|
+
throw new Error(
|
|
54
|
+
`Parsed block number is not finite for ${chainName}: ${latestBlockHex}`,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
latestBlock = await provider.getBlockNumber();
|
|
59
|
+
}
|
|
60
|
+
return Math.max(0, latestBlock - reorgPeriod);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
logger?.warn(
|
|
63
|
+
{ chain: chainName, error: (error as Error).message },
|
|
64
|
+
'Failed to get confirmed block, using latest',
|
|
65
|
+
);
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
}
|