diff --git a/README.md b/README.md index 681e75b..518ab6f 100644 --- a/README.md +++ b/README.md @@ -388,7 +388,7 @@ Common options (run): - `-p, --project `: Project root directory (defaults to current directory) - `--dotenv `: Load environment variables from a custom .env file (run command only) - `-n, --network `: Comma-separated selectors by chain ID or network name -- `--rpc-url `: Run against a single custom RPC; chain ID is auto-detected (no networks.yaml required) +- `--rpc-url `: Run against a single custom RPC; chain ID is auto-detected. If `networks.yaml` defines that chain, Catapult merges yaml settings (name, `supports`, `gasLimit`, `testnet`, `evmVersion`, `params`) while using your RPC URL. - `-k, --private-key `: EOA private key (or set `PRIVATE_KEY`) - `--etherscan-api-key `: Etherscan API key (or set `ETHERSCAN_API_KEY`) - `--fail-early`: Stop as soon as any job fails diff --git a/src/commands/run.ts b/src/commands/run.ts index de05eb0..8fed50d 100644 --- a/src/commands/run.ts +++ b/src/commands/run.ts @@ -2,8 +2,8 @@ import { Command } from 'commander' import { Deployer, DeployerOptions } from '../lib/deployer' import { loadNetworks } from '../lib/network-loader' import { detectNetworkFromRpc, isValidRpcUrl } from '../lib/network-utils' -import { deploymentEvents } from '../lib/events' import { Network } from '../lib/types' +import { deploymentEvents } from '../lib/events' import { projectOption, dotenvOption, noStdOption, verbosityOption, loadDotenv } from './common' import { resolveSelectedChainIds } from '../lib/network-selection' import { setVerbosity } from '../index' @@ -76,15 +76,20 @@ export function makeRunCommand(): Command { // Detect network information from RPC URL const detectedNetwork = await detectNetworkFromRpc(options.rpcUrl) - // Create a complete network object + // Try to find a matching network in networks.yaml by chainId to merge verification platform support + const knownNetwork = networks.find(n => n.chainId === detectedNetwork.chainId) + + // Create a complete network object, merging with known network config if found const customNetwork: Network = { - name: detectedNetwork.name || `custom-${detectedNetwork.chainId}`, + name: detectedNetwork.name || knownNetwork?.name || `custom-${detectedNetwork.chainId}`, chainId: detectedNetwork.chainId!, rpcUrl: options.rpcUrl, // Optional fields with defaults - supports: detectedNetwork.supports || [], - gasLimit: detectedNetwork.gasLimit, - testnet: detectedNetwork.testnet + supports: detectedNetwork.supports || knownNetwork?.supports || [], + gasLimit: detectedNetwork.gasLimit || knownNetwork?.gasLimit, + testnet: detectedNetwork.testnet !== undefined ? detectedNetwork.testnet : knownNetwork?.testnet, + evmVersion: detectedNetwork.evmVersion || knownNetwork?.evmVersion, + params: detectedNetwork.params || knownNetwork?.params, } // Custom network created from RPC detection diff --git a/src/lib/__tests__/network-utils.spec.ts b/src/lib/__tests__/network-utils.spec.ts index c0ff95b..1521688 100644 --- a/src/lib/__tests__/network-utils.spec.ts +++ b/src/lib/__tests__/network-utils.spec.ts @@ -108,16 +108,15 @@ describe('Network Utils', () => { describe('Integration with Run Command', () => { it('should create a complete Network object from detected information', async () => { const { ethers } = require('ethers') - - // Mock successful network detection + const mockNetwork = { name: 'sepolia', - chainId: 11155111 + chainId: 11155111, } - + const getNetworkMock = jest.fn().mockResolvedValue(mockNetwork) ethers.JsonRpcProvider.mockImplementation(() => ({ - getNetwork: getNetworkMock + getNetwork: getNetworkMock, })) const detectedInfo = await detectNetworkFromRpc('https://sepolia.infura.io/v3/abc123') @@ -129,7 +128,9 @@ describe('Network Utils', () => { rpcUrl: 'https://sepolia.infura.io/v3/abc123', supports: detectedInfo.supports || [], gasLimit: detectedInfo.gasLimit, - testnet: detectedInfo.testnet + testnet: detectedInfo.testnet, + evmVersion: detectedInfo.evmVersion, + params: detectedInfo.params, } expect(customNetwork).toEqual({ @@ -138,22 +139,66 @@ describe('Network Utils', () => { rpcUrl: 'https://sepolia.infura.io/v3/abc123', supports: [], gasLimit: undefined, - testnet: undefined + testnet: undefined, + evmVersion: undefined, + params: undefined, + }) + }) + + it('should merge params and other yaml fields when chainId matches networks.yaml', async () => { + const { ethers } = require('ethers') + + const getNetworkMock = jest.fn().mockResolvedValue({ name: 'unknown', chainId: 137 }) + ethers.JsonRpcProvider.mockImplementation(() => ({ + getNetwork: getNetworkMock, + })) + + const detectedInfo = await detectNetworkFromRpc('http://127.0.0.1:8545') + const knownNetwork: Network = { + name: 'Polygon', + chainId: 137, + rpcUrl: 'https://nodes.sequence.app/polygon/token', + supports: ['etherscan_v2', 'sourcify'], + testnet: false, + evmVersion: 'paris', + params: { trailsOnly: false }, + } + + // Simulate what the run command does + const customNetwork: Network = { + name: detectedInfo.name || knownNetwork.name || `custom-${detectedInfo.chainId}`, + chainId: detectedInfo.chainId!, + rpcUrl: 'http://127.0.0.1:8545', + supports: detectedInfo.supports || knownNetwork.supports || [], + gasLimit: detectedInfo.gasLimit || knownNetwork.gasLimit, + testnet: detectedInfo.testnet !== undefined ? detectedInfo.testnet : knownNetwork.testnet, + evmVersion: detectedInfo.evmVersion || knownNetwork.evmVersion, + params: detectedInfo.params || knownNetwork.params, + } + + expect(customNetwork).toEqual({ + name: 'unknown', + chainId: 137, + rpcUrl: 'http://127.0.0.1:8545', + supports: ['etherscan_v2', 'sourcify'], + gasLimit: undefined, + testnet: false, + evmVersion: 'paris', + params: { trailsOnly: false }, }) }) it('should handle partial network information gracefully', async () => { const { ethers } = require('ethers') - - // Mock network with minimal information + const mockNetwork = { name: 'unknown', - chainId: 42 + chainId: 42, } - + const getNetworkMock = jest.fn().mockResolvedValue(mockNetwork) ethers.JsonRpcProvider.mockImplementation(() => ({ - getNetwork: getNetworkMock + getNetwork: getNetworkMock, })) const detectedInfo = await detectNetworkFromRpc('http://custom-network:8545') @@ -165,7 +210,9 @@ describe('Network Utils', () => { rpcUrl: 'http://custom-network:8545', supports: detectedInfo.supports || [], gasLimit: detectedInfo.gasLimit, - testnet: detectedInfo.testnet + testnet: detectedInfo.testnet, + evmVersion: detectedInfo.evmVersion, + params: detectedInfo.params, } expect(customNetwork).toEqual({ @@ -174,7 +221,9 @@ describe('Network Utils', () => { rpcUrl: 'http://custom-network:8545', supports: [], gasLimit: undefined, - testnet: undefined + testnet: undefined, + evmVersion: undefined, + params: undefined, }) }) })