@hyperlane-xyz/sdk
Version:
The official SDK for the Hyperlane Network
91 lines • 4.65 kB
JavaScript
import { assert, bytes32ToAddress, formatStandardHookMetadata, } from '@hyperlane-xyz/utils';
import { buildInterchainAccountApp, } from '../../../middleware/account/InterchainAccount.js';
import { TxSubmitterType } from './TxSubmitterTypes.js';
export class EvmIcaTxSubmitter {
config;
submitter;
multiProvider;
interchainAccountApp;
txSubmitterType = TxSubmitterType.INTERCHAIN_ACCOUNT;
constructor(config, submitter, multiProvider, interchainAccountApp) {
this.config = config;
this.submitter = submitter;
this.multiProvider = multiProvider;
this.interchainAccountApp = interchainAccountApp;
}
static async fromConfig(config, multiProvider, coreAddressesByChain, getSubmitterFn) {
const interchainAccountRouterAddress = config.originInterchainAccountRouter ??
coreAddressesByChain[config.chain].interchainAccountRouter;
assert(interchainAccountRouterAddress, `Origin chain InterchainAccountRouter address not supplied and none found in the registry metadata for chain ${config.chain}`);
const internalSubmitter = await getSubmitterFn(multiProvider, config.internalSubmitter, coreAddressesByChain);
const interchainAccountApp = await buildInterchainAccountApp(multiProvider, config.chain, {
owner: config.owner,
origin: config.chain,
localRouter: interchainAccountRouterAddress,
ismOverride: config.interchainSecurityModule,
}, coreAddressesByChain);
return new EvmIcaTxSubmitter({
owner: config.owner,
chain: config.chain,
destinationChain: config.destinationChain,
originInterchainAccountRouter: interchainAccountRouterAddress,
destinationInterchainAccountRouter: config.destinationInterchainAccountRouter,
interchainSecurityModule: config.interchainSecurityModule,
}, internalSubmitter, multiProvider, interchainAccountApp);
}
async submit(...txs) {
if (txs.length === 0) {
return [];
}
const transactionChains = new Set(txs.map((tx) => tx.chainId));
if (transactionChains.size !== 1) {
throw new Error('ICA transactions should have all the same destination chain');
}
const [chainId] = transactionChains.values();
if (!chainId) {
throw new Error('Destination domain for ICA transactions should be defined');
}
const { chainId: destinationEvmChainId, domainId: destinationDomainId } = this.multiProvider.getChainMetadata(this.config.destinationChain);
// On the EVM chains the id and domain id might be different so we match either against the
// EVM chain id or the Hyperlane domain id
if (chainId !== destinationDomainId && chainId !== destinationEvmChainId) {
throw new Error(`Destination chain mismatch. Expected EVM chain id ${destinationEvmChainId} or Hyperlane domain id ${destinationDomainId} but received ${chainId}.`);
}
const innerCalls = txs.map(({ to, data, chainId, value }) => {
assert(chainId, 'Invalid PopulatedTransaction: "chainId" is required');
assert(to, 'Invalid PopulatedTransaction: "to" is required');
assert(data, 'Invalid PopulatedTransaction: "data" is required');
return { data, to, value: value?.toString() };
});
const refundAddress = bytes32ToAddress(this.config.owner);
const icaConfig = {
origin: this.config.chain,
owner: this.config.owner,
ismOverride: this.config.interchainSecurityModule,
routerOverride: this.config.destinationInterchainAccountRouter,
localRouter: this.config.originInterchainAccountRouter,
};
const gasLimit = await this.interchainAccountApp.estimateIcaHandleGas({
origin: this.config.chain,
destination: this.config.destinationChain,
innerCalls,
config: icaConfig,
});
const hookMetadata = formatStandardHookMetadata({
refundAddress,
gasLimit: gasLimit.toBigInt(),
});
const icaTx = await this.interchainAccountApp.getCallRemote({
chain: this.config.chain,
destination: this.config.destinationChain,
innerCalls,
config: icaConfig,
hookMetadata,
});
return this.submitter.submit({
chainId: this.multiProvider.getDomainId(this.config.chain),
...icaTx,
});
}
}
//# sourceMappingURL=IcaTxSubmitter.js.map