From 7eb849d7b3b31751440dc30fc13a59873fdd530a Mon Sep 17 00:00:00 2001 From: Yorke Rhodes Date: Thu, 23 Mar 2023 13:55:03 -0400 Subject: [PATCH] Update config and tests for default token metadata --- src/config.ts | 16 ++- src/deploy.ts | 296 ++++++++++++++++++++++++++++++++------------ test/erc20.test.ts | 9 +- test/erc721.test.ts | 6 +- 4 files changed, 239 insertions(+), 88 deletions(-) diff --git a/src/config.ts b/src/config.ts index dd17b41..71172cf 100644 --- a/src/config.ts +++ b/src/config.ts @@ -10,11 +10,23 @@ export enum TokenType { native = 'native', } -export type SyntheticConfig = { - type: TokenType.synthetic | TokenType.syntheticUri; +export type TokenMetadata = { name: string; symbol: string; totalSupply: ethers.BigNumberish; +} + +export type ERC20Metadata = TokenMetadata & { + decimals: number; +} + +export const isTokenMetadata = (metadata: any): metadata is TokenMetadata => + metadata.name !== undefined && metadata.symbol !== undefined && metadata.totalSupply !== undefined; + +export const isErc20Metadata = (metadata: any): metadata is ERC20Metadata => metadata.decimals !== undefined && isTokenMetadata(metadata); + +export type SyntheticConfig = Partial & { + type: TokenType.synthetic | TokenType.syntheticUri; }; export type CollateralConfig = { type: TokenType.collateral | TokenType.collateralUri; diff --git a/src/deploy.ts b/src/deploy.ts index 1def108..2319048 100644 --- a/src/deploy.ts +++ b/src/deploy.ts @@ -9,22 +9,36 @@ import { import { DeployerOptions } from '@hyperlane-xyz/sdk/dist/deploy/HyperlaneDeployer'; import { + CollateralConfig, HypERC20Config, HypERC721Config, + SyntheticConfig, TokenConfig, isCollateralConfig, isNativeConfig, isSyntheticConfig, isUriConfig, + NativeConfig, + isTokenMetadata, + ERC20Metadata, + isErc20Metadata, + TokenMetadata, } from './config'; import { HypERC20Contracts, HypERC721Contracts } from './contracts'; import { + ERC20__factory, + ERC721__factory, + HypERC20, + HypERC20Collateral, HypERC20Collateral__factory, HypERC20__factory, + HypERC721, + HypERC721Collateral, HypERC721Collateral__factory, HypERC721URICollateral__factory, HypERC721URIStorage__factory, HypERC721__factory, + HypNative, HypNative__factory, } from './types'; @@ -55,7 +69,7 @@ const gasDefaults = (config: TokenConfig, tokenType: TokenType) => { return 44_000; case 'collateral': default: - return 68_000; + return 69_000; } } }; @@ -65,6 +79,8 @@ export class HypERC20Deployer extends GasRouterDeployer< HypERC20Contracts, any // RouterFactories doesn't work well when router has multiple types > { + tokenMetadata?: ERC20Metadata; + constructor( multiProvider: MultiProvider, configMap: ChainMap, @@ -86,51 +102,121 @@ export class HypERC20Deployer extends GasRouterDeployer< ); } + protected async deployCollateral( + chain: ChainName, + config: CollateralConfig & HypERC20Config, + ): Promise { + this.logger(`Deploying collateral router on ${chain}...`); + const erc20 = new ERC20__factory() + .attach(config.token) + .connect(this.multiProvider.getProvider(chain)); + + const decimals = await erc20.decimals(); + const name = await erc20.name(); + const symbol = await erc20.symbol(); + const totalSupply = 0; // synthetic tokens are minted on demand + + this.tokenMetadata = { decimals, name, symbol, totalSupply }; + + const router = await this.deployContractFromFactory( + chain, + new HypERC20Collateral__factory(), + 'HypERC20Collateral', + [config.token], + ); + await this.multiProvider.handleTx( + chain, + router.initialize(config.mailbox, config.interchainGasPaymaster), + ); + return router; + } + + protected async deployNative( + chain: ChainName, + config: NativeConfig & HypERC20Config, + ): Promise { + this.logger(`Deploying native router on ${chain}...`); + const nativeToken = this.multiProvider.getChainMetadata(chain).nativeToken; + + if (nativeToken) { + this.tokenMetadata = { ...nativeToken, totalSupply: 0 }; + } + + const router = await this.deployContractFromFactory( + chain, + new HypNative__factory(), + 'HypNative', + [], + ); + await this.multiProvider.handleTx( + chain, + router.initialize(config.mailbox, config.interchainGasPaymaster), + ); + return router; + } + + protected async deploySynthetic( + chain: ChainName, + config: SyntheticConfig & HypERC20Config, + tokenMetadata: ERC20Metadata + ): Promise { + this.logger(`Deploying synthetic router on ${chain}...`); + const router = await this.deployContractFromFactory( + chain, + new HypERC20__factory(), + 'HypERC20', + [tokenMetadata.decimals], + ); + await this.multiProvider.handleTx( + chain, + router.initialize( + config.mailbox, + config.interchainGasPaymaster, + tokenMetadata.totalSupply, + tokenMetadata.name, + tokenMetadata.symbol, + ), + ); + return router; + } + + async deploy( + partialDeployment?: ChainMap, + ): Promise> { + // deploy collateral or native first to populate token metadata for synthetics + for (const [chain, config] of Object.entries(this.configMap)) { + if (isCollateralConfig(config)) { + this.deployedContracts[chain] = { + router: await this.deployCollateral(chain as ChainName, config) + }; + } else if (isNativeConfig(config)) { + this.deployedContracts[chain] = { + router: await this.deployNative(chain as ChainName, config) + }; + } + } + + // deploy synthetics + return super.deploy({ ...partialDeployment, ...this.deployedContracts }); + } + async deployContracts(chain: ChainName, config: HypERC20Config) { - if (isCollateralConfig(config)) { - const router = await this.deployContractFromFactory( - chain, - new HypERC20Collateral__factory(), - 'HypERC20Collateral', - [config.token], - ); - await this.multiProvider.handleTx( - chain, - router.initialize(config.mailbox, config.interchainGasPaymaster), - ); - return { router }; - } else if (isSyntheticConfig(config)) { - const router = await this.deployContractFromFactory( - chain, - new HypERC20__factory(), - 'HypERC20', - [], - ); - await this.multiProvider.handleTx( - chain, - router.initialize( - config.mailbox, - config.interchainGasPaymaster, - config.totalSupply, - config.name, - config.symbol, - ), - ); - return { router }; - } else if (isNativeConfig(config)) { - const router = await this.deployContractFromFactory( - chain, - new HypNative__factory(), - 'HypNative', - [], - ); - await this.multiProvider.handleTx( - chain, - router.initialize(config.mailbox, config.interchainGasPaymaster), - ); - return { router }; + // skip if already deployed + if (this.deployedContracts[chain]) { + return this.deployedContracts[chain]; } - throw new Error('Invalid config'); + + if (!isSyntheticConfig(config)) { + throw new Error('Expect only synthetic configs'); + } + + const erc20Metadata = {...this.tokenMetadata, ...config }; + if (!isErc20Metadata(erc20Metadata)) { + throw new Error(`ERC20 metadata not populated for ${chain}`); + } + + const router = await this.deploySynthetic(chain, config, erc20Metadata); + return { router }; } } @@ -140,6 +226,8 @@ export class HypERC721Deployer extends GasRouterDeployer< HypERC721Contracts, any > { + tokenMetadata?: TokenMetadata; + constructor( multiProvider: MultiProvider, configMap: ChainMap, @@ -161,42 +249,92 @@ export class HypERC721Deployer extends GasRouterDeployer< ); } + protected async deployCollateral( + chain: ChainName, + config: CollateralConfig & HypERC721Config, + ): Promise { + this.logger(`Deploying collateral router on ${chain}...`); + + const erc721 = ERC721__factory.connect(config.token, this.multiProvider.getProvider(chain)); + const name = await erc721.name(); + const symbol = await erc721.symbol(); + const totalSupply = 0; // synthetic tokens are minted on demand + this.tokenMetadata = { name, symbol, totalSupply }; + + const router = await this.deployContractFromFactory( + chain, + isUriConfig(config) + ? new HypERC721URICollateral__factory() + : new HypERC721Collateral__factory(), + `HypERC721${isUriConfig(config) ? 'URI' : ''}Collateral`, + [config.token], + ); + await this.multiProvider.handleTx( + chain, + router.initialize(config.mailbox, config.interchainGasPaymaster), + ); + return router; + } + + protected async deploySynthetic( + chain: ChainName, + config: HypERC721Config & SyntheticConfig, + tokenMetadata: TokenMetadata + ): Promise { + this.logger(`Deploying synthetic router on ${chain}...`); + const router = await this.deployContractFromFactory( + chain, + isUriConfig(config) + ? new HypERC721URIStorage__factory() + : new HypERC721__factory(), + `HypERC721${isUriConfig(config) ? 'URIStorage' : ''}`, + [], + ); + await this.multiProvider.handleTx( + chain, + router.initialize( + config.mailbox, + config.interchainGasPaymaster, + tokenMetadata.totalSupply, + tokenMetadata.name, + tokenMetadata.symbol, + ), + ); + return router; + } + + async deploy( + partialDeployment?: ChainMap, + ): Promise> { + // deploy collateral first to populate token metadata for synthetics + for (const [chain, config] of Object.entries(this.configMap)) { + if (isCollateralConfig(config)) { + this.deployedContracts[chain] = { + router: await this.deployCollateral(chain as ChainName, config) + }; + } + } + + // deploy synthetics + return super.deploy({ ...partialDeployment, ...this.deployedContracts }); + } + async deployContracts(chain: ChainName, config: HypERC721Config) { - if (isCollateralConfig(config)) { - const router = await this.deployContractFromFactory( - chain, - isUriConfig(config) - ? new HypERC721URICollateral__factory() - : new HypERC721Collateral__factory(), - `HypERC721${isUriConfig(config) ? 'URI' : ''}Collateral`, - [config.token], - ); - await this.multiProvider.handleTx( - chain, - router.initialize(config.mailbox, config.interchainGasPaymaster), - ); - return { router }; - } else if (isSyntheticConfig(config)) { - const router = await this.deployContractFromFactory( - chain, - isUriConfig(config) - ? new HypERC721URIStorage__factory() - : new HypERC721__factory(), - `HypERC721${isUriConfig(config) ? 'URIStorage' : ''}`, - [], - ); - await this.multiProvider.handleTx( - chain, - router.initialize( - config.mailbox, - config.interchainGasPaymaster, - config.totalSupply, - config.name, - config.symbol, - ), - ); - return { router }; + // skip if already deployed + if (this.deployedContracts[chain]) { + return this.deployedContracts[chain]; + } + + if (!isSyntheticConfig(config)) { + throw new Error('Expect only synthetic configs'); + } + + const erc721Metadata = {...this.tokenMetadata, ...config }; + if (!isTokenMetadata(erc721Metadata)) { + throw new Error(`Token metadata not populated for ${chain}`); } - throw new Error('Invalid config'); + + const router = await this.deploySynthetic(chain, config, erc721Metadata); + return { router }; } } diff --git a/test/erc20.test.ts b/test/erc20.test.ts index 01f9ae1..9c829b2 100644 --- a/test/erc20.test.ts +++ b/test/erc20.test.ts @@ -42,7 +42,8 @@ const tokenConfig: SyntheticConfig = { type: TokenType.synthetic, name: 'HypERC20', symbol: 'HYP', - totalSupply, + decimals: 18, + totalSupply }; for (const variant of [ @@ -77,9 +78,9 @@ for (const variant of [ let erc20: ERC20 | undefined; if (variant === TokenType.collateral) { erc20 = await new ERC20Test__factory(owner).deploy( - tokenConfig.name, - tokenConfig.symbol, - tokenConfig.totalSupply, + tokenConfig.name!, + tokenConfig.symbol!, + tokenConfig.totalSupply!, ); localTokenConfig = { type: variant, diff --git a/test/erc721.test.ts b/test/erc721.test.ts index daf5c5f..794a287 100644 --- a/test/erc721.test.ts +++ b/test/erc721.test.ts @@ -100,9 +100,9 @@ for (const withCollateral of [true, false]) { let erc721: ERC721 | undefined; if (withCollateral) { erc721 = await new ERC721Test__factory(owner).deploy( - tokenConfig.name, - tokenConfig.symbol, - tokenConfig.totalSupply, + tokenConfig.name!, + tokenConfig.symbol!, + tokenConfig.totalSupply!, ); configWithTokenInfo.test1 = { ...configWithTokenInfo.test1,