diff --git a/typescript/sdk/src/token/EvmERC20WarpModule.ts b/typescript/sdk/src/token/EvmERC20WarpModule.ts index 9690aa813c..fc82bae1e5 100644 --- a/typescript/sdk/src/token/EvmERC20WarpModule.ts +++ b/typescript/sdk/src/token/EvmERC20WarpModule.ts @@ -8,15 +8,13 @@ import { } from '../core/AbstractHyperlaneModule.js'; import { MultiProvider } from '../providers/MultiProvider.js'; import { EthersV5Transaction } from '../providers/ProviderType.js'; -import { RouterConfig } from '../router/types.js'; -import { ChainMap, ChainNameOrId } from '../types.js'; +import { ChainNameOrId } from '../types.js'; import { DerivedTokenRouterConfig, - DerivedTokenType, EvmERC20WarpRouteReader, } from './EvmERC20WarpRouteReader.js'; -import { TokenConfig } from './config.js'; +import { HypERC20Config } from './config.js'; import { HypERC20Factories } from './contracts.js'; import { HypERC20Deployer } from './deploy.js'; @@ -86,18 +84,17 @@ export class EvmERC20WarpModule extends HyperlaneModule< multiProvider: MultiProvider; }): Promise { const { chain, config, multiProvider } = params; + const chainName = multiProvider.getChainName(chain); const deployer = new HypERC20Deployer(multiProvider); - const deployedContracts = ( - await deployer.deploy({ - [chain]: config, - } as ChainMap) - )[chain]; + const deployedContracts = await deployer.deployContracts( + chainName, + config as HypERC20Config, + ); return new EvmERC20WarpModule(multiProvider, { addresses: { ...serializeContracts(deployedContracts), - deployedTokenRoute: - deployedContracts[config.type as DerivedTokenType].address, + deployedTokenRoute: deployedContracts[config.type].address, }, chain, config, diff --git a/typescript/sdk/src/token/EvmERC20WarpRouteReader.hardhat-test.ts b/typescript/sdk/src/token/EvmERC20WarpRouteReader.hardhat-test.ts index 4c921118c9..375aa141f6 100644 --- a/typescript/sdk/src/token/EvmERC20WarpRouteReader.hardhat-test.ts +++ b/typescript/sdk/src/token/EvmERC20WarpRouteReader.hardhat-test.ts @@ -26,7 +26,6 @@ import { ChainMap } from '../types.js'; import { DerivedTokenRouterConfig, - DerivedTokenType, EvmERC20WarpRouteReader, } from './EvmERC20WarpRouteReader.js'; import { TokenConfig, TokenType } from './config.js'; @@ -80,12 +79,12 @@ describe('ERC20WarpRouterReader', async () => { }); it('should derive a token type from contract', async () => { - const typesToDerive: DerivedTokenType[] = [ + const typesToDerive = [ TokenType.collateral, TokenType.collateralVault, TokenType.synthetic, TokenType.native, - ]; + ] as const; await Promise.all( typesToDerive.map(async (type) => { @@ -211,4 +210,52 @@ describe('ERC20WarpRouterReader', async () => { // Check if token values matches expect(derivedConfig.decimals).to.equal(TOKEN_DECIMALS); }); + + it('should return undefined if ism is not set onchain', async () => { + // Create config + let config = { + [chain]: { + type: TokenType.collateral, + token: token.address, + hook: await mailbox.defaultHook(), + ...baseConfig, + }, + }; + // Deploy with config + let warpRoute = await deployer.deploy( + config as ChainMap, + ); + + // Derive config and check if each value matches + let derivedConfig = await evmERC20WarpRouteReader.deriveWarpRouteConfig( + warpRoute[chain].collateral.address, + ); + + expect(derivedConfig.interchainSecurityModule).to.be.undefined; + const interchainSecurityModule = await mailbox.defaultIsm(); + + // Try with Ism set + config = { + [chain]: { + type: TokenType.collateral, + token: token.address, + hook: await mailbox.defaultHook(), + interchainSecurityModule, + ...baseConfig, + }, + }; + // Deploy with config + warpRoute = await deployer.deploy( + config as ChainMap, + ); + + // Derive config and check if each value matches + derivedConfig = await evmERC20WarpRouteReader.deriveWarpRouteConfig( + warpRoute[chain].collateral.address, + ); + + expect(derivedConfig.interchainSecurityModule?.address).to.be.equal( + interchainSecurityModule, + ); + }); }); diff --git a/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts b/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts index 05176bfcae..b717ee64dc 100644 --- a/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts +++ b/typescript/sdk/src/token/EvmERC20WarpRouteReader.ts @@ -1,4 +1,4 @@ -import { ethers, providers } from 'ethers'; +import { providers } from 'ethers'; import { HypERC20CollateralVaultDeposit__factory, @@ -7,7 +7,6 @@ import { } from '@hyperlane-xyz/core'; import { ERC20Metadata, - IsmType, TokenRouterConfig, TokenType, } from '@hyperlane-xyz/sdk'; @@ -26,17 +25,7 @@ type WarpRouteBaseMetadata = Record<'mailbox' | 'owner' | 'hook', string> & { interchainSecurityModule?: DerivedIsmConfigWithAddress; }; -/** - * @remark - * We only expect to support deriving a subset of these types, for now. - */ -export type DerivedTokenType = Extract< - TokenType, - 'collateral' | 'collateralVault' | 'native' | 'synthetic' ->; - export type DerivedTokenRouterConfig = TokenRouterConfig & { - type: DerivedTokenType; interchainSecurityModule?: DerivedIsmConfigWithAddress; }; @@ -89,10 +78,9 @@ export class EvmERC20WarpRouteReader { * @param warpRouteAddress - The Warp Route address to derive the token type for. * @returns The derived token type, which can be one of: collateralVault, collateral, native, or synthetic. */ - async deriveTokenType(warpRouteAddress: Address): Promise { - const contractTypes: Record< - Exclude, // native is excluded because it's the default return type - { factory: any; method: string } + async deriveTokenType(warpRouteAddress: Address): Promise { + const contractTypes: Partial< + Record > = { collateralVault: { factory: HypERC20CollateralVaultDeposit__factory, @@ -112,7 +100,7 @@ export class EvmERC20WarpRouteReader { try { const warpRoute = factory.connect(warpRouteAddress, this.provider); await warpRoute[method](); - return type as DerivedTokenType; + return type as TokenType; } catch (e) { this.logger.info( `Error accessing token specific method, ${method}, implying this is not a ${type} token.`, @@ -154,23 +142,14 @@ export class EvmERC20WarpRouteReader { hook, }; - // If ISM is unset, then Address Zero will be returned - isZeroishAddress(interchainSecurityModule) - ? (metadata.interchainSecurityModule = { - type: IsmType.CUSTOM, - address: ethers.constants.AddressZero, - }) - : (metadata.interchainSecurityModule = - await this.evmIsmReader.deriveIsmConfig(interchainSecurityModule)); - - // @todo add after https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3667 is fixed - // isZeroishAddress(interchainSecurityModule) - // ? (metadata.hook = { - // type: IsmType.ADDRESS, - // address: ethers.constants.AddressZero, - // }) - // : (metadata.hook = - // await this.evmIsmReader.deriveHookConfig(hook)); + // Derive If ISM is set onchain + metadata.interchainSecurityModule = !isZeroishAddress( + interchainSecurityModule, + ) + ? await this.evmIsmReader.deriveIsmConfig(interchainSecurityModule) + : undefined; + + // @todo add Hook config after https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3667 is fixed return metadata; } @@ -180,9 +159,10 @@ export class EvmERC20WarpRouteReader { * * @param tokenAddress - The address of the token. * @returns A partial ERC20 metadata object containing the token name, symbol, total supply, and decimals. + * Throws if unsupported token type */ async fetchTokenMetadata( - type: DerivedTokenType, + type: TokenType, tokenAddress: Address, ): Promise { if (type === TokenType.collateral || type === TokenType.collateralVault) { @@ -197,8 +177,7 @@ export class EvmERC20WarpRouteReader { return { name, symbol, decimals, totalSupply, token }; } else if (type === TokenType.synthetic) { return this.fetchERC20Metadata(tokenAddress); - } else { - // Assumes Native + } else if (type === TokenType.native) { const chainMetadata = this.multiProvider.getChainMetadata(this.chain); if (chainMetadata.nativeToken) { const { name, symbol, decimals } = chainMetadata.nativeToken; @@ -208,6 +187,10 @@ export class EvmERC20WarpRouteReader { `Warp route config specifies native token but chain metadata for ${this.chain} does not provide native token details`, ); } + } else { + throw new Error( + `Unsupported token type ${type} when fetching token metadata`, + ); } }