Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ Common options (run):
- `-p, --project <path>`: Project root directory (defaults to current directory)
- `--dotenv <path>`: Load environment variables from a custom .env file (run command only)
- `-n, --network <selectors>`: Comma-separated selectors by chain ID or network name
- `--rpc-url <url>`: Run against a single custom RPC; chain ID is auto-detected (no networks.yaml required)
- `--rpc-url <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 <key>`: EOA private key (or set `PRIVATE_KEY`)
- `--etherscan-api-key <key>`: Etherscan API key (or set `ETHERSCAN_API_KEY`)
- `--fail-early`: Stop as soon as any job fails
Expand Down
17 changes: 11 additions & 6 deletions src/commands/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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
Expand Down
77 changes: 63 additions & 14 deletions src/lib/__tests__/network-utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -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({
Expand All @@ -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')
Expand All @@ -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({
Expand All @@ -174,7 +221,9 @@ describe('Network Utils', () => {
rpcUrl: 'http://custom-network:8545',
supports: [],
gasLimit: undefined,
testnet: undefined
testnet: undefined,
evmVersion: undefined,
params: undefined,
})
})
})
Expand Down
Loading