@humanode-hyperlane/rebalancer 0.0.0-stage → 27.2.2-humanode.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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,1008 @@
|
|
|
1
|
+
import { expect } from 'chai';
|
|
2
|
+
import { BigNumber, ethers, providers } from 'ethers';
|
|
3
|
+
|
|
4
|
+
import { ERC20__factory } from '@humanode-hyperlane/core';
|
|
5
|
+
import {
|
|
6
|
+
HyperlaneCore,
|
|
7
|
+
MultiProvider,
|
|
8
|
+
snapshot,
|
|
9
|
+
} from '@humanode-hyperlane/sdk';
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
RebalancerMinAmountType,
|
|
13
|
+
RebalancerStrategyOptions,
|
|
14
|
+
} from '../config/types.js';
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
ANVIL_TEST_PRIVATE_KEY,
|
|
18
|
+
ANVIL_USER_PRIVATE_KEY,
|
|
19
|
+
DOMAIN_IDS,
|
|
20
|
+
type DeployedAddresses,
|
|
21
|
+
TEST_CHAINS,
|
|
22
|
+
type TestChain,
|
|
23
|
+
} from './fixtures/routes.js';
|
|
24
|
+
import { type LocalDeploymentContext } from './harness/BaseLocalDeploymentManager.js';
|
|
25
|
+
import { Erc20LocalDeploymentManager } from './harness/Erc20LocalDeploymentManager.js';
|
|
26
|
+
import { resetSnapshotsAndRefreshProviders } from './harness/SnapshotHelper.js';
|
|
27
|
+
import { getFirstMonitorEvent } from './harness/TestHelpers.js';
|
|
28
|
+
import { TestRebalancer } from './harness/TestRebalancer.js';
|
|
29
|
+
import {
|
|
30
|
+
executeWarpTransfer,
|
|
31
|
+
tryRelayMessage,
|
|
32
|
+
} from './harness/TransferHelper.js';
|
|
33
|
+
|
|
34
|
+
describe('CompositeStrategy E2E', function () {
|
|
35
|
+
this.timeout(300_000);
|
|
36
|
+
|
|
37
|
+
let deploymentManager: Erc20LocalDeploymentManager;
|
|
38
|
+
let multiProvider: MultiProvider;
|
|
39
|
+
let localProviders: Map<string, providers.JsonRpcProvider>;
|
|
40
|
+
let userAddress: string;
|
|
41
|
+
let snapshotIds: Map<string, string>;
|
|
42
|
+
let hyperlaneCore: HyperlaneCore;
|
|
43
|
+
let deployedAddresses: DeployedAddresses;
|
|
44
|
+
|
|
45
|
+
before(async function () {
|
|
46
|
+
const wallet = new ethers.Wallet(ANVIL_USER_PRIVATE_KEY);
|
|
47
|
+
userAddress = wallet.address;
|
|
48
|
+
|
|
49
|
+
deploymentManager = new Erc20LocalDeploymentManager();
|
|
50
|
+
const ctx: LocalDeploymentContext<DeployedAddresses> =
|
|
51
|
+
await deploymentManager.start();
|
|
52
|
+
multiProvider = ctx.multiProvider;
|
|
53
|
+
localProviders = ctx.providers;
|
|
54
|
+
deployedAddresses = ctx.deployedAddresses;
|
|
55
|
+
|
|
56
|
+
const coreAddresses: Record<string, Record<string, string>> = {};
|
|
57
|
+
for (const chain of TEST_CHAINS) {
|
|
58
|
+
coreAddresses[chain] = {
|
|
59
|
+
mailbox: deployedAddresses.chains[chain].mailbox,
|
|
60
|
+
interchainSecurityModule: deployedAddresses.chains[chain].ism,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
hyperlaneCore = HyperlaneCore.fromAddressesMap(
|
|
64
|
+
coreAddresses,
|
|
65
|
+
multiProvider,
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
snapshotIds = new Map();
|
|
69
|
+
for (const [chain, provider] of localProviders) {
|
|
70
|
+
snapshotIds.set(chain, await snapshot(provider));
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
afterEach(async function () {
|
|
75
|
+
await resetSnapshotsAndRefreshProviders({
|
|
76
|
+
localProviders,
|
|
77
|
+
multiProvider,
|
|
78
|
+
snapshotIds,
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
after(async function () {
|
|
83
|
+
if (deploymentManager) {
|
|
84
|
+
await deploymentManager.stop();
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('collateralDeficit + weighted: routes use different bridges', async function () {
|
|
89
|
+
const transferAmount = BigNumber.from('600000000'); // 600 USDC
|
|
90
|
+
|
|
91
|
+
const context = await TestRebalancer.builder(
|
|
92
|
+
deploymentManager,
|
|
93
|
+
multiProvider,
|
|
94
|
+
)
|
|
95
|
+
.withStrategy([
|
|
96
|
+
{
|
|
97
|
+
rebalanceStrategy: RebalancerStrategyOptions.CollateralDeficit,
|
|
98
|
+
chains: {
|
|
99
|
+
anvil1: {
|
|
100
|
+
buffer: '0',
|
|
101
|
+
bridge: deployedAddresses.bridgeRoute1.anvil1,
|
|
102
|
+
},
|
|
103
|
+
anvil2: {
|
|
104
|
+
buffer: '0',
|
|
105
|
+
bridge: deployedAddresses.bridgeRoute1.anvil2,
|
|
106
|
+
},
|
|
107
|
+
anvil3: {
|
|
108
|
+
buffer: '0',
|
|
109
|
+
bridge: deployedAddresses.bridgeRoute1.anvil3,
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
rebalanceStrategy: RebalancerStrategyOptions.Weighted,
|
|
115
|
+
chains: {
|
|
116
|
+
anvil1: {
|
|
117
|
+
weighted: { weight: 60n, tolerance: 5n },
|
|
118
|
+
bridge: deployedAddresses.bridgeRoute2.anvil1,
|
|
119
|
+
},
|
|
120
|
+
anvil2: {
|
|
121
|
+
weighted: { weight: 20n, tolerance: 5n },
|
|
122
|
+
bridge: deployedAddresses.bridgeRoute2.anvil2,
|
|
123
|
+
},
|
|
124
|
+
anvil3: {
|
|
125
|
+
weighted: { weight: 20n, tolerance: 5n },
|
|
126
|
+
bridge: deployedAddresses.bridgeRoute2.anvil3,
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
])
|
|
131
|
+
.withBalances('COMPOSITE_DEFICIT_IMBALANCE')
|
|
132
|
+
.withExecutionMode('execute')
|
|
133
|
+
.build();
|
|
134
|
+
|
|
135
|
+
// Fund user and execute actual warp transfer
|
|
136
|
+
const ethProvider = localProviders.get('anvil1')!;
|
|
137
|
+
const deployer = new ethers.Wallet(ANVIL_TEST_PRIVATE_KEY, ethProvider);
|
|
138
|
+
const token = ERC20__factory.connect(
|
|
139
|
+
deployedAddresses.tokens.anvil1,
|
|
140
|
+
deployer,
|
|
141
|
+
);
|
|
142
|
+
const tx = await token.transfer(userAddress, transferAmount.mul(2));
|
|
143
|
+
await tx.wait();
|
|
144
|
+
|
|
145
|
+
const transferResult = await executeWarpTransfer(
|
|
146
|
+
context.multiProvider,
|
|
147
|
+
{
|
|
148
|
+
originChain: 'anvil1',
|
|
149
|
+
destinationChain: 'anvil2',
|
|
150
|
+
routerAddress: deployedAddresses.monitoredRoute.anvil1,
|
|
151
|
+
tokenAddress: deployedAddresses.tokens.anvil1,
|
|
152
|
+
amount: transferAmount,
|
|
153
|
+
recipient: userAddress,
|
|
154
|
+
senderAddress: userAddress,
|
|
155
|
+
},
|
|
156
|
+
ethProvider,
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
const blockTags = await context.getConfirmedBlockTags();
|
|
160
|
+
await context.tracker.syncTransfers(blockTags);
|
|
161
|
+
|
|
162
|
+
// Verify transfer was tracked
|
|
163
|
+
const transfersBeforeRebalance =
|
|
164
|
+
await context.tracker.getInProgressTransfers();
|
|
165
|
+
expect(transfersBeforeRebalance.length).to.equal(
|
|
166
|
+
1,
|
|
167
|
+
'Should have exactly 1 in-progress transfer',
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
const monitor = context.createMonitor(0);
|
|
171
|
+
const event = await getFirstMonitorEvent(monitor);
|
|
172
|
+
await context.orchestrator.executeCycle(event);
|
|
173
|
+
|
|
174
|
+
// Assert using ActionTracker: Both SUPERSEED (CollateralDeficit) and SUBTENSOR intent should exist
|
|
175
|
+
const activeIntents = await context.tracker.getActiveRebalanceIntents();
|
|
176
|
+
expect(
|
|
177
|
+
activeIntents.length,
|
|
178
|
+
'Should have active rebalance intents',
|
|
179
|
+
).to.be.equal(3, 'Should have exactly 3 active rebalance intents');
|
|
180
|
+
|
|
181
|
+
const inProgressActions = await context.tracker.getInProgressActions();
|
|
182
|
+
|
|
183
|
+
// Check for SUPERSEED actions (CollateralDeficit strategy routes to arbitrum)
|
|
184
|
+
const superseedActions = [];
|
|
185
|
+
for (const action of inProgressActions) {
|
|
186
|
+
const intent = activeIntents.find((i) => i.id === action.intentId);
|
|
187
|
+
if (intent?.bridge) {
|
|
188
|
+
const originChain = Object.entries(DOMAIN_IDS).find(
|
|
189
|
+
([, id]) => id === action.origin,
|
|
190
|
+
)?.[0] as TestChain | undefined;
|
|
191
|
+
if (
|
|
192
|
+
originChain &&
|
|
193
|
+
intent.bridge === deployedAddresses.bridgeRoute1[originChain]
|
|
194
|
+
) {
|
|
195
|
+
superseedActions.push(action);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
expect(
|
|
200
|
+
superseedActions.length,
|
|
201
|
+
'Should have SUPERSEED actions from CollateralDeficit',
|
|
202
|
+
).to.be.equal(1);
|
|
203
|
+
|
|
204
|
+
// Verify SUPERSEED route goes to arbitrum (has deficit from pending transfer)
|
|
205
|
+
const actionToArbitrum = superseedActions.find(
|
|
206
|
+
(a) => a.destination === DOMAIN_IDS.anvil2,
|
|
207
|
+
);
|
|
208
|
+
expect(actionToArbitrum, 'Should have SUPERSEED action to arbitrum').to
|
|
209
|
+
.exist;
|
|
210
|
+
|
|
211
|
+
// Relay SUPERSEED actions and verify completion
|
|
212
|
+
for (const action of superseedActions) {
|
|
213
|
+
if (!action.txHash) continue;
|
|
214
|
+
const originChain = Object.entries(DOMAIN_IDS).find(
|
|
215
|
+
([, id]) => id === action.origin,
|
|
216
|
+
)?.[0] as TestChain | undefined;
|
|
217
|
+
const destChain = Object.entries(DOMAIN_IDS).find(
|
|
218
|
+
([, id]) => id === action.destination,
|
|
219
|
+
)?.[0] as TestChain | undefined;
|
|
220
|
+
|
|
221
|
+
if (originChain && destChain) {
|
|
222
|
+
const originProvider = localProviders.get(originChain);
|
|
223
|
+
expect(originProvider, `Provider should exist for ${originChain}`).to
|
|
224
|
+
.exist;
|
|
225
|
+
const rebalanceTxReceipt = await originProvider!.getTransactionReceipt(
|
|
226
|
+
action.txHash,
|
|
227
|
+
);
|
|
228
|
+
expect(rebalanceTxReceipt, `Receipt should exist for ${action.id}`).to
|
|
229
|
+
.exist;
|
|
230
|
+
|
|
231
|
+
const relayResult = await tryRelayMessage(
|
|
232
|
+
multiProvider,
|
|
233
|
+
hyperlaneCore,
|
|
234
|
+
{
|
|
235
|
+
dispatchTx: rebalanceTxReceipt!,
|
|
236
|
+
messageId: action.messageId!,
|
|
237
|
+
origin: originChain,
|
|
238
|
+
destination: destChain,
|
|
239
|
+
},
|
|
240
|
+
);
|
|
241
|
+
expect(
|
|
242
|
+
relayResult.success,
|
|
243
|
+
`SUPERSEED relay should succeed: ${relayResult.error}`,
|
|
244
|
+
).to.be.true;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const blockTags2 = await context.getConfirmedBlockTags();
|
|
249
|
+
await context.tracker.syncRebalanceActions(blockTags2);
|
|
250
|
+
|
|
251
|
+
for (const action of superseedActions) {
|
|
252
|
+
const completedAction = await context.tracker.getRebalanceAction(
|
|
253
|
+
action.id,
|
|
254
|
+
);
|
|
255
|
+
expect(completedAction!.status).to.equal('complete');
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Relay the original user transfer now that collateral has been rebalanced
|
|
259
|
+
const userTransferRelay = await tryRelayMessage(
|
|
260
|
+
context.multiProvider,
|
|
261
|
+
hyperlaneCore,
|
|
262
|
+
transferResult,
|
|
263
|
+
);
|
|
264
|
+
expect(
|
|
265
|
+
userTransferRelay.success,
|
|
266
|
+
`User transfer relay should succeed: ${userTransferRelay.error}`,
|
|
267
|
+
).to.be.true;
|
|
268
|
+
|
|
269
|
+
const blockTags3 = await context.getConfirmedBlockTags();
|
|
270
|
+
await context.tracker.syncTransfers(blockTags3);
|
|
271
|
+
const completedTransfer = await context.tracker.getTransfer(
|
|
272
|
+
transferResult.messageId,
|
|
273
|
+
);
|
|
274
|
+
expect(completedTransfer!.status).to.equal('complete');
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it('collateralDeficit + minAmount: routes use different bridges', async function () {
|
|
278
|
+
const transferAmount = BigNumber.from('600000000'); // 600 USDC
|
|
279
|
+
const customBalances = {
|
|
280
|
+
anvil1: BigNumber.from('8000000000'),
|
|
281
|
+
anvil2: BigNumber.from('500000000'),
|
|
282
|
+
anvil3: BigNumber.from('50000000'), // below minAmount threshold (100 USDC)
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
const context = await TestRebalancer.builder(
|
|
286
|
+
deploymentManager,
|
|
287
|
+
multiProvider,
|
|
288
|
+
)
|
|
289
|
+
.withStrategy([
|
|
290
|
+
{
|
|
291
|
+
rebalanceStrategy: RebalancerStrategyOptions.CollateralDeficit,
|
|
292
|
+
chains: {
|
|
293
|
+
anvil1: {
|
|
294
|
+
buffer: '0',
|
|
295
|
+
bridge: deployedAddresses.bridgeRoute1.anvil1,
|
|
296
|
+
},
|
|
297
|
+
anvil2: {
|
|
298
|
+
buffer: '0',
|
|
299
|
+
bridge: deployedAddresses.bridgeRoute1.anvil2,
|
|
300
|
+
},
|
|
301
|
+
anvil3: {
|
|
302
|
+
buffer: '0',
|
|
303
|
+
bridge: deployedAddresses.bridgeRoute1.anvil3,
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
rebalanceStrategy: RebalancerStrategyOptions.MinAmount,
|
|
309
|
+
chains: {
|
|
310
|
+
anvil1: {
|
|
311
|
+
minAmount: {
|
|
312
|
+
min: '100',
|
|
313
|
+
target: '120',
|
|
314
|
+
type: RebalancerMinAmountType.Absolute,
|
|
315
|
+
},
|
|
316
|
+
bridge: deployedAddresses.bridgeRoute2.anvil1,
|
|
317
|
+
},
|
|
318
|
+
anvil2: {
|
|
319
|
+
minAmount: {
|
|
320
|
+
min: '100',
|
|
321
|
+
target: '120',
|
|
322
|
+
type: RebalancerMinAmountType.Absolute,
|
|
323
|
+
},
|
|
324
|
+
bridge: deployedAddresses.bridgeRoute2.anvil2,
|
|
325
|
+
},
|
|
326
|
+
anvil3: {
|
|
327
|
+
minAmount: {
|
|
328
|
+
min: '100',
|
|
329
|
+
target: '120',
|
|
330
|
+
type: RebalancerMinAmountType.Absolute,
|
|
331
|
+
},
|
|
332
|
+
bridge: deployedAddresses.bridgeRoute2.anvil3,
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
])
|
|
337
|
+
.withBalances(customBalances)
|
|
338
|
+
.withExecutionMode('execute')
|
|
339
|
+
.build();
|
|
340
|
+
|
|
341
|
+
const ethProvider = localProviders.get('anvil1')!;
|
|
342
|
+
const deployer2 = new ethers.Wallet(ANVIL_TEST_PRIVATE_KEY, ethProvider);
|
|
343
|
+
const token2 = ERC20__factory.connect(
|
|
344
|
+
deployedAddresses.tokens.anvil1,
|
|
345
|
+
deployer2,
|
|
346
|
+
);
|
|
347
|
+
await token2.transfer(userAddress, transferAmount.mul(2));
|
|
348
|
+
|
|
349
|
+
const transferResult = await executeWarpTransfer(
|
|
350
|
+
context.multiProvider,
|
|
351
|
+
{
|
|
352
|
+
originChain: 'anvil1',
|
|
353
|
+
destinationChain: 'anvil2',
|
|
354
|
+
routerAddress: deployedAddresses.monitoredRoute.anvil1,
|
|
355
|
+
tokenAddress: deployedAddresses.tokens.anvil1,
|
|
356
|
+
amount: transferAmount,
|
|
357
|
+
recipient: userAddress,
|
|
358
|
+
senderAddress: userAddress,
|
|
359
|
+
},
|
|
360
|
+
ethProvider,
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
const blockTags4 = await context.getConfirmedBlockTags();
|
|
364
|
+
await context.tracker.syncTransfers(blockTags4);
|
|
365
|
+
|
|
366
|
+
const transfersBeforeRebalance =
|
|
367
|
+
await context.tracker.getInProgressTransfers();
|
|
368
|
+
expect(transfersBeforeRebalance.length).to.equal(
|
|
369
|
+
1,
|
|
370
|
+
'Should have exactly 1 in-progress transfer',
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
const monitor = context.createMonitor(0);
|
|
374
|
+
const event = await getFirstMonitorEvent(monitor);
|
|
375
|
+
await context.orchestrator.executeCycle(event);
|
|
376
|
+
|
|
377
|
+
const activeIntents = await context.tracker.getActiveRebalanceIntents();
|
|
378
|
+
expect(
|
|
379
|
+
activeIntents.length,
|
|
380
|
+
'Should have active rebalance intents',
|
|
381
|
+
).to.be.greaterThan(0);
|
|
382
|
+
|
|
383
|
+
const inProgressActions = await context.tracker.getInProgressActions();
|
|
384
|
+
|
|
385
|
+
const superseedActions = [];
|
|
386
|
+
for (const action of inProgressActions) {
|
|
387
|
+
const intent = activeIntents.find((i) => i.id === action.intentId);
|
|
388
|
+
if (intent?.bridge) {
|
|
389
|
+
const originChain = Object.entries(DOMAIN_IDS).find(
|
|
390
|
+
([, id]) => id === action.origin,
|
|
391
|
+
)?.[0] as TestChain | undefined;
|
|
392
|
+
if (
|
|
393
|
+
originChain &&
|
|
394
|
+
intent.bridge === deployedAddresses.bridgeRoute1[originChain]
|
|
395
|
+
) {
|
|
396
|
+
superseedActions.push(action);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
expect(
|
|
401
|
+
superseedActions.length,
|
|
402
|
+
'Should have SUPERSEED actions from CollateralDeficit',
|
|
403
|
+
).to.be.greaterThan(0);
|
|
404
|
+
|
|
405
|
+
const actionToArbitrum = superseedActions.find(
|
|
406
|
+
(a) => a.destination === DOMAIN_IDS.anvil2,
|
|
407
|
+
);
|
|
408
|
+
expect(actionToArbitrum, 'Should have SUPERSEED action to arbitrum').to
|
|
409
|
+
.exist;
|
|
410
|
+
|
|
411
|
+
for (const action of superseedActions) {
|
|
412
|
+
if (!action.txHash) continue;
|
|
413
|
+
const originChain = Object.entries(DOMAIN_IDS).find(
|
|
414
|
+
([, id]) => id === action.origin,
|
|
415
|
+
)?.[0] as TestChain | undefined;
|
|
416
|
+
const destChain = Object.entries(DOMAIN_IDS).find(
|
|
417
|
+
([, id]) => id === action.destination,
|
|
418
|
+
)?.[0] as TestChain | undefined;
|
|
419
|
+
|
|
420
|
+
if (originChain && destChain) {
|
|
421
|
+
const originProvider = localProviders.get(originChain);
|
|
422
|
+
expect(originProvider, `Provider should exist for ${originChain}`).to
|
|
423
|
+
.exist;
|
|
424
|
+
const rebalanceTxReceipt = await originProvider!.getTransactionReceipt(
|
|
425
|
+
action.txHash,
|
|
426
|
+
);
|
|
427
|
+
expect(rebalanceTxReceipt, `Receipt should exist for ${action.id}`).to
|
|
428
|
+
.exist;
|
|
429
|
+
|
|
430
|
+
const relayResult = await tryRelayMessage(
|
|
431
|
+
multiProvider,
|
|
432
|
+
hyperlaneCore,
|
|
433
|
+
{
|
|
434
|
+
dispatchTx: rebalanceTxReceipt!,
|
|
435
|
+
messageId: action.messageId!,
|
|
436
|
+
origin: originChain,
|
|
437
|
+
destination: destChain,
|
|
438
|
+
},
|
|
439
|
+
);
|
|
440
|
+
expect(
|
|
441
|
+
relayResult.success,
|
|
442
|
+
`SUPERSEED relay should succeed: ${relayResult.error}`,
|
|
443
|
+
).to.be.true;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const blockTags5 = await context.getConfirmedBlockTags();
|
|
448
|
+
await context.tracker.syncRebalanceActions(blockTags5);
|
|
449
|
+
|
|
450
|
+
for (const action of superseedActions) {
|
|
451
|
+
const completedAction = await context.tracker.getRebalanceAction(
|
|
452
|
+
action.id,
|
|
453
|
+
);
|
|
454
|
+
expect(completedAction!.status).to.equal('complete');
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const userTransferRelay = await tryRelayMessage(
|
|
458
|
+
context.multiProvider,
|
|
459
|
+
hyperlaneCore,
|
|
460
|
+
transferResult,
|
|
461
|
+
);
|
|
462
|
+
expect(
|
|
463
|
+
userTransferRelay.success,
|
|
464
|
+
`User transfer relay should succeed: ${userTransferRelay.error}`,
|
|
465
|
+
).to.be.true;
|
|
466
|
+
|
|
467
|
+
const blockTags6 = await context.getConfirmedBlockTags();
|
|
468
|
+
await context.tracker.syncTransfers(blockTags6);
|
|
469
|
+
const completedTransfer = await context.tracker.getTransfer(
|
|
470
|
+
transferResult.messageId,
|
|
471
|
+
);
|
|
472
|
+
expect(completedTransfer!.status).to.equal('complete');
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
it('should propose collateralDeficit rebalance even when slow rebalance is inflight', async function () {
|
|
476
|
+
const ethProvider = localProviders.get('anvil1')!;
|
|
477
|
+
const transferAmount = BigNumber.from('600000000');
|
|
478
|
+
|
|
479
|
+
// Build context with Composite strategy from the start
|
|
480
|
+
// COMPOSITE_DEFICIT_IMBALANCE: eth=8000, arb=500, base=1500
|
|
481
|
+
// Weighted sees: eth surplus (target=6000), base deficit (target=2000)
|
|
482
|
+
// Cycle 1: Weighted creates SUBTENSOR rebalance eth→base
|
|
483
|
+
const context = await TestRebalancer.builder(
|
|
484
|
+
deploymentManager,
|
|
485
|
+
multiProvider,
|
|
486
|
+
)
|
|
487
|
+
.withStrategy([
|
|
488
|
+
{
|
|
489
|
+
rebalanceStrategy: RebalancerStrategyOptions.CollateralDeficit,
|
|
490
|
+
chains: {
|
|
491
|
+
anvil1: {
|
|
492
|
+
buffer: '0',
|
|
493
|
+
bridge: deployedAddresses.bridgeRoute1.anvil1,
|
|
494
|
+
},
|
|
495
|
+
anvil2: {
|
|
496
|
+
buffer: '0',
|
|
497
|
+
bridge: deployedAddresses.bridgeRoute1.anvil2,
|
|
498
|
+
},
|
|
499
|
+
anvil3: {
|
|
500
|
+
buffer: '0',
|
|
501
|
+
bridge: deployedAddresses.bridgeRoute1.anvil3,
|
|
502
|
+
},
|
|
503
|
+
},
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
rebalanceStrategy: RebalancerStrategyOptions.Weighted,
|
|
507
|
+
chains: {
|
|
508
|
+
anvil1: {
|
|
509
|
+
weighted: { weight: 60n, tolerance: 5n },
|
|
510
|
+
bridge: deployedAddresses.bridgeRoute2.anvil1,
|
|
511
|
+
},
|
|
512
|
+
anvil2: {
|
|
513
|
+
weighted: { weight: 20n, tolerance: 5n },
|
|
514
|
+
bridge: deployedAddresses.bridgeRoute2.anvil2,
|
|
515
|
+
},
|
|
516
|
+
anvil3: {
|
|
517
|
+
weighted: { weight: 20n, tolerance: 5n },
|
|
518
|
+
bridge: deployedAddresses.bridgeRoute2.anvil3,
|
|
519
|
+
},
|
|
520
|
+
},
|
|
521
|
+
},
|
|
522
|
+
])
|
|
523
|
+
.withBalances('COMPOSITE_DEFICIT_IMBALANCE')
|
|
524
|
+
.withExecutionMode('execute')
|
|
525
|
+
.build();
|
|
526
|
+
|
|
527
|
+
// ===== CYCLE 1: Execute Weighted rebalance (no deficit yet) =====
|
|
528
|
+
// Initial: eth=7000, arb=2000, base=1000. Target: eth=6000, arb=2000, base=2000
|
|
529
|
+
// CollateralDeficit finds no deficit (no pending transfers), so Weighted runs
|
|
530
|
+
const monitor1 = context.createMonitor(0);
|
|
531
|
+
const event1 = await getFirstMonitorEvent(monitor1);
|
|
532
|
+
await context.orchestrator.executeCycle(event1);
|
|
533
|
+
|
|
534
|
+
const blockTags7 = await context.getConfirmedBlockTags();
|
|
535
|
+
await context.tracker.syncRebalanceActions(blockTags7);
|
|
536
|
+
const inflightAfterCycle1 = await context.tracker.getInProgressActions();
|
|
537
|
+
expect(
|
|
538
|
+
inflightAfterCycle1.length,
|
|
539
|
+
'Cycle 1 should create inflight actions',
|
|
540
|
+
).to.be.greaterThan(0);
|
|
541
|
+
|
|
542
|
+
const activeIntents1 = await context.tracker.getActiveRebalanceIntents();
|
|
543
|
+
const subtensorInflight = inflightAfterCycle1.find((action) => {
|
|
544
|
+
const intent = activeIntents1.find((i) => i.id === action.intentId);
|
|
545
|
+
if (!intent?.bridge) return false;
|
|
546
|
+
const originChain = Object.entries(DOMAIN_IDS).find(
|
|
547
|
+
([, id]) => id === action.origin,
|
|
548
|
+
)?.[0] as TestChain | undefined;
|
|
549
|
+
return (
|
|
550
|
+
originChain &&
|
|
551
|
+
intent.bridge === deployedAddresses.bridgeRoute2[originChain]
|
|
552
|
+
);
|
|
553
|
+
});
|
|
554
|
+
expect(
|
|
555
|
+
subtensorInflight,
|
|
556
|
+
'Should have SUBTENSOR inflight from Weighted strategy',
|
|
557
|
+
).to.exist;
|
|
558
|
+
|
|
559
|
+
// ===== CYCLE 2: Add pending transfer to create deficit, then execute =====
|
|
560
|
+
// Fund user and execute a warp transfer eth→arbitrum to create deficit on arbitrum
|
|
561
|
+
const deployer3 = new ethers.Wallet(ANVIL_TEST_PRIVATE_KEY, ethProvider);
|
|
562
|
+
const token3 = ERC20__factory.connect(
|
|
563
|
+
deployedAddresses.tokens.anvil1,
|
|
564
|
+
deployer3,
|
|
565
|
+
);
|
|
566
|
+
await token3.transfer(userAddress, transferAmount.mul(2));
|
|
567
|
+
|
|
568
|
+
const transferResult = await executeWarpTransfer(
|
|
569
|
+
context.multiProvider,
|
|
570
|
+
{
|
|
571
|
+
originChain: 'anvil1',
|
|
572
|
+
destinationChain: 'anvil2',
|
|
573
|
+
routerAddress: deployedAddresses.monitoredRoute.anvil1,
|
|
574
|
+
tokenAddress: deployedAddresses.tokens.anvil1,
|
|
575
|
+
amount: transferAmount,
|
|
576
|
+
recipient: userAddress,
|
|
577
|
+
senderAddress: userAddress,
|
|
578
|
+
},
|
|
579
|
+
ethProvider,
|
|
580
|
+
);
|
|
581
|
+
|
|
582
|
+
const blockTags8 = await context.getConfirmedBlockTags();
|
|
583
|
+
await context.tracker.syncTransfers(blockTags8);
|
|
584
|
+
|
|
585
|
+
// Verify transfer tracked
|
|
586
|
+
const transfersBeforeCycle2 =
|
|
587
|
+
await context.tracker.getInProgressTransfers();
|
|
588
|
+
expect(transfersBeforeCycle2.length).to.equal(
|
|
589
|
+
1,
|
|
590
|
+
'Should have 1 in-progress transfer',
|
|
591
|
+
);
|
|
592
|
+
|
|
593
|
+
// Execute cycle 2 - now CollateralDeficit should see deficit on arbitrum
|
|
594
|
+
const monitor2 = context.createMonitor(0);
|
|
595
|
+
const event2 = await getFirstMonitorEvent(monitor2);
|
|
596
|
+
await context.orchestrator.executeCycle(event2);
|
|
597
|
+
|
|
598
|
+
const blockTags9 = await context.getConfirmedBlockTags();
|
|
599
|
+
await context.tracker.syncRebalanceActions(blockTags9);
|
|
600
|
+
|
|
601
|
+
const inProgressAfterCycle2 = await context.tracker.getInProgressActions();
|
|
602
|
+
const activeIntents2 = await context.tracker.getActiveRebalanceIntents();
|
|
603
|
+
|
|
604
|
+
const superseedActions = inProgressAfterCycle2.filter((action) => {
|
|
605
|
+
const intent = activeIntents2.find((i) => i.id === action.intentId);
|
|
606
|
+
if (!intent?.bridge) return false;
|
|
607
|
+
const originChain = Object.entries(DOMAIN_IDS).find(
|
|
608
|
+
([, id]) => id === action.origin,
|
|
609
|
+
)?.[0] as TestChain | undefined;
|
|
610
|
+
return (
|
|
611
|
+
originChain &&
|
|
612
|
+
intent.bridge === deployedAddresses.bridgeRoute1[originChain]
|
|
613
|
+
);
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
expect(
|
|
617
|
+
superseedActions.length,
|
|
618
|
+
'Should have SUPERSEED actions from CollateralDeficit despite SUBTENSOR inflight',
|
|
619
|
+
).to.be.greaterThan(0);
|
|
620
|
+
|
|
621
|
+
const superseedToArbitrum = superseedActions.find(
|
|
622
|
+
(a) => a.destination === DOMAIN_IDS.anvil2,
|
|
623
|
+
);
|
|
624
|
+
expect(
|
|
625
|
+
superseedToArbitrum,
|
|
626
|
+
'Should have SUPERSEED action to arbitrum for deficit',
|
|
627
|
+
).to.exist;
|
|
628
|
+
|
|
629
|
+
// Relay SUPERSEED actions and verify completion
|
|
630
|
+
for (const action of superseedActions) {
|
|
631
|
+
if (!action.txHash) continue;
|
|
632
|
+
const originChain = Object.entries(DOMAIN_IDS).find(
|
|
633
|
+
([, id]) => id === action.origin,
|
|
634
|
+
)?.[0] as TestChain | undefined;
|
|
635
|
+
const destChain = Object.entries(DOMAIN_IDS).find(
|
|
636
|
+
([, id]) => id === action.destination,
|
|
637
|
+
)?.[0] as TestChain | undefined;
|
|
638
|
+
|
|
639
|
+
if (originChain && destChain) {
|
|
640
|
+
const originProvider = localProviders.get(originChain);
|
|
641
|
+
expect(originProvider, `Provider should exist for ${originChain}`).to
|
|
642
|
+
.exist;
|
|
643
|
+
const rebalanceTxReceipt = await originProvider!.getTransactionReceipt(
|
|
644
|
+
action.txHash,
|
|
645
|
+
);
|
|
646
|
+
expect(rebalanceTxReceipt, `Receipt should exist for ${action.id}`).to
|
|
647
|
+
.exist;
|
|
648
|
+
|
|
649
|
+
const relayResult = await tryRelayMessage(
|
|
650
|
+
multiProvider,
|
|
651
|
+
hyperlaneCore,
|
|
652
|
+
{
|
|
653
|
+
dispatchTx: rebalanceTxReceipt!,
|
|
654
|
+
messageId: action.messageId!,
|
|
655
|
+
origin: originChain,
|
|
656
|
+
destination: destChain,
|
|
657
|
+
},
|
|
658
|
+
);
|
|
659
|
+
expect(
|
|
660
|
+
relayResult.success,
|
|
661
|
+
`SUPERSEED relay should succeed: ${relayResult.error}`,
|
|
662
|
+
).to.be.true;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
const blockTags10 = await context.getConfirmedBlockTags();
|
|
667
|
+
await context.tracker.syncRebalanceActions(blockTags10);
|
|
668
|
+
|
|
669
|
+
for (const action of superseedActions) {
|
|
670
|
+
const completedAction = await context.tracker.getRebalanceAction(
|
|
671
|
+
action.id,
|
|
672
|
+
);
|
|
673
|
+
expect(completedAction!.status).to.equal('complete');
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// Relay the original user transfer now that collateral has been rebalanced
|
|
677
|
+
const userTransferRelay = await tryRelayMessage(
|
|
678
|
+
context.multiProvider,
|
|
679
|
+
hyperlaneCore,
|
|
680
|
+
transferResult,
|
|
681
|
+
);
|
|
682
|
+
expect(
|
|
683
|
+
userTransferRelay.success,
|
|
684
|
+
`User transfer relay should succeed: ${userTransferRelay.error}`,
|
|
685
|
+
).to.be.true;
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
it('should simulate end state accounting for inflight rebalances', async function () {
|
|
689
|
+
const ethProvider = localProviders.get('anvil1')!;
|
|
690
|
+
|
|
691
|
+
// Build context with Weighted strategy
|
|
692
|
+
// Initial: eth=7000, arb=2000, base=1000 (total=10000)
|
|
693
|
+
// Target: eth=60% (6000), arb=20% (2000), base=20% (2000)
|
|
694
|
+
// Cycle 1 will create inflight eth→base for ~1000 USDC
|
|
695
|
+
const context = await TestRebalancer.builder(
|
|
696
|
+
deploymentManager,
|
|
697
|
+
multiProvider,
|
|
698
|
+
)
|
|
699
|
+
.withStrategy([
|
|
700
|
+
{
|
|
701
|
+
rebalanceStrategy: RebalancerStrategyOptions.Weighted,
|
|
702
|
+
chains: {
|
|
703
|
+
anvil1: {
|
|
704
|
+
weighted: { weight: 60n, tolerance: 5n },
|
|
705
|
+
bridge: deployedAddresses.bridgeRoute2.anvil1,
|
|
706
|
+
},
|
|
707
|
+
anvil2: {
|
|
708
|
+
weighted: { weight: 20n, tolerance: 5n },
|
|
709
|
+
bridge: deployedAddresses.bridgeRoute2.anvil2,
|
|
710
|
+
},
|
|
711
|
+
anvil3: {
|
|
712
|
+
weighted: { weight: 20n, tolerance: 5n },
|
|
713
|
+
bridge: deployedAddresses.bridgeRoute2.anvil3,
|
|
714
|
+
},
|
|
715
|
+
},
|
|
716
|
+
},
|
|
717
|
+
])
|
|
718
|
+
.withBalances({
|
|
719
|
+
anvil1: BigNumber.from('7000000000'),
|
|
720
|
+
anvil2: BigNumber.from('2000000000'),
|
|
721
|
+
anvil3: BigNumber.from('1000000000'),
|
|
722
|
+
})
|
|
723
|
+
.withExecutionMode('execute')
|
|
724
|
+
.build();
|
|
725
|
+
|
|
726
|
+
// ===== CYCLE 1: Execute rebalance to create inflight eth→base =====
|
|
727
|
+
const monitor1 = context.createMonitor(0);
|
|
728
|
+
const event1 = await getFirstMonitorEvent(monitor1);
|
|
729
|
+
await context.orchestrator.executeCycle(event1);
|
|
730
|
+
|
|
731
|
+
const blockTags11 = await context.getConfirmedBlockTags();
|
|
732
|
+
await context.tracker.syncRebalanceActions(blockTags11);
|
|
733
|
+
|
|
734
|
+
const inflightAfterCycle1 = await context.tracker.getInProgressActions();
|
|
735
|
+
expect(
|
|
736
|
+
inflightAfterCycle1.length,
|
|
737
|
+
'Cycle 1 should create inflight action',
|
|
738
|
+
).to.be.greaterThan(0);
|
|
739
|
+
|
|
740
|
+
const inflightToBase = inflightAfterCycle1.find(
|
|
741
|
+
(a) =>
|
|
742
|
+
a.destination === DOMAIN_IDS.anvil3 && a.origin === DOMAIN_IDS.anvil1,
|
|
743
|
+
);
|
|
744
|
+
expect(inflightToBase, 'Should have inflight action eth→base').to.exist;
|
|
745
|
+
|
|
746
|
+
const inflightAmount = BigNumber.from(inflightToBase!.amount);
|
|
747
|
+
expect(inflightAmount.gt(0), 'Inflight amount should be positive').to.be
|
|
748
|
+
.true;
|
|
749
|
+
|
|
750
|
+
// ===== CYCLE 2: Execute again - should account for inflight =====
|
|
751
|
+
// Weighted now sees: base effective = current + inflight ≈ 2000 (target)
|
|
752
|
+
// Should propose reduced amount or nothing to base
|
|
753
|
+
const monitor2 = context.createMonitor(0);
|
|
754
|
+
const event2 = await getFirstMonitorEvent(monitor2);
|
|
755
|
+
await context.orchestrator.executeCycle(event2);
|
|
756
|
+
|
|
757
|
+
const blockTags12 = await context.getConfirmedBlockTags();
|
|
758
|
+
await context.tracker.syncRebalanceActions(blockTags12);
|
|
759
|
+
|
|
760
|
+
// Check if new routes to base were proposed
|
|
761
|
+
const inProgressAfterCycle2 = await context.tracker.getInProgressActions();
|
|
762
|
+
const newActionsToBase = inProgressAfterCycle2.filter(
|
|
763
|
+
(a) =>
|
|
764
|
+
a.destination === DOMAIN_IDS.anvil3 &&
|
|
765
|
+
a.id !== inflightToBase!.id &&
|
|
766
|
+
a.status === 'in_progress',
|
|
767
|
+
);
|
|
768
|
+
|
|
769
|
+
if (newActionsToBase.length > 0) {
|
|
770
|
+
// If route was proposed, should be much smaller than original 1000 USDC
|
|
771
|
+
const proposedAmount = BigNumber.from(
|
|
772
|
+
newActionsToBase[0].amount,
|
|
773
|
+
).toBigInt();
|
|
774
|
+
expect(
|
|
775
|
+
proposedAmount < 500000000n,
|
|
776
|
+
`Amount to base (${proposedAmount}) should be reduced accounting for inflight`,
|
|
777
|
+
).to.be.true;
|
|
778
|
+
}
|
|
779
|
+
// If no new route to base, that's valid (within tolerance after inflight)
|
|
780
|
+
|
|
781
|
+
// Verify inflight still exists and action tracking is working
|
|
782
|
+
const finalInProgress = await context.tracker.getInProgressActions();
|
|
783
|
+
const inflightStillActive = finalInProgress.find(
|
|
784
|
+
(a) => a.id === inflightToBase!.id,
|
|
785
|
+
);
|
|
786
|
+
expect(inflightStillActive, 'Inflight action should still be tracked').to
|
|
787
|
+
.exist;
|
|
788
|
+
expect(inflightStillActive!.status).to.equal('in_progress');
|
|
789
|
+
|
|
790
|
+
// Relay the inflight from cycle 1
|
|
791
|
+
if (inflightToBase?.txHash) {
|
|
792
|
+
const rebalanceTxReceipt = await ethProvider.getTransactionReceipt(
|
|
793
|
+
inflightToBase.txHash,
|
|
794
|
+
);
|
|
795
|
+
const relayResult = await tryRelayMessage(multiProvider, hyperlaneCore, {
|
|
796
|
+
dispatchTx: rebalanceTxReceipt,
|
|
797
|
+
messageId: inflightToBase.messageId!,
|
|
798
|
+
origin: 'anvil1',
|
|
799
|
+
destination: 'anvil3',
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
if (relayResult.success) {
|
|
803
|
+
const blockTags13 = await context.getConfirmedBlockTags();
|
|
804
|
+
await context.tracker.syncRebalanceActions(blockTags13);
|
|
805
|
+
|
|
806
|
+
const completedAction = await context.tracker.getRebalanceAction(
|
|
807
|
+
inflightToBase!.id,
|
|
808
|
+
);
|
|
809
|
+
expect(completedAction!.status).to.equal('complete');
|
|
810
|
+
}
|
|
811
|
+
// Relay may fail due to ISM configuration in test environment - main assertion already passed
|
|
812
|
+
}
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
it('should execute collateralDeficit portion; slow bridge intents fail', async function () {
|
|
816
|
+
const transferAmount = BigNumber.from('600000000'); // 600 USDC
|
|
817
|
+
|
|
818
|
+
// Use balances that trigger both strategies
|
|
819
|
+
// - Weighted: ethereum has too much (needs rebalance to base)
|
|
820
|
+
// - CollateralDeficit: pending transfer will create deficit on arbitrum
|
|
821
|
+
const context = await TestRebalancer.builder(
|
|
822
|
+
deploymentManager,
|
|
823
|
+
multiProvider,
|
|
824
|
+
)
|
|
825
|
+
.withStrategy([
|
|
826
|
+
{
|
|
827
|
+
rebalanceStrategy: RebalancerStrategyOptions.CollateralDeficit,
|
|
828
|
+
chains: {
|
|
829
|
+
anvil1: {
|
|
830
|
+
buffer: '0',
|
|
831
|
+
bridge: deployedAddresses.bridgeRoute1.anvil1,
|
|
832
|
+
},
|
|
833
|
+
anvil2: {
|
|
834
|
+
buffer: '0',
|
|
835
|
+
bridge: deployedAddresses.bridgeRoute1.anvil2,
|
|
836
|
+
},
|
|
837
|
+
anvil3: {
|
|
838
|
+
buffer: '0',
|
|
839
|
+
bridge: deployedAddresses.bridgeRoute1.anvil3,
|
|
840
|
+
},
|
|
841
|
+
},
|
|
842
|
+
},
|
|
843
|
+
{
|
|
844
|
+
rebalanceStrategy: RebalancerStrategyOptions.Weighted,
|
|
845
|
+
chains: {
|
|
846
|
+
anvil1: {
|
|
847
|
+
weighted: { weight: 60n, tolerance: 5n },
|
|
848
|
+
bridge: deployedAddresses.bridgeRoute2.anvil1,
|
|
849
|
+
},
|
|
850
|
+
anvil2: {
|
|
851
|
+
weighted: { weight: 20n, tolerance: 5n },
|
|
852
|
+
bridge: deployedAddresses.bridgeRoute2.anvil2,
|
|
853
|
+
},
|
|
854
|
+
anvil3: {
|
|
855
|
+
weighted: { weight: 20n, tolerance: 5n },
|
|
856
|
+
bridge: deployedAddresses.bridgeRoute2.anvil3,
|
|
857
|
+
},
|
|
858
|
+
},
|
|
859
|
+
},
|
|
860
|
+
])
|
|
861
|
+
.withBalances('COMPOSITE_DEFICIT_IMBALANCE')
|
|
862
|
+
.withExecutionMode('execute')
|
|
863
|
+
.build();
|
|
864
|
+
|
|
865
|
+
// Fund user and execute warp transfer to create deficit
|
|
866
|
+
const ethProvider = localProviders.get('anvil1')!;
|
|
867
|
+
const deployer4 = new ethers.Wallet(ANVIL_TEST_PRIVATE_KEY, ethProvider);
|
|
868
|
+
const token4 = ERC20__factory.connect(
|
|
869
|
+
deployedAddresses.tokens.anvil1,
|
|
870
|
+
deployer4,
|
|
871
|
+
);
|
|
872
|
+
await token4.transfer(userAddress, transferAmount.mul(2));
|
|
873
|
+
|
|
874
|
+
const transferResult = await executeWarpTransfer(
|
|
875
|
+
context.multiProvider,
|
|
876
|
+
{
|
|
877
|
+
originChain: 'anvil1',
|
|
878
|
+
destinationChain: 'anvil2',
|
|
879
|
+
routerAddress: deployedAddresses.monitoredRoute.anvil1,
|
|
880
|
+
tokenAddress: deployedAddresses.tokens.anvil1,
|
|
881
|
+
amount: transferAmount,
|
|
882
|
+
recipient: userAddress,
|
|
883
|
+
senderAddress: userAddress,
|
|
884
|
+
},
|
|
885
|
+
ethProvider,
|
|
886
|
+
);
|
|
887
|
+
|
|
888
|
+
const blockTags14 = await context.getConfirmedBlockTags();
|
|
889
|
+
await context.tracker.syncTransfers(blockTags14);
|
|
890
|
+
|
|
891
|
+
// Verify transfer was tracked
|
|
892
|
+
const transfersBeforeRebalance =
|
|
893
|
+
await context.tracker.getInProgressTransfers();
|
|
894
|
+
expect(transfersBeforeRebalance.length).to.equal(
|
|
895
|
+
1,
|
|
896
|
+
'Should have exactly 1 in-progress transfer',
|
|
897
|
+
);
|
|
898
|
+
|
|
899
|
+
// Execute cycle
|
|
900
|
+
const monitor = context.createMonitor(0);
|
|
901
|
+
const event = await getFirstMonitorEvent(monitor);
|
|
902
|
+
await context.orchestrator.executeCycle(event);
|
|
903
|
+
|
|
904
|
+
// Verify BOTH bridge types have intents/actions
|
|
905
|
+
const activeIntents = await context.tracker.getActiveRebalanceIntents();
|
|
906
|
+
const inProgressActions = await context.tracker.getInProgressActions();
|
|
907
|
+
|
|
908
|
+
expect(
|
|
909
|
+
activeIntents.length,
|
|
910
|
+
'Should have active rebalance intents',
|
|
911
|
+
).to.be.greaterThan(0);
|
|
912
|
+
|
|
913
|
+
// Helper to get chain name from domain
|
|
914
|
+
const getChainFromDomain = (domain: number): TestChain | undefined =>
|
|
915
|
+
Object.entries(DOMAIN_IDS).find(([, id]) => id === domain)?.[0] as
|
|
916
|
+
| TestChain
|
|
917
|
+
| undefined;
|
|
918
|
+
|
|
919
|
+
// Identify SUPERSEED and SUBTENSOR actions
|
|
920
|
+
const superseedActions = [];
|
|
921
|
+
const subtensorActions = [];
|
|
922
|
+
for (const action of inProgressActions) {
|
|
923
|
+
const intent = activeIntents.find((i) => i.id === action.intentId);
|
|
924
|
+
if (intent?.bridge) {
|
|
925
|
+
const originChain = getChainFromDomain(action.origin);
|
|
926
|
+
if (originChain) {
|
|
927
|
+
if (intent.bridge === deployedAddresses.bridgeRoute1[originChain]) {
|
|
928
|
+
superseedActions.push(action);
|
|
929
|
+
} else if (
|
|
930
|
+
intent.bridge === deployedAddresses.bridgeRoute2[originChain]
|
|
931
|
+
) {
|
|
932
|
+
subtensorActions.push(action);
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
expect(
|
|
939
|
+
superseedActions.length,
|
|
940
|
+
'Should have SUPERSEED actions from CollateralDeficit',
|
|
941
|
+
).to.be.greaterThan(0);
|
|
942
|
+
expect(
|
|
943
|
+
subtensorActions.length,
|
|
944
|
+
'Should have SUBTENSOR actions from Weighted',
|
|
945
|
+
).to.be.greaterThan(0);
|
|
946
|
+
|
|
947
|
+
// Relay SUPERSEED actions (SUBTENSOR relay requires CCIP-read metadata not available in test env)
|
|
948
|
+
for (const action of superseedActions) {
|
|
949
|
+
if (!action.txHash) continue;
|
|
950
|
+
|
|
951
|
+
const originChain = getChainFromDomain(action.origin);
|
|
952
|
+
const destChain = getChainFromDomain(action.destination);
|
|
953
|
+
if (!originChain || !destChain) continue;
|
|
954
|
+
|
|
955
|
+
const provider = localProviders.get(originChain)!;
|
|
956
|
+
const rebalanceTxReceipt = await provider.getTransactionReceipt(
|
|
957
|
+
action.txHash,
|
|
958
|
+
);
|
|
959
|
+
|
|
960
|
+
const relayResult = await tryRelayMessage(multiProvider, hyperlaneCore, {
|
|
961
|
+
dispatchTx: rebalanceTxReceipt,
|
|
962
|
+
messageId: action.messageId!,
|
|
963
|
+
origin: originChain,
|
|
964
|
+
destination: destChain,
|
|
965
|
+
});
|
|
966
|
+
expect(
|
|
967
|
+
relayResult.success,
|
|
968
|
+
`SUPERSEED relay should succeed: ${relayResult.error}`,
|
|
969
|
+
).to.be.true;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
const blockTags15 = await context.getConfirmedBlockTags();
|
|
973
|
+
await context.tracker.syncRebalanceActions(blockTags15);
|
|
974
|
+
|
|
975
|
+
for (const action of superseedActions) {
|
|
976
|
+
const completedAction = await context.tracker.getRebalanceAction(
|
|
977
|
+
action.id,
|
|
978
|
+
);
|
|
979
|
+
expect(completedAction!.status).to.equal('complete');
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
// Verify SUBTENSOR actions are still in progress (relay not possible in test env)
|
|
983
|
+
for (const action of subtensorActions) {
|
|
984
|
+
const trackedAction = await context.tracker.getRebalanceAction(action.id);
|
|
985
|
+
expect(trackedAction, `SUBTENSOR action ${action.id} should exist`).to
|
|
986
|
+
.exist;
|
|
987
|
+
expect(trackedAction!.status).to.equal('in_progress');
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
// Relay the original user transfer now that collateral has been rebalanced
|
|
991
|
+
const userTransferRelay = await tryRelayMessage(
|
|
992
|
+
context.multiProvider,
|
|
993
|
+
hyperlaneCore,
|
|
994
|
+
transferResult,
|
|
995
|
+
);
|
|
996
|
+
expect(
|
|
997
|
+
userTransferRelay.success,
|
|
998
|
+
`User transfer relay should succeed: ${userTransferRelay.error}`,
|
|
999
|
+
).to.be.true;
|
|
1000
|
+
|
|
1001
|
+
const blockTags16 = await context.getConfirmedBlockTags();
|
|
1002
|
+
await context.tracker.syncTransfers(blockTags16);
|
|
1003
|
+
const completedTransfer = await context.tracker.getTransfer(
|
|
1004
|
+
transferResult.messageId,
|
|
1005
|
+
);
|
|
1006
|
+
expect(completedTransfer!.status).to.equal('complete');
|
|
1007
|
+
});
|
|
1008
|
+
});
|