Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/multiprotocol-core-rebalancer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@hyperlane-xyz/rebalancer': patch
---

ActionTracker now uses MultiProtocolCore instead of HyperlaneCore for message delivery checks, enabling support for all VM types. Registry addresses are validated at startup to ensure mailbox is present.
38 changes: 29 additions & 9 deletions typescript/rebalancer/src/factories/RebalancerContextFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { type Logger } from 'pino';
import { IRegistry } from '@hyperlane-xyz/registry';
import {
type ChainMap,
HyperlaneCore,
type CoreAddresses,
MultiProtocolCore,
MultiProtocolProvider,
MultiProvider,
type Token,
Expand Down Expand Up @@ -45,6 +46,7 @@ export class RebalancerContextFactory {
* @param warpCore - An instance of `WarpCore` configured for the specified `warpRouteId`.
* @param tokensByChainName - A map of chain->token to ease the lookup of token by chain
* @param multiProvider - MultiProvider instance
* @param multiProtocolProvider - MultiProtocolProvider instance (with mailbox metadata)
* @param registry - IRegistry instance
* @param logger - Logger instance
*/
Expand All @@ -53,6 +55,7 @@ export class RebalancerContextFactory {
private readonly warpCore: WarpCore,
private readonly tokensByChainName: ChainMap<Token>,
private readonly multiProvider: MultiProvider,
private readonly multiProtocolProvider: MultiProtocolProvider,
private readonly registry: IRegistry,
private readonly logger: Logger,
) {}
Expand Down Expand Up @@ -87,15 +90,18 @@ export class RebalancerContextFactory {
const mpp =
multiProtocolProvider ??
MultiProtocolProvider.fromMultiProvider(multiProvider);
const provider = mpp.extendChainMetadata(mailboxes);
const extendedMultiProtocolProvider = mpp.extendChainMetadata(mailboxes);

const warpCoreConfig = await registry.getWarpRoute(config.warpRouteId);
if (!warpCoreConfig) {
throw new Error(
`Warp route config for ${config.warpRouteId} not found in registry`,
);
}
const warpCore = WarpCore.FromConfig(provider, warpCoreConfig);
const warpCore = WarpCore.FromConfig(
extendedMultiProtocolProvider,
warpCoreConfig,
);
const tokensByChainName = Object.fromEntries(
warpCore.tokens.map((t) => [t.chainName, t]),
);
Expand All @@ -111,6 +117,7 @@ export class RebalancerContextFactory {
warpCore,
tokensByChainName,
multiProvider,
extendedMultiProtocolProvider,
registry,
logger,
);
Expand Down Expand Up @@ -228,11 +235,24 @@ export class RebalancerContextFactory {
// 2. Create ExplorerClient
const explorerClient = new ExplorerClient(explorerUrl);

// 3. Get HyperlaneCore from registry
const addresses = await this.registry.getAddresses();
const hyperlaneCore = HyperlaneCore.fromAddressesMap(
addresses,
this.multiProvider,
// 3. Get MultiProtocolCore from registry (supports all VM types)
// Only fetch/validate addresses for warp route chains (not all registry chains)
const warpRouteChains = new Set(
this.warpCore.tokens.map((t) => t.chainName),
);
const coreAddresses: ChainMap<CoreAddresses> = {};
for (const chain of warpRouteChains) {
const addrs = await this.registry.getChainAddresses(chain);
if (!addrs?.mailbox) {
throw new Error(
`Missing mailbox address for chain ${chain} in registry`,
);
}
coreAddresses[chain] = addrs as CoreAddresses;
}
const multiProtocolCore = MultiProtocolCore.fromAddressesMap(
coreAddresses,
this.multiProtocolProvider,
);

// 4. Get rebalancer address from signer
Expand Down Expand Up @@ -265,7 +285,7 @@ export class RebalancerContextFactory {
intentStore,
actionStore,
explorerClient,
hyperlaneCore,
multiProtocolCore,
trackerConfig,
this.logger,
);
Expand Down
37 changes: 7 additions & 30 deletions typescript/rebalancer/src/monitor/Monitor.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import { type Logger } from 'pino';

import {
EthJsonRpcBlockParameterTag,
type Token,
type WarpCore,
} from '@hyperlane-xyz/sdk';
import { type Token, type WarpCore } from '@hyperlane-xyz/sdk';
import { sleep } from '@hyperlane-xyz/utils';

import {
Expand All @@ -16,6 +12,7 @@ import {
MonitorPollingError,
MonitorStartError,
} from '../interfaces/IMonitor.js';
import { getConfirmedBlockTag } from '../utils/blockTag.js';

/**
* Simple monitor implementation that polls warp route collateral balances and emits them as MonitorEvent.
Expand All @@ -38,36 +35,16 @@ export class Monitor implements IMonitor {
private readonly logger: Logger,
) {}

private async getConfirmedBlockTag(
chainName: string,
): Promise<ConfirmedBlockTag> {
try {
const metadata = this.warpCore.multiProvider.getChainMetadata(chainName);
const reorgPeriod = metadata.blocks?.reorgPeriod ?? 32;

if (typeof reorgPeriod === 'string') {
return reorgPeriod as EthJsonRpcBlockParameterTag;
}

const provider =
this.warpCore.multiProvider.getEthersV5Provider(chainName);
const latestBlock = await provider.getBlockNumber();
return Math.max(0, latestBlock - reorgPeriod);
} catch (error) {
this.logger.warn(
{ chain: chainName, error: (error as Error).message },
'Failed to get confirmed block, using latest',
);
return undefined;
}
}

private async computeConfirmedBlockTags(): Promise<ConfirmedBlockTags> {
const blockTags: ConfirmedBlockTags = {};
const chains = new Set(this.warpCore.tokens.map((t) => t.chainName));

for (const chain of chains) {
blockTags[chain] = await this.getConfirmedBlockTag(chain);
blockTags[chain] = await getConfirmedBlockTag(
this.warpCore.multiProvider,
chain,
this.logger,
);
}

return blockTags;
Expand Down
Loading
Loading