deploy-warp-route
Deploy cross-chain asset transfer routes between multiple blockchains using the Hyperlane protocol to enable token movement across different networks.
Instructions
Deploys a warp route.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| warpChains | Yes | Warp chains to deploy the route on | |
| tokenTypes | Yes | Token types to deploy |
Implementation Reference
- src/index.ts:452-569 (registration)Registration of the 'deploy-warp-route' tool using server.tool(), including description, input schema, and handler function.server.tool( 'deploy-warp-route', 'Deploys a warp route.', { warpChains: z .array(z.string()) .describe('Warp chains to deploy the route on'), tokenTypes: z .array( z.enum( TYPE_CHOICES.map((choice) => choice.name) as [string, ...string[]] ) ) .describe('Token types to deploy'), }, async ({ warpChains, tokenTypes }) => { server.server.sendLoggingMessage({ level: 'info', data: `Deploying warp route with chains: ${warpChains.join( ', ' )} and token types: ${tokenTypes.join(', ')}.`, }); const fileName = `routes/${ warpChains.map((chain, i) => `${chain}:${tokenTypes[i]}`).join('-') + '.yaml' }`; let warpRouteConfig: WarpRouteDeployConfig; const filePath = path.join(mcpDir, fileName); if (fs.existsSync(filePath)) { server.server.sendLoggingMessage({ level: 'info', data: `Warp Route Already exists @ ${fileName} already exists. Skipping Config Creation.`, }); const fileContent = fs.readFileSync(filePath, 'utf-8'); warpRouteConfig = yaml.parse(fileContent) as WarpRouteDeployConfig; return { content: [ { type: 'text', text: `Warp Route Config already exists @ ${fileName}. Skipping Config Creation. Config: ${JSON.stringify( warpRouteConfig, null, 2 )}`, }, ], }; } else { server.server.sendLoggingMessage({ level: 'info', data: `Creating Warp Route Config @ ${fileName}`, }); warpRouteConfig = await createWarpRouteDeployConfig({ warpChains, tokenTypes: tokenTypes.map( (t) => TokenType[t as keyof typeof TokenType] ), signerAddress: signer.address, registry, outPath: './warpRouteDeployConfig.yaml', }); server.server.sendLoggingMessage({ level: 'info', data: `Warp route deployment config created: ${JSON.stringify( warpRouteConfig, null, 2 )}`, }); } const chainMetadata: ChainMap<ChainMetadata> = {}; for (const chain of warpChains) { chainMetadata[chain] = (await registry.getChainMetadata(chain))!; } const multiProvider = new MultiProvider(chainMetadata, { signers: Object.fromEntries(warpChains.map((chain) => [chain, signer])), }); const deploymentConfig = await deployWarpRoute({ registry, chainMetadata, multiProvider, warpRouteDeployConfig: warpRouteConfig, filePath, }); server.server.sendLoggingMessage({ level: 'info', data: `Warp route deployed successfully. Config: ${JSON.stringify( warpRouteConfig, null, 2 )}`, }); return { content: [ { type: 'text', text: `Warp route deployment config created successfully. Config: ${JSON.stringify( deploymentConfig, null, 2 )}`, }, ], }; } );
- src/index.ts:455-466 (schema)Input schema (Zod) for the deploy-warp-route tool parameters: warpChains and tokenTypes.{ warpChains: z .array(z.string()) .describe('Warp chains to deploy the route on'), tokenTypes: z .array( z.enum( TYPE_CHOICES.map((choice) => choice.name) as [string, ...string[]] ) ) .describe('Token types to deploy'), },
- src/index.ts:467-568 (handler)The main handler function that orchestrates the deployment: logs params, checks existing config file, creates WarpRouteDeployConfig if needed, deploys via deployWarpRoute helper, and returns deployment config.async ({ warpChains, tokenTypes }) => { server.server.sendLoggingMessage({ level: 'info', data: `Deploying warp route with chains: ${warpChains.join( ', ' )} and token types: ${tokenTypes.join(', ')}.`, }); const fileName = `routes/${ warpChains.map((chain, i) => `${chain}:${tokenTypes[i]}`).join('-') + '.yaml' }`; let warpRouteConfig: WarpRouteDeployConfig; const filePath = path.join(mcpDir, fileName); if (fs.existsSync(filePath)) { server.server.sendLoggingMessage({ level: 'info', data: `Warp Route Already exists @ ${fileName} already exists. Skipping Config Creation.`, }); const fileContent = fs.readFileSync(filePath, 'utf-8'); warpRouteConfig = yaml.parse(fileContent) as WarpRouteDeployConfig; return { content: [ { type: 'text', text: `Warp Route Config already exists @ ${fileName}. Skipping Config Creation. Config: ${JSON.stringify( warpRouteConfig, null, 2 )}`, }, ], }; } else { server.server.sendLoggingMessage({ level: 'info', data: `Creating Warp Route Config @ ${fileName}`, }); warpRouteConfig = await createWarpRouteDeployConfig({ warpChains, tokenTypes: tokenTypes.map( (t) => TokenType[t as keyof typeof TokenType] ), signerAddress: signer.address, registry, outPath: './warpRouteDeployConfig.yaml', }); server.server.sendLoggingMessage({ level: 'info', data: `Warp route deployment config created: ${JSON.stringify( warpRouteConfig, null, 2 )}`, }); } const chainMetadata: ChainMap<ChainMetadata> = {}; for (const chain of warpChains) { chainMetadata[chain] = (await registry.getChainMetadata(chain))!; } const multiProvider = new MultiProvider(chainMetadata, { signers: Object.fromEntries(warpChains.map((chain) => [chain, signer])), }); const deploymentConfig = await deployWarpRoute({ registry, chainMetadata, multiProvider, warpRouteDeployConfig: warpRouteConfig, filePath, }); server.server.sendLoggingMessage({ level: 'info', data: `Warp route deployed successfully. Config: ${JSON.stringify( warpRouteConfig, null, 2 )}`, }); return { content: [ { type: 'text', text: `Warp route deployment config created successfully. Config: ${JSON.stringify( deploymentConfig, null, 2 )}`, }, ], }; }
- src/warpRoute.ts:34-193 (helper)Helper function to create the WarpRouteDeployConfig from chains, token types, signer, and registry.export async function createWarpRouteDeployConfig({ warpChains, tokenTypes, signerAddress: owner, registry, outPath, }: { warpChains: ChainName[]; tokenTypes: TokenType[]; signerAddress: string; registry: BaseRegistry; outPath: string; }): Promise<WarpRouteDeployConfig> { // logBlue("Creating a new warp route deployment config..."); if (warpChains.length < 2 || tokenTypes.length < 2) { throw new Error( 'At least two warp chains and two token types are required.' ); } if (warpChains.length !== tokenTypes.length) { throw new Error( 'The number of warp chains and token types must be the same.' ); } const result: WarpRouteDeployConfig = {}; let typeChoices = TYPE_CHOICES; let index = 0; for (const chain of warpChains) { // logBlue(`${chain}: Configuring warp route...`); const proxyAdmin: DeployedOwnableConfig | undefined = undefined; let interchainSecurityModule: IsmConfig | undefined = undefined; const type = tokenTypes[index]; const isNft = type === TokenType.syntheticUri || type === TokenType.collateralUri; const mailbox = (await registry.getChainAddresses(chain))!.mailbox; switch (type) { case TokenType.collateral: case TokenType.XERC20: case TokenType.XERC20Lockbox: case TokenType.collateralFiat: case TokenType.collateralUri: // result[chain] = { // type, // owner, // proxyAdmin, // isNft, // interchainSecurityModule, // token: await input({ // message: `Enter the existing token address on chain ${chain}`, // }), // }; break; case TokenType.syntheticRebase: result[chain] = { type, owner, isNft, proxyAdmin, collateralChainName: '', // This will be derived correctly by zod.parse() below interchainSecurityModule, mailbox: '', // This will need to be set to the actual mailbox address }; typeChoices = restrictChoices([ TokenType.syntheticRebase, TokenType.collateralVaultRebase, ]); break; case TokenType.collateralVaultRebase: // result[chain] = { // type, // owner, // proxyAdmin, // isNft, // interchainSecurityModule, // token: await input({ // message: `Enter the ERC-4626 vault address on chain ${chain}`, // }), // }; // typeChoices = restrictChoices([TokenType.syntheticRebase]); break; case TokenType.collateralVault: // result[chain] = { // type, // owner, // proxyAdmin, // isNft, // interchainSecurityModule, // token: await input({ // message: `Enter the ERC-4626 vault address on chain ${chain}`, // }), // }; break; case TokenType.synthetic: case TokenType.syntheticUri: result[chain] = { type, owner, proxyAdmin, isNft, interchainSecurityModule, mailbox, }; break; case TokenType.fastSynthetic: result[chain] = { type, owner, proxyAdmin, isNft, interchainSecurityModule, mailbox: '', }; break; case TokenType.fastCollateral: result[chain] = { type, owner, proxyAdmin, isNft, interchainSecurityModule, token: '', mailbox, }; break; case TokenType.native: case TokenType.nativeScaled: result[chain] = { type, owner, proxyAdmin, isNft, interchainSecurityModule, mailbox, }; } index++; } try { const warpRouteDeployConfig = WarpRouteDeployConfigSchema.parse(result); // logBlue(`Warp Route config is valid, writing to file ${outPath}:\n`); // log(indentYamlOrJson(yamlStringify(warpRouteDeployConfig, null, 2), 4)); // writeYaml(outPath, warpRouteDeployConfig); // logGreen("✅ Successfully created new warp route deployment config."); return warpRouteDeployConfig; } catch (e) { throw new Error( `Warp route deployment config is invalid: ${JSON.stringify( result )}. Error: ${e}` ); } }
- src/warpRoute.ts:319-348 (helper)Core helper function that performs the actual warp route deployment using Hyperlane SDK deployers and generates/saves the WarpCoreConfig.export async function deployWarpRoute({ registry, multiProvider, warpRouteDeployConfig: warpRouteConfig, filePath, }: { registry: BaseRegistry; chainMetadata: ChainMap<ChainMetadata>; multiProvider: MultiProvider; warpRouteDeployConfig: WarpRouteDeployConfig; filePath: string; }): Promise<WarpCoreConfig> { const deployedContracts = await executeDeploy( warpRouteConfig, multiProvider, registry ); const { warpCoreConfig } = await getWarpCoreConfig( warpRouteConfig, multiProvider, deployedContracts ); // NOT IMPLEMENTED YE // await registry.addWarpRoute(warpCoreConfig); await fs.promises.writeFile(filePath, stringify(warpCoreConfig, null, 2)); return warpCoreConfig; }