@hyperlane-xyz/sdk
Version:
The official SDK for the Hyperlane Network
181 lines • 7.71 kB
JavaScript
import { constants } from 'ethers';
import { ERC20__factory, ERC721Enumerable__factory, IERC4626__factory, IXERC20Lockbox__factory, } from '@hyperlane-xyz/core';
import { ProtocolType, assert, objKeys, objMap, rootLogger, } from '@hyperlane-xyz/utils';
import { GasRouterDeployer } from '../router/GasRouterDeployer.js';
import { TokenType, gasOverhead } from './config.js';
import { hypERC20contracts, hypERC20factories, hypERC721contracts, hypERC721factories, } from './contracts.js';
import { TokenMetadataSchema, isCollateralTokenConfig, isNativeTokenConfig, isSyntheticRebaseTokenConfig, isSyntheticTokenConfig, isTokenMetadata, isXERC20TokenConfig, } from './types.js';
class TokenDeployer extends GasRouterDeployer {
constructor(multiProvider, factories, loggerName, ismFactory, contractVerifier, concurrentDeploy = false) {
super(multiProvider, factories, {
logger: rootLogger.child({ module: loggerName }),
ismFactory,
contractVerifier,
concurrentDeploy,
}); // factories not used in deploy
}
async constructorArgs(_, config) {
// TODO: derive as specified in https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/5296
const scale = config.scale ?? 1;
if (isCollateralTokenConfig(config) || isXERC20TokenConfig(config)) {
return [config.token, scale, config.mailbox];
}
else if (isNativeTokenConfig(config)) {
return [scale, config.mailbox];
}
else if (isSyntheticTokenConfig(config)) {
assert(config.decimals, 'decimals is undefined for config'); // decimals must be defined by this point
return [config.decimals, scale, config.mailbox];
}
else if (isSyntheticRebaseTokenConfig(config)) {
const collateralDomain = this.multiProvider.getDomainId(config.collateralChainName);
return [config.decimals, scale, config.mailbox, collateralDomain];
}
else {
throw new Error('Unknown token type when constructing arguments');
}
}
async initializeArgs(chain, config) {
const signer = await this.multiProvider.getSigner(chain).getAddress();
const defaultArgs = [
config.hook ?? constants.AddressZero,
config.interchainSecurityModule ?? constants.AddressZero,
// TransferOwnership will happen later in RouterDeployer
signer,
];
if (isCollateralTokenConfig(config) ||
isXERC20TokenConfig(config) ||
isNativeTokenConfig(config)) {
return defaultArgs;
}
else if (isSyntheticTokenConfig(config)) {
return [
config.initialSupply ?? 0,
config.name,
config.symbol,
...defaultArgs,
];
}
else if (isSyntheticRebaseTokenConfig(config)) {
return [0, config.name, config.symbol, ...defaultArgs];
}
else {
throw new Error('Unknown collateral type when initializing arguments');
}
}
static async deriveTokenMetadata(multiProvider, configMap) {
for (const [chain, config] of Object.entries(configMap)) {
if (isTokenMetadata(config)) {
return TokenMetadataSchema.parse(config);
}
else if (multiProvider.getProtocol(chain) !== ProtocolType.Ethereum) {
// If the config didn't specify the token metadata, we can only now
// derive it for Ethereum chains. So here we skip non-Ethereum chains.
continue;
}
if (isNativeTokenConfig(config)) {
const nativeToken = multiProvider.getChainMetadata(chain).nativeToken;
if (nativeToken) {
return TokenMetadataSchema.parse({
...nativeToken,
});
}
}
if (isCollateralTokenConfig(config) || isXERC20TokenConfig(config)) {
const provider = multiProvider.getProvider(chain);
if (config.isNft) {
const erc721 = ERC721Enumerable__factory.connect(config.token, provider);
const [name, symbol] = await Promise.all([
erc721.name(),
erc721.symbol(),
]);
return TokenMetadataSchema.parse({
name,
symbol,
});
}
let token;
switch (config.type) {
case TokenType.XERC20Lockbox:
token = await IXERC20Lockbox__factory.connect(config.token, provider).callStatic.ERC20();
break;
case TokenType.collateralVault:
token = await IERC4626__factory.connect(config.token, provider).callStatic.asset();
break;
default:
token = config.token;
break;
}
const erc20 = ERC20__factory.connect(token, provider);
const [name, symbol, decimals] = await Promise.all([
erc20.name(),
erc20.symbol(),
erc20.decimals(),
]);
return TokenMetadataSchema.parse({
name,
symbol,
decimals,
});
}
}
return undefined;
}
async deploy(configMap) {
let tokenMetadata;
try {
tokenMetadata = await TokenDeployer.deriveTokenMetadata(this.multiProvider, configMap);
}
catch (err) {
this.logger.error('Failed to derive token metadata', err, configMap);
throw err;
}
const resolvedConfigMap = objMap(configMap, (_, config) => ({
...tokenMetadata,
gas: gasOverhead(config.type),
...config,
}));
return super.deploy(resolvedConfigMap);
}
}
export class HypERC20Deployer extends TokenDeployer {
constructor(multiProvider, ismFactory, contractVerifier, concurrentDeploy = false) {
super(multiProvider, hypERC20factories, 'HypERC20Deployer', ismFactory, contractVerifier, concurrentDeploy);
}
router(contracts) {
for (const key of objKeys(hypERC20factories)) {
if (contracts[key]) {
return contracts[key];
}
}
throw new Error('No matching contract found');
}
routerContractKey(config) {
assert(config.type in hypERC20factories, 'Invalid ERC20 token type');
return config.type;
}
routerContractName(config) {
return hypERC20contracts[this.routerContractKey(config)];
}
}
export class HypERC721Deployer extends TokenDeployer {
constructor(multiProvider, ismFactory, contractVerifier) {
super(multiProvider, hypERC721factories, 'HypERC721Deployer', ismFactory, contractVerifier);
}
router(contracts) {
for (const key of objKeys(hypERC721factories)) {
if (contracts[key]) {
return contracts[key];
}
}
throw new Error('No matching contract found');
}
routerContractKey(config) {
assert(config.type in hypERC721factories, 'Invalid ERC721 token type');
return config.type;
}
routerContractName(config) {
return hypERC721contracts[this.routerContractKey(config)];
}
}
//# sourceMappingURL=deploy.js.map