@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,731 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EVM,
|
|
3
|
+
KeypairWalletAdapter,
|
|
4
|
+
Solana,
|
|
5
|
+
type LiFiStep,
|
|
6
|
+
type Route,
|
|
7
|
+
type RouteExtended,
|
|
8
|
+
convertQuoteToRoute,
|
|
9
|
+
createConfig,
|
|
10
|
+
executeRoute,
|
|
11
|
+
getQuote,
|
|
12
|
+
getStatus,
|
|
13
|
+
config as lifiConfig,
|
|
14
|
+
} from '@lifi/sdk';
|
|
15
|
+
import bs58 from 'bs58';
|
|
16
|
+
import type { ChainMetadata } from '@humanode-hyperlane/sdk';
|
|
17
|
+
import { ProtocolType, assert, ensure0x } from '@humanode-hyperlane/utils';
|
|
18
|
+
import type { Logger } from 'pino';
|
|
19
|
+
import { type Chain, createWalletClient, http } from 'viem';
|
|
20
|
+
import { privateKeyToAccount } from 'viem/accounts';
|
|
21
|
+
import { arbitrum, base, mainnet, optimism } from 'viem/chains';
|
|
22
|
+
|
|
23
|
+
import type {
|
|
24
|
+
BridgeQuote,
|
|
25
|
+
BridgeQuoteParams,
|
|
26
|
+
BridgeTransferResult,
|
|
27
|
+
BridgeTransferStatus,
|
|
28
|
+
ExternalBridgeConfig,
|
|
29
|
+
IExternalBridge,
|
|
30
|
+
} from '../interfaces/IExternalBridge.js';
|
|
31
|
+
import { parseSolanaPrivateKey } from '../utils/solanaKeyParser.js';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* LiFi API base URL for REST endpoints.
|
|
35
|
+
* The SDK doesn't support toAmount quotes, so we use REST API directly.
|
|
36
|
+
*/
|
|
37
|
+
const LIFI_API_BASE = 'https://li.quest/v1';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Known chains for viem - add more as needed.
|
|
41
|
+
* TODO: can we think of a cleaner way to do this?
|
|
42
|
+
*/
|
|
43
|
+
const VIEM_CHAINS: Record<number, Chain> = {
|
|
44
|
+
[mainnet.id]: mainnet,
|
|
45
|
+
[arbitrum.id]: arbitrum,
|
|
46
|
+
[base.id]: base,
|
|
47
|
+
[optimism.id]: optimism,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Mapping from Hyperlane domain IDs to LiFi chain IDs for non-EVM chains.
|
|
52
|
+
* EVM chains use their native chain ID which is the same in both systems.
|
|
53
|
+
* Non-EVM chains (e.g., Solana) have different identifiers in each system.
|
|
54
|
+
*
|
|
55
|
+
* @see https://docs.li.fi/introduction/lifi-architecture/solana-overview
|
|
56
|
+
*/
|
|
57
|
+
const HYPERLANE_TO_LIFI_CHAIN_IDS: Record<number, number> = {
|
|
58
|
+
1399811149: 1151111081099710, // Solana: Hyperlane domain → LiFi chain ID
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get viem chain config by chain ID.
|
|
63
|
+
* Falls back to a minimal chain config if not found.
|
|
64
|
+
*/
|
|
65
|
+
function getViemChain(chainId: number, rpcUrl?: string): Chain {
|
|
66
|
+
const chain = VIEM_CHAINS[chainId];
|
|
67
|
+
if (chain) {
|
|
68
|
+
if (rpcUrl) {
|
|
69
|
+
return { ...chain, rpcUrls: { default: { http: [rpcUrl] } } };
|
|
70
|
+
}
|
|
71
|
+
return chain;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Fallback for chains not in our registry
|
|
75
|
+
return {
|
|
76
|
+
id: chainId,
|
|
77
|
+
name: `Chain ${chainId}`,
|
|
78
|
+
nativeCurrency: { name: 'ETH', symbol: 'ETH', decimals: 18 },
|
|
79
|
+
rpcUrls: {
|
|
80
|
+
default: { http: rpcUrl ? [rpcUrl] : [] },
|
|
81
|
+
},
|
|
82
|
+
} as Chain;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function toBase58SolanaKey(rawKey: string): string {
|
|
86
|
+
const trimmedKey = rawKey.trim();
|
|
87
|
+
if (!trimmedKey.startsWith('[') && !trimmedKey.includes(',')) {
|
|
88
|
+
try {
|
|
89
|
+
const decoded = bs58.decode(trimmedKey);
|
|
90
|
+
if (decoded.length === 64) {
|
|
91
|
+
return trimmedKey;
|
|
92
|
+
}
|
|
93
|
+
} catch {
|
|
94
|
+
// Not valid base58, continue to parse as byte array
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const bytes = parseSolanaPrivateKey(trimmedKey);
|
|
99
|
+
return bs58.encode(bytes);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* LiFi implementation of IExternalBridge using the official @lifi/sdk.
|
|
104
|
+
*
|
|
105
|
+
* The SDK provides:
|
|
106
|
+
* - Automatic token approvals via executeRoute()
|
|
107
|
+
* - Multi-step route handling (swap → bridge → swap)
|
|
108
|
+
* - Built-in status tracking via getStatus()
|
|
109
|
+
* - Native support for EVM, Solana, and other chains
|
|
110
|
+
*
|
|
111
|
+
* @see https://docs.li.fi/integrate-li.fi-sdk
|
|
112
|
+
*/
|
|
113
|
+
export class LiFiBridge implements IExternalBridge {
|
|
114
|
+
private static readonly NATIVE_TOKEN_ADDRESS =
|
|
115
|
+
'0x0000000000000000000000000000000000000000';
|
|
116
|
+
|
|
117
|
+
readonly externalBridgeId = 'lifi';
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Convert a Hyperlane domain ID to a LiFi-compatible chain ID.
|
|
121
|
+
* For EVM chains, these are identical (e.g., Arbitrum = 42161 in both).
|
|
122
|
+
* For non-EVM chains like Solana, LiFi uses different chain identifiers.
|
|
123
|
+
*/
|
|
124
|
+
static toLiFiChainId(chainId: number): number {
|
|
125
|
+
return HYPERLANE_TO_LIFI_CHAIN_IDS[chainId] ?? chainId;
|
|
126
|
+
}
|
|
127
|
+
readonly logger: Logger;
|
|
128
|
+
private initialized = false;
|
|
129
|
+
private _executeLock: Promise<void> = Promise.resolve();
|
|
130
|
+
private readonly config: ExternalBridgeConfig;
|
|
131
|
+
private readonly chainMetadataByChainId: Map<number, ChainMetadata>;
|
|
132
|
+
|
|
133
|
+
constructor(config: ExternalBridgeConfig, logger: Logger) {
|
|
134
|
+
this.config = config;
|
|
135
|
+
this.logger = logger;
|
|
136
|
+
// Build chainId -> metadata map for O(1) lookups
|
|
137
|
+
this.chainMetadataByChainId = new Map();
|
|
138
|
+
if (config.chainMetadata) {
|
|
139
|
+
for (const metadata of Object.values(config.chainMetadata)) {
|
|
140
|
+
if (metadata.chainId !== undefined) {
|
|
141
|
+
this.chainMetadataByChainId.set(Number(metadata.chainId), metadata);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Also key by LiFi chain IDs so both Hyperlane domains and LiFi IDs resolve to the same metadata.
|
|
145
|
+
for (const [hyperlaneDomainId, lifiChainId] of Object.entries(
|
|
146
|
+
HYPERLANE_TO_LIFI_CHAIN_IDS,
|
|
147
|
+
)) {
|
|
148
|
+
const metadata = this.chainMetadataByChainId.get(
|
|
149
|
+
Number(hyperlaneDomainId),
|
|
150
|
+
);
|
|
151
|
+
if (metadata !== undefined) {
|
|
152
|
+
this.chainMetadataByChainId.set(Number(lifiChainId), metadata);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
getNativeTokenAddress(): string {
|
|
159
|
+
return LiFiBridge.NATIVE_TOKEN_ADDRESS;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
private initialize(): void {
|
|
163
|
+
if (this.initialized) return;
|
|
164
|
+
|
|
165
|
+
createConfig({
|
|
166
|
+
integrator: this.config.integrator,
|
|
167
|
+
apiKey: this.config.apiKey,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
this.initialized = true;
|
|
171
|
+
this.logger.info(
|
|
172
|
+
{ integrator: this.config.integrator },
|
|
173
|
+
'LiFi SDK initialized',
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Resolve RPC URL for a given EVM chainId from chain metadata.
|
|
179
|
+
* Iterates metadata to find matching chainId and returns first HTTP RPC URL.
|
|
180
|
+
*/
|
|
181
|
+
private getRpcUrlForChainId(chainId: number): string | undefined {
|
|
182
|
+
return this.chainMetadataByChainId.get(chainId)?.rpcUrls?.[0]?.http;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private getProtocolTypeForChainId(chainId: number): ProtocolType | undefined {
|
|
186
|
+
return this.chainMetadataByChainId.get(chainId)?.protocol;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
private addressesEqual(a: string, b: string, chainId: number): boolean {
|
|
190
|
+
const protocol = this.getProtocolTypeForChainId(chainId);
|
|
191
|
+
// Sealevel uses base58 addresses where case is significant.
|
|
192
|
+
if (protocol === ProtocolType.Sealevel) {
|
|
193
|
+
return a === b;
|
|
194
|
+
}
|
|
195
|
+
return a.toLowerCase() === b.toLowerCase();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Configure LiFi SDK providers from the given private keys.
|
|
200
|
+
* Sets up wallet/signer for each protocol type present in the keys map.
|
|
201
|
+
*/
|
|
202
|
+
private configureLiFiProviders(
|
|
203
|
+
privateKeys: Partial<Record<ProtocolType, string>>,
|
|
204
|
+
fromChain: number,
|
|
205
|
+
fromRpcUrl: string | undefined,
|
|
206
|
+
): void {
|
|
207
|
+
const providers: Parameters<typeof lifiConfig.setProviders>[0] = [];
|
|
208
|
+
for (const [protocol, key] of Object.entries(privateKeys)) {
|
|
209
|
+
switch (protocol) {
|
|
210
|
+
case ProtocolType.Ethereum: {
|
|
211
|
+
const account = privateKeyToAccount(ensure0x(key) as `0x${string}`);
|
|
212
|
+
const chain = getViemChain(fromChain, fromRpcUrl);
|
|
213
|
+
const walletClient = createWalletClient({
|
|
214
|
+
account,
|
|
215
|
+
chain,
|
|
216
|
+
transport: http(fromRpcUrl),
|
|
217
|
+
});
|
|
218
|
+
providers.push(
|
|
219
|
+
EVM({
|
|
220
|
+
getWalletClient: async () => walletClient,
|
|
221
|
+
switchChain: async (requiredChainId: number) => {
|
|
222
|
+
const switchRpcUrl = this.getRpcUrlForChainId(requiredChainId);
|
|
223
|
+
const requiredChain = getViemChain(
|
|
224
|
+
requiredChainId,
|
|
225
|
+
switchRpcUrl,
|
|
226
|
+
);
|
|
227
|
+
return createWalletClient({
|
|
228
|
+
account,
|
|
229
|
+
chain: requiredChain,
|
|
230
|
+
transport: http(switchRpcUrl),
|
|
231
|
+
});
|
|
232
|
+
},
|
|
233
|
+
}),
|
|
234
|
+
);
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
case ProtocolType.Sealevel: {
|
|
238
|
+
const base58Key = toBase58SolanaKey(key);
|
|
239
|
+
providers.push(
|
|
240
|
+
Solana({
|
|
241
|
+
getWalletAdapter: async () => new KeypairWalletAdapter(base58Key),
|
|
242
|
+
}),
|
|
243
|
+
);
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
default:
|
|
247
|
+
throw new Error(
|
|
248
|
+
`Unsupported protocol type '${protocol}' for LiFi provider`,
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
lifiConfig.setProviders(providers);
|
|
254
|
+
|
|
255
|
+
this.logger.debug(
|
|
256
|
+
{
|
|
257
|
+
fromChain,
|
|
258
|
+
protocols: Object.keys(privateKeys),
|
|
259
|
+
},
|
|
260
|
+
'Configured LiFi providers for route execution',
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Get a quote for bridging tokens.
|
|
266
|
+
* Supports two modes:
|
|
267
|
+
* - fromAmount: "I'm sending X, what do I get?" (uses SDK)
|
|
268
|
+
* - toAmount: "I want X, how much do I send?" (uses REST API)
|
|
269
|
+
*
|
|
270
|
+
* Returns route data ready for execution.
|
|
271
|
+
*/
|
|
272
|
+
async quote(params: BridgeQuoteParams): Promise<BridgeQuote<LiFiStep>> {
|
|
273
|
+
this.initialize();
|
|
274
|
+
|
|
275
|
+
// Validate that exactly one of fromAmount or toAmount is provided
|
|
276
|
+
if (params.fromAmount !== undefined && params.toAmount !== undefined) {
|
|
277
|
+
throw new Error(
|
|
278
|
+
'Cannot specify both fromAmount and toAmount - provide exactly one',
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
if (params.fromAmount === undefined && params.toAmount === undefined) {
|
|
282
|
+
throw new Error('Must specify either fromAmount or toAmount');
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
assert(
|
|
286
|
+
params.fromAmount === undefined || params.fromAmount > 0n,
|
|
287
|
+
'fromAmount must be positive',
|
|
288
|
+
);
|
|
289
|
+
assert(
|
|
290
|
+
params.toAmount === undefined || params.toAmount > 0n,
|
|
291
|
+
'toAmount must be positive',
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
// Dispatch to appropriate quote method
|
|
295
|
+
if (params.toAmount !== undefined) {
|
|
296
|
+
return this.quoteByReceivingAmount(params);
|
|
297
|
+
} else {
|
|
298
|
+
return this.quoteBySpendingAmount(params);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Get a quote by specifying the amount to send (standard quote).
|
|
304
|
+
* Uses the LiFi SDK.
|
|
305
|
+
*/
|
|
306
|
+
private async quoteBySpendingAmount(
|
|
307
|
+
params: BridgeQuoteParams,
|
|
308
|
+
): Promise<BridgeQuote<LiFiStep>> {
|
|
309
|
+
this.logger.debug({ params }, 'Requesting LiFi quote by spending amount');
|
|
310
|
+
|
|
311
|
+
const lifiFromChain = LiFiBridge.toLiFiChainId(params.fromChain);
|
|
312
|
+
const lifiToChain = LiFiBridge.toLiFiChainId(params.toChain);
|
|
313
|
+
|
|
314
|
+
const quote = await getQuote({
|
|
315
|
+
fromChain: lifiFromChain,
|
|
316
|
+
toChain: lifiToChain,
|
|
317
|
+
fromToken: params.fromToken,
|
|
318
|
+
toToken: params.toToken,
|
|
319
|
+
fromAmount: params.fromAmount!.toString(),
|
|
320
|
+
fromAddress: params.fromAddress,
|
|
321
|
+
toAddress: params.toAddress ?? params.fromAddress,
|
|
322
|
+
slippage: params.slippage ?? this.config.defaultSlippage ?? 0.005,
|
|
323
|
+
// Prefer faster routes for rebalancing
|
|
324
|
+
order: 'RECOMMENDED',
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
const { gasCosts, feeCosts } = this.extractCosts(quote);
|
|
328
|
+
|
|
329
|
+
this.logger.info(
|
|
330
|
+
{
|
|
331
|
+
quoteId: quote.id,
|
|
332
|
+
tool: quote.tool,
|
|
333
|
+
fromAmount: quote.action.fromAmount,
|
|
334
|
+
toAmount: quote.estimate.toAmount,
|
|
335
|
+
toAmountMin: quote.estimate.toAmountMin,
|
|
336
|
+
executionDuration: quote.estimate.executionDuration,
|
|
337
|
+
gasCosts: gasCosts.toString(),
|
|
338
|
+
feeCosts: feeCosts.toString(),
|
|
339
|
+
},
|
|
340
|
+
'LiFi quote received (fromAmount)',
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
return {
|
|
344
|
+
id: quote.id,
|
|
345
|
+
tool: quote.tool,
|
|
346
|
+
fromAmount: BigInt(quote.action.fromAmount),
|
|
347
|
+
toAmount: BigInt(quote.estimate.toAmount),
|
|
348
|
+
toAmountMin: BigInt(quote.estimate.toAmountMin),
|
|
349
|
+
executionDuration: quote.estimate.executionDuration,
|
|
350
|
+
gasCosts,
|
|
351
|
+
feeCosts,
|
|
352
|
+
route: quote, // Store full quote for conversion to route
|
|
353
|
+
requestParams: {
|
|
354
|
+
...params,
|
|
355
|
+
fromChain: lifiFromChain,
|
|
356
|
+
toChain: lifiToChain,
|
|
357
|
+
},
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Get a quote by specifying the amount to receive (reverse quote).
|
|
363
|
+
* Uses the LiFi REST API directly since the SDK doesn't support toAmount.
|
|
364
|
+
*/
|
|
365
|
+
private async quoteByReceivingAmount(
|
|
366
|
+
params: BridgeQuoteParams,
|
|
367
|
+
): Promise<BridgeQuote<LiFiStep>> {
|
|
368
|
+
this.logger.debug({ params }, 'Requesting LiFi quote by receiving amount');
|
|
369
|
+
|
|
370
|
+
const lifiFromChain = LiFiBridge.toLiFiChainId(params.fromChain);
|
|
371
|
+
const lifiToChain = LiFiBridge.toLiFiChainId(params.toChain);
|
|
372
|
+
|
|
373
|
+
const queryParams = new URLSearchParams({
|
|
374
|
+
fromChain: lifiFromChain.toString(),
|
|
375
|
+
toChain: lifiToChain.toString(),
|
|
376
|
+
fromToken: params.fromToken,
|
|
377
|
+
toToken: params.toToken,
|
|
378
|
+
toAmount: params.toAmount!.toString(),
|
|
379
|
+
fromAddress: params.fromAddress,
|
|
380
|
+
toAddress: params.toAddress ?? params.fromAddress,
|
|
381
|
+
slippage: (params.slippage ?? this.config.defaultSlippage ?? 0.005)
|
|
382
|
+
.toFixed(4)
|
|
383
|
+
.replace(/\.?0+$/, ''),
|
|
384
|
+
order: 'CHEAPEST',
|
|
385
|
+
integrator: this.config.integrator,
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
if (this.config.apiKey) {
|
|
389
|
+
queryParams.set('apiKey', this.config.apiKey);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const url = `${LIFI_API_BASE}/quote/toAmount?${queryParams.toString()}`;
|
|
393
|
+
this.logger.debug(
|
|
394
|
+
{ url: url.replace(/apiKey=[^&]+/, 'apiKey=***') },
|
|
395
|
+
'Fetching LiFi toAmount quote',
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
const response = await fetch(url);
|
|
399
|
+
if (!response.ok) {
|
|
400
|
+
const errorBody = await response.text();
|
|
401
|
+
throw new Error(
|
|
402
|
+
`LiFi toAmount quote failed: ${response.status} ${response.statusText} - ${errorBody}`,
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const quote: LiFiStep = await response.json();
|
|
407
|
+
const { gasCosts, feeCosts } = this.extractCosts(quote);
|
|
408
|
+
|
|
409
|
+
this.logger.info(
|
|
410
|
+
{
|
|
411
|
+
quoteId: quote.id,
|
|
412
|
+
tool: quote.tool,
|
|
413
|
+
fromAmount: quote.action.fromAmount,
|
|
414
|
+
toAmount: quote.estimate.toAmount,
|
|
415
|
+
toAmountMin: quote.estimate.toAmountMin,
|
|
416
|
+
executionDuration: quote.estimate.executionDuration,
|
|
417
|
+
gasCosts: gasCosts.toString(),
|
|
418
|
+
feeCosts: feeCosts.toString(),
|
|
419
|
+
},
|
|
420
|
+
'LiFi quote received (toAmount)',
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
return {
|
|
424
|
+
id: quote.id,
|
|
425
|
+
tool: quote.tool,
|
|
426
|
+
fromAmount: BigInt(quote.action.fromAmount),
|
|
427
|
+
toAmount: BigInt(quote.estimate.toAmount),
|
|
428
|
+
toAmountMin: BigInt(quote.estimate.toAmountMin),
|
|
429
|
+
executionDuration: quote.estimate.executionDuration,
|
|
430
|
+
gasCosts,
|
|
431
|
+
feeCosts,
|
|
432
|
+
route: quote, // Store full quote for conversion to route
|
|
433
|
+
requestParams: {
|
|
434
|
+
...params,
|
|
435
|
+
fromChain: lifiFromChain,
|
|
436
|
+
toChain: lifiToChain,
|
|
437
|
+
},
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Extract gas and fee costs from a LiFi quote response.
|
|
443
|
+
* - gasCosts: Sum of all gas costs (transaction fees)
|
|
444
|
+
* - feeCosts: Sum of non-included fee costs (protocol fees not deducted from amount)
|
|
445
|
+
*/
|
|
446
|
+
private extractCosts(quote: LiFiStep): {
|
|
447
|
+
gasCosts: bigint;
|
|
448
|
+
feeCosts: bigint;
|
|
449
|
+
} {
|
|
450
|
+
let gasCosts = 0n;
|
|
451
|
+
let feeCosts = 0n;
|
|
452
|
+
|
|
453
|
+
// Sum up gas costs
|
|
454
|
+
if (quote.estimate.gasCosts) {
|
|
455
|
+
for (const cost of quote.estimate.gasCosts) {
|
|
456
|
+
gasCosts += BigInt(cost.amount);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Sum up non-included fee costs
|
|
461
|
+
// (included fees are already deducted from toAmount, so we only count non-included)
|
|
462
|
+
if (quote.estimate.feeCosts) {
|
|
463
|
+
for (const cost of quote.estimate.feeCosts) {
|
|
464
|
+
if (!cost.included) {
|
|
465
|
+
feeCosts += BigInt(cost.amount);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return { gasCosts, feeCosts };
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Execute a bridge transfer using the SDK.
|
|
475
|
+
* Handles approvals, transaction signing, and execution automatically.
|
|
476
|
+
*
|
|
477
|
+
* @param quote - Quote obtained from quote()
|
|
478
|
+
* @param privateKeys - Private keys keyed by protocol type for signing transactions
|
|
479
|
+
*/
|
|
480
|
+
async execute(
|
|
481
|
+
quote: BridgeQuote<LiFiStep>,
|
|
482
|
+
privateKeys: Partial<Record<ProtocolType, string>>,
|
|
483
|
+
): Promise<BridgeTransferResult> {
|
|
484
|
+
this.initialize();
|
|
485
|
+
|
|
486
|
+
// Convert quote to route for execution
|
|
487
|
+
const route = convertQuoteToRoute(quote.route);
|
|
488
|
+
|
|
489
|
+
this.validateRouteAgainstRequest(route, quote.requestParams);
|
|
490
|
+
|
|
491
|
+
const fromChain = route.fromChainId;
|
|
492
|
+
const toChain = route.toChainId;
|
|
493
|
+
const fromProtocol = this.getProtocolTypeForChainId(fromChain);
|
|
494
|
+
assert(
|
|
495
|
+
privateKeys[fromProtocol ?? ProtocolType.Ethereum],
|
|
496
|
+
`Missing private key for source chain protocol ${fromProtocol ?? ProtocolType.Ethereum}`,
|
|
497
|
+
);
|
|
498
|
+
|
|
499
|
+
this.logger.info(
|
|
500
|
+
{
|
|
501
|
+
quoteId: quote.id,
|
|
502
|
+
tool: quote.tool,
|
|
503
|
+
fromChain,
|
|
504
|
+
toChain,
|
|
505
|
+
fromAmount: quote.fromAmount.toString(),
|
|
506
|
+
},
|
|
507
|
+
'Executing LiFi bridge transfer',
|
|
508
|
+
);
|
|
509
|
+
|
|
510
|
+
const fromRpcUrl = this.getRpcUrlForChainId(fromChain);
|
|
511
|
+
|
|
512
|
+
let release!: () => void;
|
|
513
|
+
const acquired = new Promise<void>((resolve) => {
|
|
514
|
+
release = resolve;
|
|
515
|
+
});
|
|
516
|
+
const prev = this._executeLock;
|
|
517
|
+
this._executeLock = acquired;
|
|
518
|
+
await prev;
|
|
519
|
+
|
|
520
|
+
let txHash: string | undefined;
|
|
521
|
+
let executedRoute!: RouteExtended;
|
|
522
|
+
|
|
523
|
+
try {
|
|
524
|
+
this.configureLiFiProviders(privateKeys, fromChain, fromRpcUrl);
|
|
525
|
+
|
|
526
|
+
this.logger.debug(
|
|
527
|
+
{
|
|
528
|
+
fromChain,
|
|
529
|
+
protocols: Object.keys(privateKeys),
|
|
530
|
+
},
|
|
531
|
+
'Configured LiFi providers for route execution',
|
|
532
|
+
);
|
|
533
|
+
|
|
534
|
+
// Execute route with update callbacks
|
|
535
|
+
executedRoute = await executeRoute(route, {
|
|
536
|
+
// Update callback for route progress
|
|
537
|
+
updateRouteHook: (updatedRoute: RouteExtended) => {
|
|
538
|
+
this.logger.debug(
|
|
539
|
+
{ step: updatedRoute.steps[0]?.id },
|
|
540
|
+
'Route step updated',
|
|
541
|
+
);
|
|
542
|
+
|
|
543
|
+
// Extract txHash from execution if available (RouteExtended has LiFiStepExtended with execution)
|
|
544
|
+
const execution = updatedRoute.steps[0]?.execution;
|
|
545
|
+
if (execution?.process) {
|
|
546
|
+
for (const process of execution.process) {
|
|
547
|
+
if (process.txHash) {
|
|
548
|
+
txHash = process.txHash;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
},
|
|
553
|
+
// Auto-accept rate updates for rebalancing
|
|
554
|
+
acceptExchangeRateUpdateHook: async () => true,
|
|
555
|
+
});
|
|
556
|
+
} finally {
|
|
557
|
+
release();
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// Extract txHash from executed route if not captured in callbacks
|
|
561
|
+
if (!txHash) {
|
|
562
|
+
const execution = executedRoute.steps[0]?.execution;
|
|
563
|
+
if (execution?.process) {
|
|
564
|
+
for (const process of execution.process) {
|
|
565
|
+
if (process.txHash) {
|
|
566
|
+
txHash = process.txHash;
|
|
567
|
+
break;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
if (!txHash) {
|
|
574
|
+
throw new Error('No transaction hash found in executed route');
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
this.logger.info(
|
|
578
|
+
{ txHash, quoteId: quote.id },
|
|
579
|
+
'LiFi bridge transaction executed',
|
|
580
|
+
);
|
|
581
|
+
|
|
582
|
+
// Extract transfer ID if available (some bridges provide this)
|
|
583
|
+
let transferId: string | undefined;
|
|
584
|
+
const processes = executedRoute.steps[0]?.execution?.process;
|
|
585
|
+
const txInfo = processes?.find((p) => p.txHash === txHash);
|
|
586
|
+
if (txInfo && 'lifiExplorerLink' in txInfo) {
|
|
587
|
+
// Extract transfer ID from explorer link if available
|
|
588
|
+
const link = (txInfo as { lifiExplorerLink?: string }).lifiExplorerLink;
|
|
589
|
+
const match = link?.match(/\/tx\/([^/]+)/);
|
|
590
|
+
if (match) {
|
|
591
|
+
transferId = match[1];
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
return {
|
|
596
|
+
txHash,
|
|
597
|
+
fromChain,
|
|
598
|
+
toChain,
|
|
599
|
+
transferId,
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* Validate that the route returned by LiFi matches the original request parameters.
|
|
605
|
+
* Prevents execution against wrong chains, tokens, or recipients if the bridge API
|
|
606
|
+
* returns a route that diverges from what was originally requested.
|
|
607
|
+
*
|
|
608
|
+
* TODO: Layer 2 validation — validate transaction calldata in route.steps[].transactionRequest
|
|
609
|
+
* and route.steps[0].estimate.approvalAddress against a known whitelist.
|
|
610
|
+
*/
|
|
611
|
+
private validateRouteAgainstRequest(
|
|
612
|
+
route: Route,
|
|
613
|
+
requestParams: BridgeQuoteParams,
|
|
614
|
+
): void {
|
|
615
|
+
assert(
|
|
616
|
+
route.fromChainId === requestParams.fromChain,
|
|
617
|
+
`Route fromChainId ${route.fromChainId} does not match requested ${requestParams.fromChain}`,
|
|
618
|
+
);
|
|
619
|
+
assert(
|
|
620
|
+
route.toChainId === requestParams.toChain,
|
|
621
|
+
`Route toChainId ${route.toChainId} does not match requested ${requestParams.toChain}`,
|
|
622
|
+
);
|
|
623
|
+
assert(
|
|
624
|
+
this.addressesEqual(
|
|
625
|
+
route.fromToken.address,
|
|
626
|
+
requestParams.fromToken,
|
|
627
|
+
route.fromChainId,
|
|
628
|
+
),
|
|
629
|
+
`Route fromToken ${route.fromToken.address} does not match requested ${requestParams.fromToken}`,
|
|
630
|
+
);
|
|
631
|
+
assert(
|
|
632
|
+
this.addressesEqual(
|
|
633
|
+
route.toToken.address,
|
|
634
|
+
requestParams.toToken,
|
|
635
|
+
route.toChainId,
|
|
636
|
+
),
|
|
637
|
+
`Route toToken ${route.toToken.address} does not match requested ${requestParams.toToken}`,
|
|
638
|
+
);
|
|
639
|
+
const expectedToAddress =
|
|
640
|
+
requestParams.toAddress ?? requestParams.fromAddress;
|
|
641
|
+
assert(
|
|
642
|
+
this.addressesEqual(
|
|
643
|
+
route.toAddress ?? '',
|
|
644
|
+
expectedToAddress,
|
|
645
|
+
route.toChainId,
|
|
646
|
+
),
|
|
647
|
+
`Route toAddress ${route.toAddress} does not match requested ${expectedToAddress}`,
|
|
648
|
+
);
|
|
649
|
+
assert(
|
|
650
|
+
this.addressesEqual(
|
|
651
|
+
route.fromAddress ?? '',
|
|
652
|
+
requestParams.fromAddress,
|
|
653
|
+
route.fromChainId,
|
|
654
|
+
),
|
|
655
|
+
`Route fromAddress ${route.fromAddress} does not match requested ${requestParams.fromAddress}`,
|
|
656
|
+
);
|
|
657
|
+
const routeFromAmount = BigInt(route.fromAmount);
|
|
658
|
+
if (requestParams.fromAmount !== undefined) {
|
|
659
|
+
assert(
|
|
660
|
+
routeFromAmount === requestParams.fromAmount,
|
|
661
|
+
`Route fromAmount ${route.fromAmount} does not match requested ${requestParams.fromAmount}`,
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
if (requestParams.toAmount !== undefined) {
|
|
665
|
+
const routeToAmount = BigInt(route.toAmount);
|
|
666
|
+
assert(
|
|
667
|
+
routeToAmount >= requestParams.toAmount,
|
|
668
|
+
`Route toAmount ${route.toAmount} is less than requested ${requestParams.toAmount}`,
|
|
669
|
+
);
|
|
670
|
+
}
|
|
671
|
+
assert(routeFromAmount > 0n, 'Route fromAmount must be positive');
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Get the status of a bridge transfer.
|
|
676
|
+
* Uses SDK's built-in status tracking.
|
|
677
|
+
*/
|
|
678
|
+
async getStatus(
|
|
679
|
+
txHash: string,
|
|
680
|
+
fromChain: number,
|
|
681
|
+
toChain: number,
|
|
682
|
+
): Promise<BridgeTransferStatus> {
|
|
683
|
+
this.initialize();
|
|
684
|
+
|
|
685
|
+
try {
|
|
686
|
+
const status = await getStatus({
|
|
687
|
+
txHash,
|
|
688
|
+
fromChain: LiFiBridge.toLiFiChainId(fromChain),
|
|
689
|
+
toChain: LiFiBridge.toLiFiChainId(toChain),
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
switch (status.status) {
|
|
693
|
+
case 'DONE': {
|
|
694
|
+
// receiving can be PendingReceivingInfo (only chainId) or ExtendedTransactionInfo (has txHash, amount)
|
|
695
|
+
const receiving = status.receiving;
|
|
696
|
+
const receivingTxHash =
|
|
697
|
+
receiving && 'txHash' in receiving ? (receiving.txHash ?? '') : '';
|
|
698
|
+
const receivedAmount =
|
|
699
|
+
receiving && 'amount' in receiving
|
|
700
|
+
? BigInt(receiving.amount ?? '0')
|
|
701
|
+
: BigInt(0);
|
|
702
|
+
return {
|
|
703
|
+
status: 'complete',
|
|
704
|
+
receivingTxHash,
|
|
705
|
+
receivedAmount,
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
case 'FAILED':
|
|
710
|
+
return {
|
|
711
|
+
status: 'failed',
|
|
712
|
+
error: status.substatus,
|
|
713
|
+
};
|
|
714
|
+
|
|
715
|
+
case 'NOT_FOUND':
|
|
716
|
+
case 'INVALID':
|
|
717
|
+
return { status: 'not_found' };
|
|
718
|
+
|
|
719
|
+
case 'PENDING':
|
|
720
|
+
default:
|
|
721
|
+
return {
|
|
722
|
+
status: 'pending',
|
|
723
|
+
substatus: status.substatus,
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
} catch (error) {
|
|
727
|
+
this.logger.warn({ txHash, error }, 'Failed to get LiFi status');
|
|
728
|
+
return { status: 'not_found' };
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
}
|