UNPKG

@layerzerolabs/hyperliquid-composer

Version:

LayerZero Labs reference EVM OmniChain Fungible Token (OFT) implementation for Hyperliquid

196 lines (159 loc) 6.99 kB
import fs from 'fs' import path from 'path' import { importDefault, Logger } from '@layerzerolabs/io-devtools' import { EndpointId } from '@layerzerolabs/lz-definitions' import type { OAppOmniGraphHardhat } from '@layerzerolabs/toolbox-hardhat' import type { HardhatUserConfig, NetworkUserConfig } from 'hardhat/types' import type { CoreSpotDeployment, TxData } from '@/types' function getFullPath(index: string, isTestnet: boolean): string { return path.join(process.cwd(), 'deployments', `hypercore-${isTestnet ? 'testnet' : 'mainnet'}`, `${index}.json`) } export function getCoreSpotDeployment(index: string | number, isTestnet: boolean, logger?: Logger): CoreSpotDeployment { const fullPath = getFullPath(index.toString(), isTestnet) const nativeSpot = fs.readFileSync(fullPath, 'utf8') if (!nativeSpot) { const errMsg = `Native spot ${index} not found - make sure the native spot for the token ${index} is found at ${fullPath}` logger?.error(errMsg) throw new Error(errMsg) } return JSON.parse(nativeSpot) as CoreSpotDeployment } export function writeCoreSpotDeployment( index: string | number, isTestnet: boolean, coreSpotDeployment: CoreSpotDeployment, logger?: Logger ) { const fullPath = getFullPath(index.toString(), isTestnet) const dir = path.dirname(fullPath) if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }) } fs.writeFileSync(fullPath, JSON.stringify(coreSpotDeployment, null, 2)) logger?.info(`Wrote deployment ${fullPath}`) } export function writeUpdatedCoreSpotDeployment( index: string | number, isTestnet: boolean, tokenFullName: string, tokenAddress: string, txData: TxData, logger?: Logger ) { const fullPath = getFullPath(index.toString(), isTestnet) const spot = getCoreSpotDeployment(index, isTestnet, logger) spot.coreSpot.evmContract = { address: tokenAddress, evm_extra_wei_decimals: txData.weiDiff ?? 0, } spot.coreSpot.fullName = tokenFullName spot.txData.txHash = txData.txHash spot.txData.nonce = txData.nonce spot.txData.from = txData.from spot.txData.connected = txData.connected spot.txData.weiDiff = txData.weiDiff fs.writeFileSync(fullPath, JSON.stringify(spot, null, 2)) logger?.info(`Updated core spot ${index}`) } export function writeNativeSpotConnected( index: string | number, isTestnet: boolean, connected: boolean, weiDiff: number, logger?: Logger ) { const fullPath = getFullPath(index.toString(), isTestnet) const spot = getCoreSpotDeployment(index, isTestnet, logger) spot.txData.connected = connected spot.txData.weiDiff = weiDiff fs.writeFileSync(fullPath, JSON.stringify(spot, null, 2)) logger?.info(`Updated core spot ${index}`) } export function updateFreezePrivilegeStatus( index: string | number, isTestnet: boolean, enabled: boolean, logger?: Logger ) { const fullPath = getFullPath(index.toString(), isTestnet) const spot = getCoreSpotDeployment(index, isTestnet, logger) spot.coreSpot.freezePrivilegeEnabled = enabled fs.writeFileSync(fullPath, JSON.stringify(spot, null, 2)) logger?.info(`Updated freeze privilege status for core spot ${index}: ${enabled ? 'enabled' : 'revoked'}`) } export function updateQuoteTokenStatus(index: string | number, isTestnet: boolean, enabled: boolean, logger?: Logger) { const fullPath = getFullPath(index.toString(), isTestnet) const spot = getCoreSpotDeployment(index, isTestnet, logger) spot.coreSpot.quoteAssetEnabled = enabled fs.writeFileSync(fullPath, JSON.stringify(spot, null, 2)) logger?.info(`Updated quote token status for core spot ${index}: ${enabled ? 'enabled' : 'disabled'}`) } export function updateUserFreezeStatus( index: string | number, isTestnet: boolean, userAddress: string, frozen: boolean, logger?: Logger ) { const fullPath = getFullPath(index.toString(), isTestnet) const spot = getCoreSpotDeployment(index, isTestnet, logger) // Ensure blacklistUsers array exists if (!spot.userGenesis.blacklistUsers) { spot.userGenesis.blacklistUsers = [] } // Normalize user address - hyperliquid indexes based on lowercase addresses const normalizedAddress = userAddress.toLowerCase() // Remove existing entry for this user spot.userGenesis.blacklistUsers = spot.userGenesis.blacklistUsers.filter((addr) => addr !== normalizedAddress) // Add user to blacklist if frozen if (frozen) { spot.userGenesis.blacklistUsers.push(normalizedAddress) } fs.writeFileSync(fullPath, JSON.stringify(spot, null, 2)) logger?.info( `Updated freeze status for user ${normalizedAddress} in core spot ${index}: ${frozen ? 'frozen' : 'unfrozen'}` ) } export async function getHyperEVMOAppDeployment( oapp_config: string, network: string, logger?: Logger ): Promise<{ contractName: string; deployment: string }> { if (oapp_config == undefined) { logger?.info(`oapp-config not supplied. prompting user for inputs`) throw new Error(`oapp-config not found for ${network}`) } const targetEid = network === 'testnet' ? EndpointId.HYPERLIQUID_V2_TESTNET.valueOf() : EndpointId.HYPERLIQUID_V2_MAINNET.valueOf() const lzConfigPath = path.join(process.cwd(), oapp_config) logger?.info(`LZ config path: ${lzConfigPath}`) const lzConfig = await importDefault(lzConfigPath) const contracts = (lzConfig as OAppOmniGraphHardhat).contracts const contractName = contracts.find((contract) => contract.contract.eid === targetEid)?.contract.contractName if (!contractName) { throw new Error(`HyperEVM deployment not found for ${targetEid}`) } const hardhatConfigPath = path.join(process.cwd(), 'hardhat.config.ts') logger?.info(`Hardhat config path: ${hardhatConfigPath}`) const hardhatConfig = await importDefault(hardhatConfigPath) const networks = (hardhatConfig as HardhatUserConfig).networks const networkKey = Object.keys(networks as NetworkUserConfig).find((key) => networks?.[key]?.eid === targetEid) if (!networkKey) { throw new Error(`Network not found for ${targetEid}`) } const deploymentFilePath = path.join(process.cwd(), 'deployments', networkKey, `${contractName}.json`) logger?.info(`HyperEVM-${network} deployment file: ${deploymentFilePath}`) const deploymentFile = JSON.parse(fs.readFileSync(deploymentFilePath, 'utf8')) return { contractName: contractName, deployment: deploymentFile, } } // eslint-disable-next-line @typescript-eslint/no-explicit-any export async function getERC20abi(): Promise<any> { const erc20Path = path.join(__dirname, '..', 'src', 'types', 'ERC20.json') const erc20Abi = JSON.parse(fs.readFileSync(erc20Path, 'utf8')) return erc20Abi['abi'] }