@humanode-hyperlane/rebalancer 27.2.2-humanode.1 → 1029.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bridges/LiFiBridge.d.ts +3 -3
- package/dist/bridges/LiFiBridge.d.ts.map +1 -1
- package/dist/bridges/LiFiBridge.js +66 -55
- package/dist/bridges/LiFiBridge.js.map +1 -1
- package/dist/bridges/LiFiBridge.test.js +213 -0
- package/dist/bridges/LiFiBridge.test.js.map +1 -1
- package/dist/config/RebalancerConfig.d.ts +4 -3
- package/dist/config/RebalancerConfig.d.ts.map +1 -1
- package/dist/config/RebalancerConfig.js +7 -5
- package/dist/config/RebalancerConfig.js.map +1 -1
- package/dist/config/RebalancerConfig.test.js +278 -2
- package/dist/config/RebalancerConfig.test.js.map +1 -1
- package/dist/config/types.d.ts +707 -2664
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config/types.js +62 -7
- package/dist/config/types.js.map +1 -1
- package/dist/core/InventoryRebalancer.d.ts +13 -19
- package/dist/core/InventoryRebalancer.d.ts.map +1 -1
- package/dist/core/InventoryRebalancer.js +404 -276
- package/dist/core/InventoryRebalancer.js.map +1 -1
- package/dist/core/InventoryRebalancer.test.js +814 -25
- package/dist/core/InventoryRebalancer.test.js.map +1 -1
- package/dist/core/Rebalancer.d.ts.map +1 -1
- package/dist/core/Rebalancer.js +12 -6
- package/dist/core/Rebalancer.js.map +1 -1
- package/dist/core/Rebalancer.test.js +51 -0
- package/dist/core/Rebalancer.test.js.map +1 -1
- package/dist/core/RebalancerOrchestrator.d.ts.map +1 -1
- package/dist/core/RebalancerOrchestrator.test.js +0 -1
- package/dist/core/RebalancerOrchestrator.test.js.map +1 -1
- package/dist/core/RebalancerService.d.ts.map +1 -1
- package/dist/core/RebalancerService.js +3 -2
- package/dist/core/RebalancerService.js.map +1 -1
- package/dist/core/RebalancerService.test.js +24 -0
- package/dist/core/RebalancerService.test.js.map +1 -1
- package/dist/e2e/fixtures/routes.d.ts +3 -3
- package/dist/e2e/fixtures/routes.d.ts.map +1 -1
- package/dist/e2e/harness/BaseLocalDeploymentManager.d.ts.map +1 -1
- package/dist/e2e/harness/BaseLocalDeploymentManager.js +3 -2
- package/dist/e2e/harness/BaseLocalDeploymentManager.js.map +1 -1
- package/dist/e2e/harness/Erc20InventoryLocalDeploymentManager.d.ts.map +1 -1
- package/dist/e2e/harness/Erc20InventoryLocalDeploymentManager.js.map +1 -1
- package/dist/e2e/harness/Erc20LocalDeploymentManager.d.ts.map +1 -1
- package/dist/e2e/harness/ForkIndexer.d.ts.map +1 -1
- package/dist/e2e/harness/ForkIndexer.js.map +1 -1
- package/dist/e2e/harness/LocalProvider.d.ts +3 -0
- package/dist/e2e/harness/LocalProvider.d.ts.map +1 -0
- package/dist/e2e/harness/LocalProvider.js +9 -0
- package/dist/e2e/harness/LocalProvider.js.map +1 -0
- package/dist/e2e/harness/LocalProvider.test.d.ts +2 -0
- package/dist/e2e/harness/LocalProvider.test.d.ts.map +1 -0
- package/dist/e2e/harness/LocalProvider.test.js +14 -0
- package/dist/e2e/harness/LocalProvider.test.js.map +1 -0
- package/dist/e2e/harness/MixedLocalDeploymentManager.d.ts.map +1 -1
- package/dist/e2e/harness/MixedLocalDeploymentManager.js.map +1 -1
- package/dist/e2e/harness/MockExplorerClient.d.ts.map +1 -1
- package/dist/e2e/harness/MockExplorerClient.js.map +1 -1
- package/dist/e2e/harness/MockExternalBridge.d.ts.map +1 -1
- package/dist/e2e/harness/MockExternalBridge.js.map +1 -1
- package/dist/e2e/harness/NativeLocalDeploymentManager.d.ts.map +1 -1
- package/dist/e2e/harness/NativeLocalDeploymentManager.js +2 -1
- package/dist/e2e/harness/NativeLocalDeploymentManager.js.map +1 -1
- package/dist/e2e/harness/SnapshotHelper.d.ts.map +1 -1
- package/dist/e2e/harness/SnapshotHelper.js +3 -5
- package/dist/e2e/harness/SnapshotHelper.js.map +1 -1
- package/dist/e2e/harness/TestHelpers.js +1 -2
- package/dist/e2e/harness/TestHelpers.js.map +1 -1
- package/dist/e2e/harness/TestRebalancer.d.ts +5 -1
- package/dist/e2e/harness/TestRebalancer.d.ts.map +1 -1
- package/dist/e2e/harness/TestRebalancer.js +14 -4
- package/dist/e2e/harness/TestRebalancer.js.map +1 -1
- package/dist/e2e/relayer-denylist.e2e-test.d.ts +2 -0
- package/dist/e2e/relayer-denylist.e2e-test.d.ts.map +1 -0
- package/dist/e2e/relayer-denylist.e2e-test.js +388 -0
- package/dist/e2e/relayer-denylist.e2e-test.js.map +1 -0
- package/dist/factories/RebalancerContextFactory.d.ts.map +1 -1
- package/dist/factories/RebalancerContextFactory.js +61 -31
- package/dist/factories/RebalancerContextFactory.js.map +1 -1
- package/dist/factories/RebalancerContextFactory.test.js +336 -5
- package/dist/factories/RebalancerContextFactory.test.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/interfaces/IRebalancer.d.ts +4 -2
- package/dist/interfaces/IRebalancer.d.ts.map +1 -1
- package/dist/metrics/Metrics.d.ts.map +1 -1
- package/dist/metrics/Metrics.js +1 -1
- package/dist/metrics/Metrics.js.map +1 -1
- package/dist/metrics/PriceGetter.d.ts +1 -0
- package/dist/metrics/PriceGetter.d.ts.map +1 -1
- package/dist/metrics/PriceGetter.js +14 -4
- package/dist/metrics/PriceGetter.js.map +1 -1
- package/dist/metrics/PriceGetter.test.d.ts +2 -0
- package/dist/metrics/PriceGetter.test.d.ts.map +1 -0
- package/dist/metrics/PriceGetter.test.js +84 -0
- package/dist/metrics/PriceGetter.test.js.map +1 -0
- package/dist/metrics/scripts/metrics.d.ts +4 -4
- package/dist/monitor/Monitor.d.ts +1 -0
- package/dist/monitor/Monitor.d.ts.map +1 -1
- package/dist/monitor/Monitor.js +55 -46
- package/dist/monitor/Monitor.js.map +1 -1
- package/dist/monitor/Monitor.test.d.ts +2 -0
- package/dist/monitor/Monitor.test.d.ts.map +1 -0
- package/dist/monitor/Monitor.test.js +346 -0
- package/dist/monitor/Monitor.test.js.map +1 -0
- package/dist/service.d.ts +1 -1
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +9 -5
- package/dist/service.js.map +1 -1
- package/dist/strategy/BaseStrategy.d.ts.map +1 -1
- package/dist/strategy/BaseStrategy.js +13 -11
- package/dist/strategy/BaseStrategy.js.map +1 -1
- package/dist/strategy/CollateralDeficitStrategy.d.ts.map +1 -1
- package/dist/strategy/CollateralDeficitStrategy.js +2 -2
- package/dist/strategy/CollateralDeficitStrategy.js.map +1 -1
- package/dist/strategy/CompositeStrategy.d.ts.map +1 -1
- package/dist/strategy/CompositeStrategy.js.map +1 -1
- package/dist/strategy/CompositeStrategy.test.js.map +1 -1
- package/dist/strategy/MinAmountStrategy.d.ts +1 -0
- package/dist/strategy/MinAmountStrategy.d.ts.map +1 -1
- package/dist/strategy/MinAmountStrategy.js +12 -8
- package/dist/strategy/MinAmountStrategy.js.map +1 -1
- package/dist/strategy/MinAmountStrategy.test.js +189 -2
- package/dist/strategy/MinAmountStrategy.test.js.map +1 -1
- package/dist/strategy/StrategyFactory.d.ts.map +1 -1
- package/dist/strategy/StrategyFactory.js.map +1 -1
- package/dist/strategy/WeightedStrategy.d.ts.map +1 -1
- package/dist/test/helpers.d.ts +11 -3
- package/dist/test/helpers.d.ts.map +1 -1
- package/dist/test/helpers.js +9 -11
- package/dist/test/helpers.js.map +1 -1
- package/dist/test/lifiMocks.d.ts +6 -6
- package/dist/test/lifiMocks.d.ts.map +1 -1
- package/dist/test/lifiMocks.js +5 -2
- package/dist/test/lifiMocks.js.map +1 -1
- package/dist/test/pendingTransferMiddlewareMocks.d.ts +17 -0
- package/dist/test/pendingTransferMiddlewareMocks.d.ts.map +1 -0
- package/dist/test/pendingTransferMiddlewareMocks.js +43 -0
- package/dist/test/pendingTransferMiddlewareMocks.js.map +1 -0
- package/dist/tracking/ActionTracker.d.ts +4 -0
- package/dist/tracking/ActionTracker.d.ts.map +1 -1
- package/dist/tracking/ActionTracker.js +76 -9
- package/dist/tracking/ActionTracker.js.map +1 -1
- package/dist/tracking/ActionTracker.test.js +418 -1
- package/dist/tracking/ActionTracker.test.js.map +1 -1
- package/dist/tracking/IActionTracker.d.ts +4 -2
- package/dist/tracking/IActionTracker.d.ts.map +1 -1
- package/dist/tracking/InflightContextAdapter.d.ts.map +1 -1
- package/dist/tracking/InflightContextAdapter.js.map +1 -1
- package/dist/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.d.ts +64 -0
- package/dist/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.d.ts.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.js +474 -0
- package/dist/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.js.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.test.d.ts +2 -0
- package/dist/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.test.d.ts.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.test.js +1168 -0
- package/dist/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.test.js.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.d.ts +61 -0
- package/dist/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.d.ts.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.js +160 -0
- package/dist/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.js.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.test.d.ts +2 -0
- package/dist/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.test.d.ts.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.test.js +369 -0
- package/dist/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.test.js.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.d.ts +60 -0
- package/dist/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.d.ts.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.js +288 -0
- package/dist/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.js.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.test.d.ts +2 -0
- package/dist/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.test.d.ts.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.test.js +606 -0
- package/dist/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.test.js.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.d.ts +25 -0
- package/dist/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.d.ts.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.js +41 -0
- package/dist/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.js.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.test.d.ts +2 -0
- package/dist/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.test.d.ts.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.test.js +141 -0
- package/dist/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.test.js.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/matchBody.d.ts +12 -0
- package/dist/tracking/pendingTransferMiddlewares/matchBody.d.ts.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/matchBody.js +29 -0
- package/dist/tracking/pendingTransferMiddlewares/matchBody.js.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/relayerDenylistRules.d.ts +59 -0
- package/dist/tracking/pendingTransferMiddlewares/relayerDenylistRules.d.ts.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/relayerDenylistRules.js +661 -0
- package/dist/tracking/pendingTransferMiddlewares/relayerDenylistRules.js.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/relayerDenylistRules.test.d.ts +2 -0
- package/dist/tracking/pendingTransferMiddlewares/relayerDenylistRules.test.d.ts.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/relayerDenylistRules.test.js +1093 -0
- package/dist/tracking/pendingTransferMiddlewares/relayerDenylistRules.test.js.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/types.d.ts +2 -0
- package/dist/tracking/pendingTransferMiddlewares/types.d.ts.map +1 -0
- package/dist/tracking/pendingTransferMiddlewares/types.js +4 -0
- package/dist/tracking/pendingTransferMiddlewares/types.js.map +1 -0
- package/dist/tracking/store/InMemoryStore.d.ts.map +1 -1
- package/dist/utils/ExplorerClient.d.ts +1 -0
- package/dist/utils/ExplorerClient.d.ts.map +1 -1
- package/dist/utils/ExplorerClient.js +8 -3
- package/dist/utils/ExplorerClient.js.map +1 -1
- package/dist/utils/ExplorerClient.test.js +25 -0
- package/dist/utils/ExplorerClient.test.js.map +1 -1
- package/dist/utils/balanceUtils.d.ts +7 -1
- package/dist/utils/balanceUtils.d.ts.map +1 -1
- package/dist/utils/balanceUtils.js +39 -1
- package/dist/utils/balanceUtils.js.map +1 -1
- package/dist/utils/balanceUtils.test.js +55 -1
- package/dist/utils/balanceUtils.test.js.map +1 -1
- package/dist/utils/blockTag.d.ts +5 -4
- package/dist/utils/blockTag.d.ts.map +1 -1
- package/dist/utils/blockTag.js +26 -10
- package/dist/utils/blockTag.js.map +1 -1
- package/dist/utils/blockTag.test.d.ts +2 -0
- package/dist/utils/blockTag.test.d.ts.map +1 -0
- package/dist/utils/blockTag.test.js +132 -0
- package/dist/utils/blockTag.test.js.map +1 -0
- package/dist/utils/bridgeUtils.js.map +1 -1
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/gasEstimation.d.ts +2 -1
- package/dist/utils/gasEstimation.d.ts.map +1 -1
- package/dist/utils/gasEstimation.js +16 -13
- package/dist/utils/gasEstimation.js.map +1 -1
- package/dist/utils/gasEstimation.test.d.ts +2 -0
- package/dist/utils/gasEstimation.test.d.ts.map +1 -0
- package/dist/utils/gasEstimation.test.js +104 -0
- package/dist/utils/gasEstimation.test.js.map +1 -0
- package/dist/utils/tokenUtils.d.ts.map +1 -1
- package/dist/utils/tokenUtils.js +5 -2
- package/dist/utils/tokenUtils.js.map +1 -1
- package/package.json +23 -22
- package/src/bridges/LiFiBridge.test.ts +270 -0
- package/src/bridges/LiFiBridge.ts +90 -71
- package/src/config/RebalancerConfig.test.ts +315 -1
- package/src/config/RebalancerConfig.ts +6 -3
- package/src/config/types.ts +98 -8
- package/src/core/InventoryRebalancer.test.ts +1092 -38
- package/src/core/InventoryRebalancer.ts +584 -351
- package/src/core/Rebalancer.test.ts +84 -0
- package/src/core/Rebalancer.ts +22 -6
- package/src/core/RebalancerOrchestrator.test.ts +0 -1
- package/src/core/RebalancerService.test.ts +35 -0
- package/src/core/RebalancerService.ts +3 -2
- package/src/e2e/harness/BaseLocalDeploymentManager.ts +3 -1
- package/src/e2e/harness/LocalProvider.test.ts +16 -0
- package/src/e2e/harness/LocalProvider.ts +10 -0
- package/src/e2e/harness/NativeLocalDeploymentManager.ts +2 -1
- package/src/e2e/harness/SnapshotHelper.ts +2 -5
- package/src/e2e/harness/TestHelpers.ts +3 -3
- package/src/e2e/harness/TestRebalancer.ts +21 -1
- package/src/e2e/relayer-denylist.e2e-test.ts +558 -0
- package/src/factories/RebalancerContextFactory.test.ts +489 -8
- package/src/factories/RebalancerContextFactory.ts +86 -36
- package/src/index.ts +2 -0
- package/src/interfaces/IRebalancer.ts +4 -1
- package/src/metrics/Metrics.ts +1 -0
- package/src/metrics/PriceGetter.test.ts +113 -0
- package/src/metrics/PriceGetter.ts +18 -3
- package/src/monitor/Monitor.test.ts +408 -0
- package/src/monitor/Monitor.ts +123 -97
- package/src/service.ts +14 -8
- package/src/strategy/BaseStrategy.ts +18 -15
- package/src/strategy/CollateralDeficitStrategy.ts +4 -3
- package/src/strategy/MinAmountStrategy.test.ts +238 -2
- package/src/strategy/MinAmountStrategy.ts +29 -17
- package/src/test/helpers.ts +13 -12
- package/src/test/lifiMocks.ts +5 -2
- package/src/test/pendingTransferMiddlewareMocks.ts +65 -0
- package/src/tracking/ActionTracker.test.ts +635 -3
- package/src/tracking/ActionTracker.ts +92 -12
- package/src/tracking/IActionTracker.ts +4 -2
- package/src/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.test.ts +1693 -0
- package/src/tracking/pendingTransferMiddlewares/BlacklistIsmMiddleware.ts +784 -0
- package/src/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.test.ts +554 -0
- package/src/tracking/pendingTransferMiddlewares/PendingTransferMiddleware.ts +233 -0
- package/src/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.test.ts +894 -0
- package/src/tracking/pendingTransferMiddlewares/RelayerDenylistMiddleware.ts +404 -0
- package/src/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.test.ts +205 -0
- package/src/tracking/pendingTransferMiddlewares/buildPendingTransferMiddlewares.ts +89 -0
- package/src/tracking/pendingTransferMiddlewares/fixtures/mainnet-denylist.json +3556 -0
- package/src/tracking/pendingTransferMiddlewares/matchBody.ts +43 -0
- package/src/tracking/pendingTransferMiddlewares/relayerDenylistRules.test.ts +1220 -0
- package/src/tracking/pendingTransferMiddlewares/relayerDenylistRules.ts +934 -0
- package/src/tracking/pendingTransferMiddlewares/types.ts +3 -0
- package/src/utils/ExplorerClient.test.ts +42 -0
- package/src/utils/ExplorerClient.ts +14 -3
- package/src/utils/balanceUtils.test.ts +87 -1
- package/src/utils/balanceUtils.ts +73 -2
- package/src/utils/blockTag.test.ts +173 -0
- package/src/utils/blockTag.ts +35 -15
- package/src/utils/gasEstimation.test.ts +192 -0
- package/src/utils/gasEstimation.ts +17 -11
- package/src/utils/tokenUtils.ts +5 -2
|
@@ -160,6 +160,48 @@ describe('ExplorerClient', () => {
|
|
|
160
160
|
});
|
|
161
161
|
|
|
162
162
|
describe('getInflightRebalanceActions address encoding', () => {
|
|
163
|
+
it('skips empty bridge discovery and queries freshly for supplied bridges', async () => {
|
|
164
|
+
const client = new ExplorerClient(
|
|
165
|
+
'https://explorer.test',
|
|
166
|
+
() => ProtocolType.Ethereum,
|
|
167
|
+
);
|
|
168
|
+
const bridges: string[] = [];
|
|
169
|
+
const params = {
|
|
170
|
+
bridges,
|
|
171
|
+
routersByDomain: { 1: '0x1234567890abcdef1234567890abcdef12345678' },
|
|
172
|
+
rebalancerAddress: '0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef',
|
|
173
|
+
};
|
|
174
|
+
fetchStub.rejects(new Error('offline'));
|
|
175
|
+
expect(
|
|
176
|
+
await client.getInflightRebalanceActions(params, testLogger),
|
|
177
|
+
).to.deep.equal([]);
|
|
178
|
+
expect(fetchStub.called).to.be.false;
|
|
179
|
+
bridges.push('0x1234567890abcdef1234567890abcdef12345678');
|
|
180
|
+
await expect(
|
|
181
|
+
client.getInflightRebalanceActions(params, testLogger),
|
|
182
|
+
).to.be.rejectedWith('offline');
|
|
183
|
+
expect(fetchStub.calledOnce).to.be.true;
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('preserves HTTP failures for nonempty bridge discovery', async () => {
|
|
187
|
+
const client = new ExplorerClient(
|
|
188
|
+
'https://explorer.test',
|
|
189
|
+
() => ProtocolType.Ethereum,
|
|
190
|
+
);
|
|
191
|
+
fetchStub.resolves(new Response('unavailable', { status: 503 }));
|
|
192
|
+
await expect(
|
|
193
|
+
client.getInflightRebalanceActions(
|
|
194
|
+
{
|
|
195
|
+
bridges: ['0x1234567890abcdef1234567890abcdef12345678'],
|
|
196
|
+
routersByDomain: {},
|
|
197
|
+
rebalancerAddress: '0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef',
|
|
198
|
+
},
|
|
199
|
+
testLogger,
|
|
200
|
+
),
|
|
201
|
+
).to.be.rejectedWith('Explorer query failed: 503');
|
|
202
|
+
expect(fetchStub.calledOnce).to.be.true;
|
|
203
|
+
});
|
|
204
|
+
|
|
163
205
|
it('encodes mixed EVM+Solana routersByDomain in originTxRecipients', async () => {
|
|
164
206
|
const evmAddr = '0x5a0e13290ec57f5e9031d01d03c6a40029cc24ea';
|
|
165
207
|
const solAddr = 'E5rVV8zXwtc4TKGypCJvSBaYbgxa4XaYg5MS6N9QGdeo';
|
|
@@ -6,6 +6,10 @@ import {
|
|
|
6
6
|
ProtocolType,
|
|
7
7
|
} from '@humanode-hyperlane/utils';
|
|
8
8
|
|
|
9
|
+
const HEX_PREFIX_REGEX = /^0x/i;
|
|
10
|
+
|
|
11
|
+
export const DEFAULT_INFLIGHT_USER_TRANSFER_LIMIT = 100;
|
|
12
|
+
|
|
9
13
|
export type InflightRebalanceQueryParams = {
|
|
10
14
|
bridges: string[];
|
|
11
15
|
routersByDomain: Record<number, string>; // Domain ID → router address (derive routers and domains from this)
|
|
@@ -71,9 +75,9 @@ export class ExplorerClient implements IExplorerClient {
|
|
|
71
75
|
domain !== undefined ? this.getProtocol(domain) : undefined;
|
|
72
76
|
if (protocol && !isEVMLike(protocol)) {
|
|
73
77
|
const hex = addressToByteHexString(addr, protocol);
|
|
74
|
-
return hex.replace(
|
|
78
|
+
return hex.replace(HEX_PREFIX_REGEX, '\\x').toLowerCase();
|
|
75
79
|
}
|
|
76
|
-
return addr.replace(
|
|
80
|
+
return addr.replace(HEX_PREFIX_REGEX, '\\x').toLowerCase();
|
|
77
81
|
}
|
|
78
82
|
|
|
79
83
|
/**
|
|
@@ -230,7 +234,11 @@ export class ExplorerClient implements IExplorerClient {
|
|
|
230
234
|
params: UserTransferQueryParams,
|
|
231
235
|
logger: Logger,
|
|
232
236
|
): Promise<ExplorerMessage[]> {
|
|
233
|
-
const {
|
|
237
|
+
const {
|
|
238
|
+
routersByDomain,
|
|
239
|
+
excludeTxSenders,
|
|
240
|
+
limit = DEFAULT_INFLIGHT_USER_TRANSFER_LIMIT,
|
|
241
|
+
} = params;
|
|
234
242
|
|
|
235
243
|
// Derive routers and domains from routersByDomain
|
|
236
244
|
const routerEntries = Object.entries(routersByDomain);
|
|
@@ -337,6 +345,9 @@ export class ExplorerClient implements IExplorerClient {
|
|
|
337
345
|
limit = 100,
|
|
338
346
|
} = params;
|
|
339
347
|
|
|
348
|
+
// Both sender and recipient must belong to bridges; an empty set cannot match.
|
|
349
|
+
if (bridges.length === 0) return [];
|
|
350
|
+
|
|
340
351
|
// Derive routers and domains from routersByDomain
|
|
341
352
|
const routerEntries = Object.entries(routersByDomain);
|
|
342
353
|
const domains = Object.keys(routersByDomain).map(Number);
|
|
@@ -10,7 +10,15 @@ import {
|
|
|
10
10
|
|
|
11
11
|
import { type MonitorEvent } from '../interfaces/IMonitor.js';
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
alignLocalToCanonical,
|
|
15
|
+
denormalizeToLocal,
|
|
16
|
+
getRawBalances,
|
|
17
|
+
isIdentityScale,
|
|
18
|
+
getTokenScale,
|
|
19
|
+
normalizeConfiguredAmount,
|
|
20
|
+
normalizeToCanonical,
|
|
21
|
+
} from './balanceUtils.js';
|
|
14
22
|
|
|
15
23
|
const testLogger = pino({ level: 'silent' });
|
|
16
24
|
|
|
@@ -49,6 +57,16 @@ describe('getRawBalances', () => {
|
|
|
49
57
|
});
|
|
50
58
|
});
|
|
51
59
|
|
|
60
|
+
it('should normalize bridged supply to canonical units when token has scale', () => {
|
|
61
|
+
token.scale = { numerator: 1, denominator: 1_000_000_000_000 };
|
|
62
|
+
tokenBridgedSupply = 1_000_000_000_000_000_000n;
|
|
63
|
+
event.tokensInfo[0].bridgedSupply = tokenBridgedSupply;
|
|
64
|
+
|
|
65
|
+
expect(getRawBalances(chains, event, testLogger)).to.deep.equal({
|
|
66
|
+
mainnet: 1_000_000n,
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
52
70
|
it('should return the bridged supply for the token (EvmHypNative)', () => {
|
|
53
71
|
token.standard = TokenStandard.EvmHypNative;
|
|
54
72
|
|
|
@@ -77,3 +95,71 @@ describe('getRawBalances', () => {
|
|
|
77
95
|
);
|
|
78
96
|
});
|
|
79
97
|
});
|
|
98
|
+
|
|
99
|
+
describe('scale helpers', () => {
|
|
100
|
+
const token = {
|
|
101
|
+
decimals: 18,
|
|
102
|
+
scale: { numerator: 1, denominator: 1_000_000_000_000 },
|
|
103
|
+
} as unknown as Token;
|
|
104
|
+
|
|
105
|
+
it('normalizes local amount to canonical amount', () => {
|
|
106
|
+
expect(normalizeToCanonical(1_000_000_000_000_000_000n, token)).to.equal(
|
|
107
|
+
1_000_000n,
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('denormalizes canonical amount to local amount', () => {
|
|
112
|
+
expect(denormalizeToLocal(1_000_000n, token)).to.equal(
|
|
113
|
+
1_000_000_000_000_000_000n,
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('aligns local amount to exact canonical progress', () => {
|
|
118
|
+
expect(alignLocalToCanonical(999_999_999_999n, token)).to.deep.equal({
|
|
119
|
+
localAmount: 0n,
|
|
120
|
+
messageAmount: 0n,
|
|
121
|
+
});
|
|
122
|
+
expect(
|
|
123
|
+
alignLocalToCanonical(1_000_000_000_000_500_000n, token),
|
|
124
|
+
).to.deep.equal({
|
|
125
|
+
localAmount: 1_000_000_000_000_000_000n,
|
|
126
|
+
messageAmount: 1_000_000n,
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('returns identity scale when scale is undefined', () => {
|
|
131
|
+
expect(getTokenScale({} as Token)).to.deep.equal({
|
|
132
|
+
numerator: 1n,
|
|
133
|
+
denominator: 1n,
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('treats equivalent ratios as identity scale', () => {
|
|
138
|
+
expect(isIdentityScale({} as Token)).to.be.true;
|
|
139
|
+
expect(
|
|
140
|
+
isIdentityScale({
|
|
141
|
+
scale: { numerator: 1, denominator: 1 },
|
|
142
|
+
} as Token),
|
|
143
|
+
).to.be.true;
|
|
144
|
+
expect(
|
|
145
|
+
isIdentityScale({
|
|
146
|
+
scale: { numerator: 5, denominator: 5 },
|
|
147
|
+
} as Token),
|
|
148
|
+
).to.be.true;
|
|
149
|
+
expect(
|
|
150
|
+
isIdentityScale({
|
|
151
|
+
scale: { numerator: 10, denominator: 20 },
|
|
152
|
+
} as Token),
|
|
153
|
+
).to.be.false;
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('normalizes configured amount using token decimals and scale', () => {
|
|
157
|
+
expect(normalizeConfiguredAmount('1', token)).to.equal(1_000_000n);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('normalizes sub-unit local dust to zero canonical amount', () => {
|
|
161
|
+
expect(normalizeConfiguredAmount('0.000000000000000001', token)).to.equal(
|
|
162
|
+
0n,
|
|
163
|
+
);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
@@ -1,12 +1,68 @@
|
|
|
1
1
|
import { type Logger } from 'pino';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
type IToken,
|
|
5
|
+
alignLocalAmountToMessage,
|
|
6
|
+
messageAmountFromLocal,
|
|
7
|
+
minLocalAmountForMessage,
|
|
8
|
+
normalizeScale,
|
|
9
|
+
scalesEqual,
|
|
10
|
+
type ChainName,
|
|
11
|
+
type NormalizedScale,
|
|
12
|
+
type ScaleAlignment,
|
|
13
|
+
type Token,
|
|
14
|
+
} from '@humanode-hyperlane/sdk';
|
|
15
|
+
import { toWei } from '@humanode-hyperlane/utils';
|
|
4
16
|
|
|
5
17
|
import { type MonitorEvent } from '../interfaces/IMonitor.js';
|
|
6
18
|
import { type RawBalances } from '../interfaces/IStrategy.js';
|
|
7
19
|
|
|
8
20
|
import { isCollateralizedTokenEligibleForRebalancing } from './tokenUtils.js';
|
|
9
21
|
|
|
22
|
+
export function getTokenScale(token: Token): NormalizedScale {
|
|
23
|
+
return normalizeScale(token.scale);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function isIdentityScale(token: Token): boolean {
|
|
27
|
+
return scalesEqual(token.scale, undefined);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function normalizeToCanonical(
|
|
31
|
+
localAmount: bigint,
|
|
32
|
+
tokenOrScale: Token | IToken | NormalizedScale,
|
|
33
|
+
): bigint {
|
|
34
|
+
return messageAmountFromLocal(localAmount, getScaleInput(tokenOrScale));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function denormalizeToLocal(
|
|
38
|
+
canonicalAmount: bigint,
|
|
39
|
+
tokenOrScale: Token | IToken | NormalizedScale,
|
|
40
|
+
): bigint {
|
|
41
|
+
// Use ceil semantics so the returned local amount always produces at least
|
|
42
|
+
// the requested canonical progress once the token router floors on outbound.
|
|
43
|
+
return minLocalAmountForMessage(canonicalAmount, getScaleInput(tokenOrScale));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function alignLocalToCanonical(
|
|
47
|
+
localAmount: bigint,
|
|
48
|
+
tokenOrScale: Token | IToken | NormalizedScale,
|
|
49
|
+
): ScaleAlignment {
|
|
50
|
+
return alignLocalAmountToMessage(localAmount, getScaleInput(tokenOrScale));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function normalizeConfiguredAmount(
|
|
54
|
+
amount: string | number,
|
|
55
|
+
token: Token,
|
|
56
|
+
): bigint {
|
|
57
|
+
return normalizeToCanonical(BigInt(toWei(amount, token.decimals)), token);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function getScaleInput(tokenOrScale: Token | IToken | NormalizedScale) {
|
|
61
|
+
return 'numerator' in tokenOrScale && 'denominator' in tokenOrScale
|
|
62
|
+
? tokenOrScale
|
|
63
|
+
: tokenOrScale.scale;
|
|
64
|
+
}
|
|
65
|
+
|
|
10
66
|
/**
|
|
11
67
|
* Returns the raw balances required by the strategies from the monitor event
|
|
12
68
|
* @param chains - The chains that should be included in the raw balances (e.g. the chains in the rebalancer config)
|
|
@@ -58,7 +114,22 @@ export function getRawBalances(
|
|
|
58
114
|
);
|
|
59
115
|
}
|
|
60
116
|
|
|
61
|
-
|
|
117
|
+
const normalizedBalance = normalizeToCanonical(bridgedSupply, token);
|
|
118
|
+
balances[token.chainName] = normalizedBalance;
|
|
119
|
+
|
|
120
|
+
if (!isIdentityScale(token)) {
|
|
121
|
+
logger.debug(
|
|
122
|
+
{
|
|
123
|
+
context: getRawBalances.name,
|
|
124
|
+
chain: token.chainName,
|
|
125
|
+
tokenSymbol: token.symbol,
|
|
126
|
+
bridgedSupply: bridgedSupply.toString(),
|
|
127
|
+
normalizedBalance: normalizedBalance.toString(),
|
|
128
|
+
scale: getTokenScale(token),
|
|
129
|
+
},
|
|
130
|
+
'Normalized bridged supply to canonical units',
|
|
131
|
+
);
|
|
132
|
+
}
|
|
62
133
|
}
|
|
63
134
|
|
|
64
135
|
return balances;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import chai, { expect } from 'chai';
|
|
2
|
+
import chaiAsPromised from 'chai-as-promised';
|
|
3
|
+
import { providers } from 'ethers';
|
|
4
|
+
import { pino } from 'pino';
|
|
5
|
+
import Sinon from 'sinon';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
type ChainMetadata,
|
|
9
|
+
EthJsonRpcBlockParameterTag,
|
|
10
|
+
MultiProtocolProvider,
|
|
11
|
+
} from '@humanode-hyperlane/sdk';
|
|
12
|
+
import { ProtocolType } from '@humanode-hyperlane/utils';
|
|
13
|
+
|
|
14
|
+
import { getConfirmedBlockTag } from './blockTag.js';
|
|
15
|
+
|
|
16
|
+
chai.use(chaiAsPromised);
|
|
17
|
+
|
|
18
|
+
const tronMetadata: ChainMetadata = {
|
|
19
|
+
protocol: ProtocolType.Tron,
|
|
20
|
+
name: 'tron',
|
|
21
|
+
chainId: 728126428,
|
|
22
|
+
domainId: 728126428,
|
|
23
|
+
rpcUrls: [{ http: 'http://unused' }],
|
|
24
|
+
blocks: { reorgPeriod: 20, confirmations: 1 },
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const logger = pino({ level: 'silent' });
|
|
28
|
+
|
|
29
|
+
describe('getConfirmedBlockTag', () => {
|
|
30
|
+
let mpp: Sinon.SinonStubbedInstance<MultiProtocolProvider>;
|
|
31
|
+
let provider: Sinon.SinonStubbedInstance<providers.JsonRpcProvider>;
|
|
32
|
+
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
mpp = Sinon.createStubInstance(MultiProtocolProvider);
|
|
35
|
+
provider = Sinon.createStubInstance(providers.JsonRpcProvider);
|
|
36
|
+
mpp.getChainMetadata.returns(tronMetadata);
|
|
37
|
+
mpp.getEthersV5Provider.returns(provider);
|
|
38
|
+
provider.send.resolves('0x64');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
afterEach(() => {
|
|
42
|
+
Sinon.restore();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('returns a confirmed block number for a numeric Tron reorg period', async () => {
|
|
46
|
+
expect(await getConfirmedBlockTag(mpp, 'tron')).to.equal(80);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('clamps a confirmed block number to zero', async () => {
|
|
50
|
+
provider.send.resolves('0x0a');
|
|
51
|
+
expect(await getConfirmedBlockTag(mpp, 'tron')).to.equal(0);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('uses the default numeric reorg period for Tron', async () => {
|
|
55
|
+
mpp.getChainMetadata.returns({ ...tronMetadata, blocks: undefined });
|
|
56
|
+
expect(await getConfirmedBlockTag(mpp, 'tron')).to.equal(68);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('returns undefined for a non-EVM-like chain without querying blocks', async () => {
|
|
60
|
+
mpp.getChainMetadata.returns({
|
|
61
|
+
...tronMetadata,
|
|
62
|
+
protocol: ProtocolType.Sealevel,
|
|
63
|
+
name: 'solana',
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
expect(await getConfirmedBlockTag(mpp, 'solana')).to.equal(undefined);
|
|
67
|
+
expect(mpp.getEthersV5Provider.notCalled).to.equal(true);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('preserves the optional Tron tag for unsupported named reorg periods', async () => {
|
|
71
|
+
const warn = Sinon.stub(logger, 'warn');
|
|
72
|
+
mpp.getChainMetadata.returns({
|
|
73
|
+
...tronMetadata,
|
|
74
|
+
blocks: {
|
|
75
|
+
reorgPeriod: EthJsonRpcBlockParameterTag.Finalized,
|
|
76
|
+
confirmations: 1,
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
expect(await getConfirmedBlockTag(mpp, 'tron', logger)).to.equal(undefined);
|
|
81
|
+
expect(mpp.getEthersV5Provider.notCalled).to.equal(true);
|
|
82
|
+
expect(warn.calledOnce).to.equal(true);
|
|
83
|
+
expect(warn.firstCall.args[1]).to.include(
|
|
84
|
+
'Tron does not support named block tags',
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('propagates failed numeric Tron block reads', async () => {
|
|
89
|
+
const failure = new Error('block unavailable');
|
|
90
|
+
const warn = Sinon.stub(logger, 'warn');
|
|
91
|
+
provider.send.rejects(failure);
|
|
92
|
+
|
|
93
|
+
await expect(getConfirmedBlockTag(mpp, 'tron', logger)).to.be.rejectedWith(
|
|
94
|
+
failure,
|
|
95
|
+
);
|
|
96
|
+
expect(warn.calledOnce).to.equal(true);
|
|
97
|
+
expect(warn.firstCall.args[1]).to.equal('Failed to get confirmed block');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('propagates provider lookup failures in numeric Tron mode', async () => {
|
|
101
|
+
const failure = new Error('provider unavailable');
|
|
102
|
+
mpp.getEthersV5Provider.throws(failure);
|
|
103
|
+
|
|
104
|
+
await expect(getConfirmedBlockTag(mpp, 'tron')).to.be.rejectedWith(failure);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
for (const invalidResponse of [
|
|
108
|
+
undefined,
|
|
109
|
+
null,
|
|
110
|
+
100,
|
|
111
|
+
'100',
|
|
112
|
+
'0x',
|
|
113
|
+
'0xgg',
|
|
114
|
+
'0x10000000000000000',
|
|
115
|
+
`0x${'f'.repeat(300)}`,
|
|
116
|
+
]) {
|
|
117
|
+
it(`rejects an invalid Tron block response (${typeof invalidResponse}): ${String(invalidResponse).slice(0, 32)}`, async () => {
|
|
118
|
+
provider.send.resolves(invalidResponse);
|
|
119
|
+
await expect(getConfirmedBlockTag(mpp, 'tron')).to.be.rejectedWith(
|
|
120
|
+
'Invalid',
|
|
121
|
+
);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
it('rejects an invalid numeric block from a non-JSON-RPC provider', async () => {
|
|
126
|
+
const fallbackProvider = Sinon.createStubInstance(providers.BaseProvider);
|
|
127
|
+
fallbackProvider.getBlockNumber.resolves(Number.NaN);
|
|
128
|
+
mpp.getEthersV5Provider.returns(fallbackProvider);
|
|
129
|
+
|
|
130
|
+
await expect(getConfirmedBlockTag(mpp, 'tron')).to.be.rejectedWith(
|
|
131
|
+
'Invalid block number for tron: NaN',
|
|
132
|
+
);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('preserves undefined on an Ethereum lookup failure', async () => {
|
|
136
|
+
mpp.getChainMetadata.returns({
|
|
137
|
+
...tronMetadata,
|
|
138
|
+
protocol: ProtocolType.Ethereum,
|
|
139
|
+
name: 'ethereum',
|
|
140
|
+
});
|
|
141
|
+
provider.send.rejects(new Error('block unavailable'));
|
|
142
|
+
|
|
143
|
+
expect(await getConfirmedBlockTag(mpp, 'ethereum')).to.equal(undefined);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('preserves Ethereum named block tags', async () => {
|
|
147
|
+
mpp.getChainMetadata.returns({
|
|
148
|
+
...tronMetadata,
|
|
149
|
+
protocol: ProtocolType.Ethereum,
|
|
150
|
+
name: 'ethereum',
|
|
151
|
+
blocks: {
|
|
152
|
+
reorgPeriod: EthJsonRpcBlockParameterTag.Finalized,
|
|
153
|
+
confirmations: 1,
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
expect(await getConfirmedBlockTag(mpp, 'ethereum')).to.equal(
|
|
158
|
+
EthJsonRpcBlockParameterTag.Finalized,
|
|
159
|
+
);
|
|
160
|
+
expect(mpp.getEthersV5Provider.notCalled).to.equal(true);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('preserves undefined on metadata lookup failures', async () => {
|
|
164
|
+
const failure = new Error('metadata unavailable');
|
|
165
|
+
mpp.getChainMetadata.throws(failure);
|
|
166
|
+
|
|
167
|
+
const warn = Sinon.stub(logger, 'warn');
|
|
168
|
+
expect(await getConfirmedBlockTag(mpp, 'tron', logger)).to.equal(undefined);
|
|
169
|
+
expect(warn.calledOnce).to.equal(true);
|
|
170
|
+
expect(warn.firstCall.args[1]).to.equal('Failed to get confirmed block');
|
|
171
|
+
expect(mpp.getEthersV5Provider.notCalled).to.equal(true);
|
|
172
|
+
});
|
|
173
|
+
});
|
package/src/utils/blockTag.ts
CHANGED
|
@@ -4,65 +4,85 @@ import { providers } from 'ethers';
|
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
EthJsonRpcBlockParameterTag,
|
|
7
|
-
MultiProtocolProvider,
|
|
7
|
+
type MultiProtocolProvider,
|
|
8
8
|
} from '@humanode-hyperlane/sdk';
|
|
9
|
-
import { ProtocolType } from '@humanode-hyperlane/utils';
|
|
9
|
+
import { isEVMLike, ProtocolType } from '@humanode-hyperlane/utils';
|
|
10
10
|
import type { ConfirmedBlockTag } from '../interfaces/IMonitor.js';
|
|
11
11
|
|
|
12
|
+
const HEX_BLOCK_NUMBER_REGEX = /^0x[0-9a-fA-F]+$/;
|
|
13
|
+
|
|
12
14
|
/**
|
|
13
15
|
* Get the confirmed block tag for a chain, accounting for reorg period.
|
|
14
16
|
* Returns a block number that is safe from reorgs, or a named tag like 'finalized'.
|
|
15
17
|
*
|
|
16
|
-
* @param multiProvider -
|
|
18
|
+
* @param multiProvider - Provider registry with chain metadata and ethers access
|
|
17
19
|
* @param chainName - Name of the chain
|
|
18
20
|
* @param logger - Optional logger for warnings
|
|
19
|
-
* @returns Confirmed block tag
|
|
21
|
+
* @returns Confirmed block tag, or undefined for optional tags, metadata errors or failed Ethereum lookups.
|
|
22
|
+
* @throws If a numeric Tron block lookup fails.
|
|
20
23
|
*/
|
|
21
24
|
export async function getConfirmedBlockTag(
|
|
22
|
-
multiProvider:
|
|
25
|
+
multiProvider: Pick<
|
|
26
|
+
MultiProtocolProvider,
|
|
27
|
+
'getChainMetadata' | 'getEthersV5Provider'
|
|
28
|
+
>,
|
|
23
29
|
chainName: string,
|
|
24
30
|
logger?: Logger,
|
|
25
31
|
): Promise<ConfirmedBlockTag> {
|
|
32
|
+
let numericTronLookup = false;
|
|
26
33
|
try {
|
|
27
34
|
const metadata = multiProvider.getChainMetadata(chainName);
|
|
28
35
|
|
|
29
|
-
// Only EVM chains support block tag queries
|
|
30
|
-
|
|
36
|
+
// Only EVM-like chains support block tag queries (e.g., Ethereum, Tron).
|
|
37
|
+
// Tron uses TronJsonRpcProvider which supports eth_blockNumber
|
|
38
|
+
if (!isEVMLike(metadata.protocol)) {
|
|
31
39
|
return undefined;
|
|
32
40
|
}
|
|
33
41
|
|
|
34
42
|
const reorgPeriod = metadata.blocks?.reorgPeriod ?? 32;
|
|
35
43
|
if (typeof reorgPeriod === 'string') {
|
|
44
|
+
if (metadata.protocol === ProtocolType.Tron) {
|
|
45
|
+
logger?.warn(
|
|
46
|
+
{ chain: chainName, reorgPeriod },
|
|
47
|
+
'Tron does not support named block tags — ignoring string reorgPeriod, using latest block',
|
|
48
|
+
);
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
36
51
|
return reorgPeriod as EthJsonRpcBlockParameterTag;
|
|
37
52
|
}
|
|
38
53
|
|
|
54
|
+
numericTronLookup = metadata.protocol === ProtocolType.Tron;
|
|
39
55
|
const provider = multiProvider.getEthersV5Provider(chainName);
|
|
40
56
|
let latestBlock: number;
|
|
41
57
|
if (provider instanceof providers.JsonRpcProvider) {
|
|
42
58
|
const latestBlockHex = await provider.send('eth_blockNumber', []);
|
|
43
59
|
if (
|
|
44
60
|
typeof latestBlockHex !== 'string' ||
|
|
45
|
-
|
|
61
|
+
!HEX_BLOCK_NUMBER_REGEX.test(latestBlockHex)
|
|
46
62
|
) {
|
|
47
63
|
throw new Error(
|
|
48
64
|
`Invalid eth_blockNumber response for ${chainName}: ${latestBlockHex}`,
|
|
49
65
|
);
|
|
50
66
|
}
|
|
51
67
|
latestBlock = parseInt(latestBlockHex, 16);
|
|
52
|
-
if (!Number.isFinite(latestBlock)) {
|
|
53
|
-
throw new Error(
|
|
54
|
-
`Parsed block number is not finite for ${chainName}: ${latestBlockHex}`,
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
68
|
} else {
|
|
58
69
|
latestBlock = await provider.getBlockNumber();
|
|
59
70
|
}
|
|
71
|
+
if (!Number.isSafeInteger(latestBlock) || latestBlock < 0) {
|
|
72
|
+
throw new Error(`Invalid block number for ${chainName}: ${latestBlock}`);
|
|
73
|
+
}
|
|
60
74
|
return Math.max(0, latestBlock - reorgPeriod);
|
|
61
75
|
} catch (error) {
|
|
62
76
|
logger?.warn(
|
|
63
|
-
{
|
|
64
|
-
|
|
77
|
+
{
|
|
78
|
+
chain: chainName,
|
|
79
|
+
error: error instanceof Error ? error.message : String(error),
|
|
80
|
+
},
|
|
81
|
+
'Failed to get confirmed block',
|
|
65
82
|
);
|
|
83
|
+
// For Tron, undefined is intentional only in named-tag mode. A failed
|
|
84
|
+
// numeric lookup must not silently query supply at latest instead.
|
|
85
|
+
if (numericTronLookup) throw error;
|
|
66
86
|
return undefined;
|
|
67
87
|
}
|
|
68
88
|
}
|