diff --git a/config/bigBlocksConfig.ts b/config/bigBlocksConfig.ts index 49db9e6..1648791 100644 --- a/config/bigBlocksConfig.ts +++ b/config/bigBlocksConfig.ts @@ -1,5 +1,7 @@ import { CHAIN_IDS } from './chainIds'; -const { HYPE_EVM_PRIVATE_KEY } = process.env; +const { PRIVATE_KEY_FOR_BATCHER_CONTRACT_DEPLOYMENT } = process.env; +const { PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT } = process.env; + /** * Configuration for a chain that supports BigBlocks */ @@ -10,6 +12,8 @@ export interface BigBlocksChainConfig { isTestnet: boolean; /** API URL for BigBlocks service */ apiUrl: string; + /** RPC URL for the network */ + rpcUrl: string; /** Chain ID for BigBlocks service */ bigBlocksChainId: number; /** Environment variable key for private key */ @@ -17,31 +21,108 @@ export interface BigBlocksChainConfig { } /** - * Map of chain IDs to their BigBlocks configuration + * Map of chain IDs to their BigBlocks configuration for V4 contracts deployment * Currently only supported on HypeEVM networks */ -export const BIGBLOCKS_SUPPORTED_CHAINS: Record = +export const BIGBLOCKS_SUPPORTED_CHAINS_V4_CONTRACTS: Record = + { + [CHAIN_IDS.HYPEEVM]: { + name: 'HypeEVM mainnet', + isTestnet: false, + apiUrl: 'https://api.hyperliquid.xyz/exchange', + rpcUrl: 'https://spectrum-01.simplystaking.xyz/hyperliquid-tn-rpc/evm', + bigBlocksChainId: 1337, + envKey: PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT + }, + [CHAIN_IDS.HYPEEVM_TESTNET]: { + name: 'HypeEVM Testnet', + isTestnet: true, + apiUrl: 'https://api.hyperliquid-testnet.xyz/exchange', + rpcUrl: 'https://spectrum-01.simplystaking.xyz/hyperliquid-tn-rpc/evm', + bigBlocksChainId: 1337, + envKey: PRIVATE_KEY_FOR_V4_CONTRACT_DEPLOYMENT + } + }; + +/** + * Map of chain IDs to their BigBlocks configuration for Batcher contract deployment + * Currently only supported on HypeEVM networks + */ +export const BIGBLOCKS_SUPPORTED_CHAINS_BATCHER: Record = { [CHAIN_IDS.HYPEEVM]: { name: 'HypeEVM mainnet', isTestnet: false, apiUrl: 'https://api.hyperliquid.xyz/exchange', + rpcUrl: 'https://spectrum-01.simplystaking.xyz/hyperliquid-tn-rpc/evm', bigBlocksChainId: 1337, - envKey: HYPE_EVM_PRIVATE_KEY + envKey: PRIVATE_KEY_FOR_BATCHER_CONTRACT_DEPLOYMENT }, [CHAIN_IDS.HYPEEVM_TESTNET]: { name: 'HypeEVM Testnet', isTestnet: true, apiUrl: 'https://api.hyperliquid-testnet.xyz/exchange', + rpcUrl: 'https://spectrum-01.simplystaking.xyz/hyperliquid-tn-rpc/evm', bigBlocksChainId: 1337, - envKey: HYPE_EVM_PRIVATE_KEY + envKey: PRIVATE_KEY_FOR_BATCHER_CONTRACT_DEPLOYMENT } }; +/** + * Legacy configuration - deprecated, use specific configurations above + * @deprecated Use BIGBLOCKS_SUPPORTED_CHAINS_V4_CONTRACTS or BIGBLOCKS_SUPPORTED_CHAINS_BATCHER instead + */ +export const BIGBLOCKS_SUPPORTED_CHAINS: Record = + BIGBLOCKS_SUPPORTED_CHAINS_V4_CONTRACTS; + +// V4 Contracts specific functions +/** + * Check if a given chain ID supports BigBlocks for V4 contracts + * @param chainId The chain ID to check + * @returns true if the chain supports BigBlocks for V4 contracts, false otherwise + */ +export const isBigBlocksSupportedV4Contracts = (chainId: number): boolean => { + return chainId in BIGBLOCKS_SUPPORTED_CHAINS_V4_CONTRACTS; +}; + +/** + * Get the BigBlocks configuration for V4 contracts deployment + * @param chainId The chain ID to get configuration for + * @returns The chain's BigBlocks configuration for V4 contracts, or undefined if not supported + */ +export const getBigBlocksConfigV4Contracts = ( + chainId: number +): BigBlocksChainConfig | undefined => { + return BIGBLOCKS_SUPPORTED_CHAINS_V4_CONTRACTS[chainId]; +}; + +// Batcher specific functions +/** + * Check if a given chain ID supports BigBlocks for Batcher contract + * @param chainId The chain ID to check + * @returns true if the chain supports BigBlocks for Batcher contract, false otherwise + */ +export const isBigBlocksSupportedBatcher = (chainId: number): boolean => { + return chainId in BIGBLOCKS_SUPPORTED_CHAINS_BATCHER; +}; + +/** + * Get the BigBlocks configuration for Batcher contract deployment + * @param chainId The chain ID to get configuration for + * @returns The chain's BigBlocks configuration for Batcher contract, or undefined if not supported + */ +export const getBigBlocksConfigBatcher = ( + chainId: number +): BigBlocksChainConfig | undefined => { + return BIGBLOCKS_SUPPORTED_CHAINS_BATCHER[chainId]; +}; + +// Legacy functions - deprecated but kept for backward compatibility /** * Check if a given chain ID supports BigBlocks * @param chainId The chain ID to check * @returns true if the chain supports BigBlocks, false otherwise + * @deprecated Use isBigBlocksSupportedV4Contracts or isBigBlocksSupportedBatcher instead */ export const isBigBlocksSupported = (chainId: number): boolean => { return chainId in BIGBLOCKS_SUPPORTED_CHAINS; @@ -51,6 +132,7 @@ export const isBigBlocksSupported = (chainId: number): boolean => { * Get the BigBlocks configuration for a chain * @param chainId The chain ID to get configuration for * @returns The chain's BigBlocks configuration, or undefined if not supported + * @deprecated Use getBigBlocksConfigV4Contracts or getBigBlocksConfigBatcher instead */ export const getBigBlocksConfig = ( chainId: number diff --git a/scripts/chainConfig.ts b/scripts/chainConfig.ts index c905249..21c83ef 100644 --- a/scripts/chainConfig.ts +++ b/scripts/chainConfig.ts @@ -99,8 +99,6 @@ export async function getChainConfig(chainId: number): Promise { case CHAIN_IDS.IRYS_TESTNET: case CHAIN_IDS.PHAROS: case CHAIN_IDS.PHAROS_TESTNET: - case CHAIN_IDS.HYPEEVM: - case CHAIN_IDS.HYPEEVM_TESTNET: case CHAIN_IDS.APECHAIN: case CHAIN_IDS.APECHAIN_TESTNET: case CHAIN_IDS.CORE_DAO: @@ -117,7 +115,16 @@ export async function getChainConfig(chainId: number): Promise { forwarderContractName = 'ForwarderV4'; forwarderFactoryContractName = 'ForwarderFactoryV4'; break; - + case CHAIN_IDS.HYPEEVM: + case CHAIN_IDS.HYPEEVM_TESTNET: + gasParams = { + maxFeePerGas: 30_000_000_000n, + maxPriorityFeePerGas: 30_000_000_000n, + gasLimit: 3_000_000 + }; + forwarderContractName = 'ForwarderV4'; + forwarderFactoryContractName = 'ForwarderFactoryV4'; + break; case CHAIN_IDS.WORLD: case CHAIN_IDS.WORLD_TESTNET: gasParams = { diff --git a/scripts/deploy.ts b/scripts/deploy.ts index ac421b1..f720c5d 100644 --- a/scripts/deploy.ts +++ b/scripts/deploy.ts @@ -9,8 +9,8 @@ import { } from '../deployUtils'; import { enableBigBlocks } from './enableBigBlocks'; import { - getBigBlocksConfig, - isBigBlocksSupported + getBigBlocksConfigV4Contracts, + isBigBlocksSupportedV4Contracts } from '../config/bigBlocksConfig'; const NONCE = { @@ -20,25 +20,149 @@ const NONCE = { FORWARDER_FACTORY: 3 }; +// Add interface for JSON RPC response +interface JsonRpcResponse { + jsonrpc: string; + id: number; + result?: boolean; + error?: { + code: number; + message: string; + }; +} + +/** + * Check if BigBlocks is already enabled using RPC call + */ +async function checkBigBlocksStatus( + userAddress: string, + chainId: number +): Promise { + const config = getBigBlocksConfigV4Contracts(chainId); + if (!config) { + throw new Error(`Chain with ID ${chainId} is not supported for BigBlocks.`); + } + console.log('Useradd' + userAddress); + console.log( + `Checking BigBlocks status for ${userAddress} on ${config.name}...` + ); + console.log(`Making RPC call to: ${config.rpcUrl}`); + try { + const requestBody = { + jsonrpc: '2.0', + id: 0, + method: 'eth_usingBigBlocks', + params: [userAddress] + }; + + const res = await fetch(config.rpcUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(requestBody) + }); + + if (!res.ok) { + throw new Error(`HTTP Error: ${res.status} ${await res.text()}`); + } + + const result = (await res.json()) as JsonRpcResponse; + + console.log(result); + + if (result.error) { + throw new Error( + `RPC Error: ${result.error.code} - ${result.error.message}` + ); + } + + return result.result || false; + } catch (err) { + console.error('Failed to fetch BigBlocks status.'); + throw err; + } +} + +/** + * Enable BigBlocks with retry mechanism + */ +async function enableBigBlocksWithRetry( + config: any, + chainId: number, + maxRetries: number = 3 +): Promise { + for (let attempt = 1; attempt <= maxRetries; attempt++) { + try { + console.log( + ` Attempt ${attempt}/${maxRetries}: Enabling BigBlocks on ${config.name}` + ); + await enableBigBlocks(config.envKey, true, chainId); + console.log(` BigBlocks enabled on ${config.name} (attempt ${attempt})`); + return; + } catch (error) { + console.log( + `Attempt ${attempt}/${maxRetries} failed:`, + (error as Error).message + ); + + if (attempt === maxRetries) { + throw new Error( + `Failed to enable BigBlocks on ${ + config.name + } after ${maxRetries} attempts: ${(error as Error).message}` + ); + } + + // Wait 2 seconds before retry + console.log(' Waiting 2 seconds before retry...'); + await new Promise((resolve) => setTimeout(resolve, 2000)); + } + } +} + /** - * Configure BigBlocks for HypeEVM network + * Setup BigBlocks for a specific chain */ -async function setupBigBlocks(chainId: number): Promise { - const config = getBigBlocksConfig(chainId); +async function setupBigBlocks( + chainId: number, + deployerAddress: string +): Promise { + const config = getBigBlocksConfigV4Contracts(chainId); if (!config) return; if (!config.envKey) { throw new Error(`Please set the private key for ${config.name}.`); } - console.log(`Using BigBlocks on ${config.name}`); - try { - await enableBigBlocks(config.envKey, true, chainId); - } catch (error) { + console.log(` Checking BigBlocks status on ${config.name}...`); + + // Check if BigBlocks is already enabled + const isEnabled = await checkBigBlocksStatus(deployerAddress, chainId); + + if (isEnabled) { + console.log(`BigBlocks already enabled on ${config.name}`); + return; + } + + console.log( + ` BigBlocks not enabled on ${config.name}, attempting to enable...` + ); + + // Try to enable BigBlocks with retry mechanism + await enableBigBlocksWithRetry(config, chainId, 3); + + // Verify it was enabled successfully + console.log(`Verifying BigBlocks was enabled...`); + const isEnabledAfter = await checkBigBlocksStatus(deployerAddress, chainId); + + if (!isEnabledAfter) { throw new Error( - `Failed to setup BigBlocks on ${config.name}: ${(error as Error).message}` + `BigBlocks enable command succeeded but verification failed on ${config.name}` ); } + + console.log(`BigBlocks successfully verified as enabled on ${config.name}`); } async function main() { @@ -47,15 +171,16 @@ async function main() { const currentNonce = await ethers.provider.getTransactionCount( deployerAddress ); - const { chainId } = await ethers.provider.getNetwork(); // More direct way to get chainId + const { chainId } = await ethers.provider.getNetwork(); const chainConfig = await getChainConfig(Number(chainId)); const output: DeploymentAddresses = loadOutput(); const gasOverrides = chainConfig.gasParams; - if (isBigBlocksSupported(Number(chainId))) { - console.log('🔄 Setting up BigBlocks...'); - await setupBigBlocks(Number(chainId)); + // Handle BigBlocks setup automatically if supported + if (isBigBlocksSupportedV4Contracts(Number(chainId))) { + console.log('🔍 BigBlocks supported on this chain, checking status...'); + await setupBigBlocks(Number(chainId), deployerAddress); } console.log( @@ -105,7 +230,7 @@ async function main() { const WalletFactory = await ethers.getContractFactory( chainConfig.walletFactoryContractName ); - const contract = await WalletFactory.deploy(walletAddress, gasOverrides); // constructor args + overrides + const contract = await WalletFactory.deploy(walletAddress, gasOverrides); await contract.waitForDeployment(); console.log( `✅ ${chainConfig.walletFactoryContractName} deployed at ${contract.target}` @@ -132,7 +257,7 @@ async function main() { const Forwarder = await ethers.getContractFactory( chainConfig.forwarderContractName ); - const contract = await Forwarder.deploy(gasOverrides); // overrides only + const contract = await Forwarder.deploy(gasOverrides); await contract.waitForDeployment(); console.log( `✅ ${chainConfig.forwarderContractName} deployed at ${contract.target}` @@ -157,7 +282,7 @@ async function main() { const contract = await ForwarderFactory.deploy( forwarderAddress, gasOverrides - ); // constructor args + overrides + ); await contract.waitForDeployment(); console.log( `✅ ${chainConfig.forwarderFactoryContractName} deployed at ${contract.target}` diff --git a/scripts/deployBatcherContract.ts b/scripts/deployBatcherContract.ts index 7196127..77b76f3 100644 --- a/scripts/deployBatcherContract.ts +++ b/scripts/deployBatcherContract.ts @@ -2,6 +2,11 @@ import hre, { ethers } from 'hardhat'; import { Contract } from 'ethers'; import { logger, waitAndVerify } from '../deployUtils'; import fs from 'fs'; +import { enableBigBlocks } from './enableBigBlocks'; +import { + getBigBlocksConfigBatcher, + isBigBlocksSupportedBatcher +} from '../config/bigBlocksConfig'; // Minimal tx override type compatible with ethers v6 type TxOverrides = { @@ -11,6 +16,158 @@ type TxOverrides = { gasPrice?: bigint; }; +// Add interface for JSON RPC response +interface JsonRpcResponse { + jsonrpc: string; + id: number; + result?: boolean; + error?: { + code: number; + message: string; + }; +} + +/** + * Check if BigBlocks is already enabled using RPC call + */ +async function checkBigBlocksStatus( + userAddress: string, + chainId: number +): Promise { + const config = getBigBlocksConfigBatcher(chainId); + if (!config) { + throw new Error(`Chain with ID ${chainId} is not supported for BigBlocks.`); + } + + logger.info( + `🔍 Checking BigBlocks status for ${userAddress} on ${config.name}...` + ); + logger.info(`📡 Making RPC call to: ${config.rpcUrl}`); + + try { + const requestBody = { + jsonrpc: '2.0', + id: 0, + method: 'eth_usingBigBlocks', + params: [userAddress] + }; + + const res = await fetch(config.rpcUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(requestBody) + }); + + if (!res.ok) { + throw new Error(`HTTP Error: ${res.status} ${await res.text()}`); + } + + const result = (await res.json()) as JsonRpcResponse; + logger.info(`đŸ“Ĩ RPC Response: ${JSON.stringify(result)}`); + + if (result.error) { + throw new Error( + `RPC Error: ${result.error.code} - ${result.error.message}` + ); + } + + return result.result || false; + } catch (err) { + logger.error( + `❌ Failed to fetch BigBlocks status: ${(err as Error).message}` + ); + throw err; + } +} + +/** + * Enable BigBlocks with retry mechanism + */ +async function enableBigBlocksWithRetry( + config: any, + chainId: number, + maxRetries: number = 3 +): Promise { + for (let attempt = 1; attempt <= maxRetries; attempt++) { + try { + logger.info( + `🔄 Attempt ${attempt}/${maxRetries}: Enabling BigBlocks on ${config.name}` + ); + await enableBigBlocks(config.envKey, true, chainId); + logger.success( + `✅ BigBlocks enabled on ${config.name} (attempt ${attempt})` + ); + return; + } catch (error) { + logger.warn( + `❌ Attempt ${attempt}/${maxRetries} failed: ${ + (error as Error).message + }` + ); + + if (attempt === maxRetries) { + throw new Error( + `Failed to enable BigBlocks on ${ + config.name + } after ${maxRetries} attempts: ${(error as Error).message}` + ); + } + + // Wait 2 seconds before retry + logger.info('âŗ Waiting 2 seconds before retry...'); + await new Promise((resolve) => setTimeout(resolve, 2000)); + } + } +} + +/** + * Setup BigBlocks for a specific chain + */ +async function setupBigBlocks( + chainId: number, + deployerAddress: string +): Promise { + const config = getBigBlocksConfigBatcher(chainId); + if (!config) return; + + if (!config.envKey) { + throw new Error(`Please set the private key for ${config.name}.`); + } + + logger.info(`🔧 Checking BigBlocks status on ${config.name}...`); + + // Check if BigBlocks is already enabled + const isEnabled = await checkBigBlocksStatus(deployerAddress, chainId); + + if (isEnabled) { + logger.success(`✅ BigBlocks already enabled on ${config.name}`); + return; + } + + logger.info( + `🔄 BigBlocks not enabled on ${config.name}, attempting to enable...` + ); + + // Try to enable BigBlocks with retry mechanism + await enableBigBlocksWithRetry(config, chainId, 3); + + // Verify it was enabled successfully + logger.info(`🔍 Verifying BigBlocks was enabled...`); + const isEnabledAfter = await checkBigBlocksStatus(deployerAddress, chainId); + + if (!isEnabledAfter) { + throw new Error( + `BigBlocks enable command succeeded but verification failed on ${config.name}` + ); + } + + logger.success( + `🎉 BigBlocks successfully verified as enabled on ${config.name}` + ); +} + async function main() { logger.step('🚀 Starting Batcher Contract Deployment 🚀'); @@ -81,6 +238,23 @@ async function main() { logger.info(`Network: ${hre.network.name} (Chain ID: ${chainId})`); logger.info(`Deployer Address: ${address}`); + // --- 1.5. BigBlocks Setup --- + logger.step('1.5. Checking and setting up BigBlocks if supported...'); + + if (isBigBlocksSupportedBatcher(Number(chainId))) { + logger.info('🔍 BigBlocks supported on this chain, checking status...'); + try { + await setupBigBlocks(Number(chainId), address); + logger.success('✅ BigBlocks setup completed successfully'); + } catch (error) { + logger.warn('âš ī¸ BigBlocks setup failed after all retry attempts'); + logger.warn(`BigBlocks error: ${(error as Error).message}`); + logger.info('đŸ“Ļ Continuing with deployment without BigBlocks...'); + } + } else { + logger.info('â„šī¸ BigBlocks not supported on this chain, skipping...'); + } + // --- 2. Gas Parameter Handling --- logger.step('2. Configuring gas parameters for the transaction...'); diff --git a/scripts/enableBigBlocks.ts b/scripts/enableBigBlocks.ts index 1032734..cab5d33 100644 --- a/scripts/enableBigBlocks.ts +++ b/scripts/enableBigBlocks.ts @@ -1,4 +1,4 @@ -import { Wallet } from 'ethers'; +import { getBytes, Wallet } from 'ethers'; import { keccak_256 } from '@noble/hashes/sha3'; import { encode } from '@msgpack/msgpack'; import { hexToBytes, bytesToHex, concatBytes } from './secp256k1Wrapper'; @@ -36,6 +36,7 @@ interface SignL1ActionParams { function getBigBlocksUrl(chainId: number): string { const config = getBigBlocksConfig(chainId); + console.log(`BigBlocks config for chain ${chainId}:`, config); if (!config) throw new Error(`Chain ${chainId} does not support BigBlocks`); return config.apiUrl; } @@ -121,10 +122,10 @@ async function signL1Action({ const message = { source: config.isTestnet ? 'b' : 'a', - connectionId: arrayify(connectionId) + connectionId: getBytes(connectionId) }; - const signature = await wallet._signTypedData(domain, types, message); + const signature = await wallet.signTypedData(domain, types, message); const r = '0x' + signature.slice(2, 66); const s = '0x' + signature.slice(66, 130); @@ -160,6 +161,7 @@ export async function enableBigBlocks( }); const apiUrl = getBigBlocksUrl(chainId); + console.log(apiUrl); const res = await fetch(apiUrl, { method: 'POST', headers: {