From b9af9e0b2a214fd66ac6cb932230d87e2dc3a3a3 Mon Sep 17 00:00:00 2001 From: "abner.huang" Date: Tue, 24 Mar 2026 04:00:19 +0800 Subject: [PATCH 1/2] feat(link-evm-core): enhance weiDiff validation and update handling in EVM connection process - Added retrieval of ERC20 ABI and on-chain decimals to validate weiDiff. - Introduced user prompt to confirm using on-chain derived weiDiff if discrepancies are found. - Updated writeUpdatedCoreSpotDeployment to ensure weiDiff is set before proceeding. --- .../src/commands/link-evm-core.ts | 40 ++++++++++++++++++- .../hyperliquid-composer/src/io/parser.ts | 9 ++++- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/packages/hyperliquid-composer/src/commands/link-evm-core.ts b/packages/hyperliquid-composer/src/commands/link-evm-core.ts index 99800498c6..5ba44a38bb 100644 --- a/packages/hyperliquid-composer/src/commands/link-evm-core.ts +++ b/packages/hyperliquid-composer/src/commands/link-evm-core.ts @@ -1,7 +1,12 @@ import { createModuleLogger, setDefaultLogLevel } from '@layerzerolabs/io-devtools' import inquirer from 'inquirer' -import { getCoreSpotDeployment, writeUpdatedCoreSpotDeployment } from '@/io/parser' +import { + getCoreSpotDeployment, + getERC20abi, + writeNativeSpotConnected, + writeUpdatedCoreSpotDeployment, +} from '@/io/parser' import { getHyperliquidSigner } from '@/signer' import { setRequestEvmContract, setFinalizeEvmContract } from '@/operations' import { LOGGER_MODULES } from '@/types/cli-constants' @@ -48,13 +53,44 @@ export async function requestEvmContract(args: RequestEvmContractArgs): Promise< logger.verbose(`txData: \n ${JSON.stringify(txData, null, 2)}`) + const rpcUrl = isTestnet ? RPC_URLS.TESTNET : RPC_URLS.MAINNET + const provider = new ethers.providers.JsonRpcProvider({ url: rpcUrl, skipFetchSetup: true }) + const tokenAbi = await getERC20abi() + const tokenContract = new ethers.Contract(tokenAddress, tokenAbi, provider) + const onChainDecimals: number = await tokenContract.decimals() + const weiDecimals = coreSpotDeployment.coreSpot.weiDecimals + const expectedWeiDiff = onChainDecimals - weiDecimals + + if (txData.weiDiff !== expectedWeiDiff) { + logger.warn(`WARNING: txData.weiDiff (${txData.weiDiff}) does not match on-chain calculation`) + logger.warn(` ERC20 decimals (on-chain): ${onChainDecimals}`) + logger.warn(` HyperCore weiDecimals: ${weiDecimals}`) + logger.warn(` Expected evmExtraWeiDecimals: ${expectedWeiDiff}`) + logger.warn(` Stored evmExtraWeiDecimals: ${txData.weiDiff}`) + + const { useCorrectValue } = await inquirer.prompt([ + { + type: 'confirm', + name: 'useCorrectValue', + message: `Use on-chain derived value (${expectedWeiDiff}) instead of stored value (${txData.weiDiff})?`, + default: true, + }, + ]) + + if (useCorrectValue) { + txData.weiDiff = expectedWeiDiff + writeNativeSpotConnected(hyperAssetIndex, isTestnet, txData.connected, txData.weiDiff, logger) + logger.info(`Updated weiDiff to ${expectedWeiDiff} in deployment file`) + } + } + const hyperAssetIndexInt = parseInt(hyperAssetIndex) const { executeTx } = await inquirer.prompt([ { type: 'confirm', name: 'executeTx', - message: `Trying to populate a request to connect HyperCore-EVM ${hyperAssetIndex} to ${tokenAddress}. This should be sent by the Spot Deployer and before finalizeEvmContract is executed. Do you want to execute the transaction?`, + message: `Connect HyperCore-EVM ${hyperAssetIndex} to ${tokenAddress} with evmExtraWeiDecimals=${txData.weiDiff}. This value is IRREVERSIBLE after finalization. Continue?`, default: false, }, ]) diff --git a/packages/hyperliquid-composer/src/io/parser.ts b/packages/hyperliquid-composer/src/io/parser.ts index b012138a25..41c86caaeb 100644 --- a/packages/hyperliquid-composer/src/io/parser.ts +++ b/packages/hyperliquid-composer/src/io/parser.ts @@ -78,9 +78,16 @@ export function writeUpdatedCoreSpotDeployment( const fullPath = getFullPath(index.toString(), isTestnet) const spot = getCoreSpotDeployment(index, isTestnet, logger) + + if (txData.weiDiff == null) { + throw new Error( + 'weiDiff is not set in txData. Run core-spot-deployment create first to calculate the correct value.' + ) + } + spot.coreSpot.evmContract = { address: tokenAddress, - evm_extra_wei_decimals: txData.weiDiff ?? 0, + evm_extra_wei_decimals: txData.weiDiff, } spot.coreSpot.fullName = tokenFullName spot.txData.txHash = txData.txHash From cea8e5644ca9b6d23b5d8352466dcbc04501ab00 Mon Sep 17 00:00:00 2001 From: "abner.huang" Date: Tue, 24 Mar 2026 05:24:48 +0800 Subject: [PATCH 2/2] fix(link-evm-core): handle errors in on-chain decimals retrieval and maintain weiDiff integrity - Wrapped the on-chain decimals retrieval in a try-catch block to handle potential errors gracefully. - Added logging to warn when on-chain decimals fetching fails, ensuring the process continues with the stored weiDiff value. --- .../src/commands/link-evm-core.ts | 59 ++++++++++--------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/packages/hyperliquid-composer/src/commands/link-evm-core.ts b/packages/hyperliquid-composer/src/commands/link-evm-core.ts index 5ba44a38bb..471ce9ced4 100644 --- a/packages/hyperliquid-composer/src/commands/link-evm-core.ts +++ b/packages/hyperliquid-composer/src/commands/link-evm-core.ts @@ -54,34 +54,39 @@ export async function requestEvmContract(args: RequestEvmContractArgs): Promise< logger.verbose(`txData: \n ${JSON.stringify(txData, null, 2)}`) const rpcUrl = isTestnet ? RPC_URLS.TESTNET : RPC_URLS.MAINNET - const provider = new ethers.providers.JsonRpcProvider({ url: rpcUrl, skipFetchSetup: true }) - const tokenAbi = await getERC20abi() - const tokenContract = new ethers.Contract(tokenAddress, tokenAbi, provider) - const onChainDecimals: number = await tokenContract.decimals() - const weiDecimals = coreSpotDeployment.coreSpot.weiDecimals - const expectedWeiDiff = onChainDecimals - weiDecimals - - if (txData.weiDiff !== expectedWeiDiff) { - logger.warn(`WARNING: txData.weiDiff (${txData.weiDiff}) does not match on-chain calculation`) - logger.warn(` ERC20 decimals (on-chain): ${onChainDecimals}`) - logger.warn(` HyperCore weiDecimals: ${weiDecimals}`) - logger.warn(` Expected evmExtraWeiDecimals: ${expectedWeiDiff}`) - logger.warn(` Stored evmExtraWeiDecimals: ${txData.weiDiff}`) - - const { useCorrectValue } = await inquirer.prompt([ - { - type: 'confirm', - name: 'useCorrectValue', - message: `Use on-chain derived value (${expectedWeiDiff}) instead of stored value (${txData.weiDiff})?`, - default: true, - }, - ]) - - if (useCorrectValue) { - txData.weiDiff = expectedWeiDiff - writeNativeSpotConnected(hyperAssetIndex, isTestnet, txData.connected, txData.weiDiff, logger) - logger.info(`Updated weiDiff to ${expectedWeiDiff} in deployment file`) + try { + const provider = new ethers.providers.JsonRpcProvider({ url: rpcUrl, skipFetchSetup: true }) + const tokenAbi = await getERC20abi() + const tokenContract = new ethers.Contract(tokenAddress, tokenAbi, provider) + const onChainDecimals = Number(await tokenContract.decimals()) + const weiDecimals = coreSpotDeployment.coreSpot.weiDecimals + const expectedWeiDiff = onChainDecimals - weiDecimals + + if (txData.weiDiff !== expectedWeiDiff) { + logger.warn(`WARNING: txData.weiDiff (${txData.weiDiff}) does not match on-chain calculation`) + logger.warn(` ERC20 decimals (on-chain): ${onChainDecimals}`) + logger.warn(` HyperCore weiDecimals: ${weiDecimals}`) + logger.warn(` Expected evmExtraWeiDecimals: ${expectedWeiDiff}`) + logger.warn(` Stored evmExtraWeiDecimals: ${txData.weiDiff}`) + + const { useCorrectValue } = await inquirer.prompt([ + { + type: 'confirm', + name: 'useCorrectValue', + message: `Use on-chain derived value (${expectedWeiDiff}) instead of stored value (${txData.weiDiff})?`, + default: true, + }, + ]) + + if (useCorrectValue) { + txData.weiDiff = expectedWeiDiff + writeNativeSpotConnected(hyperAssetIndex, isTestnet, txData.connected, txData.weiDiff, logger) + logger.info(`Updated weiDiff to ${expectedWeiDiff} in deployment file`) + } } + } catch (error) { + logger.warn(`Failed to fetch on-chain decimals: ${error instanceof Error ? error.message : error}`) + logger.warn(`Proceeding with stored txData.weiDiff (${txData.weiDiff}) without on-chain validation`) } const hyperAssetIndexInt = parseInt(hyperAssetIndex)