Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Config override rework #6139

Open
wants to merge 3 commits into
base: v-next
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,20 @@ import type {
EdrNetworkChainConfig,
EdrNetworkChainsConfig,
EdrNetworkChainsUserConfig,
EdrNetworkConfig,
EdrNetworkForkingConfig,
EdrNetworkForkingUserConfig,
EdrNetworkMiningConfig,
EdrNetworkMiningUserConfig,
EdrNetworkUserConfig,
GasConfig,
GasUserConfig,
HttpNetworkAccountsConfig,
HttpNetworkAccountsUserConfig,
HttpNetworkConfig,
HttpNetworkUserConfig,
NetworkConfig,
NetworkUserConfig,
} from "../../../types/config.js";

import path from "node:path";
Expand All @@ -21,14 +27,163 @@ import {
hexStringToBytes,
normalizeHexString,
} from "@ignored/hardhat-vnext-utils/hex";
import { isObject } from "micro-eth-signer/utils";

import { DEFAULT_HD_ACCOUNTS_CONFIG_PARAMS } from "./accounts/constants.js";
import {
DEFAULT_EDR_NETWORK_HD_ACCOUNTS_CONFIG_PARAMS,
EDR_NETWORK_DEFAULT_COINBASE,
} from "./edr/edr-provider.js";
import { HardforkName, LATEST_HARDFORK } from "./edr/types/hardfork.js";
import { isHdAccountsUserConfig } from "./type-validation.js";
import { isHttpNetworkHdAccountsUserConfig } from "./type-validation.js";

export function resolveHttpNetwork(
networkConfig: HttpNetworkUserConfig,
resolveConfigurationVariable: ConfigurationVariableResolver,
): HttpNetworkConfig {
return {
type: "http",
accounts: resolveHttpNetworkAccounts(
networkConfig.accounts,
resolveConfigurationVariable,
),
chainId: networkConfig.chainId,
chainType: networkConfig.chainType,
from: networkConfig.from,
gas: resolveGasConfig(networkConfig.gas),
gasMultiplier: networkConfig.gasMultiplier ?? 1,
gasPrice: resolveGasConfig(networkConfig.gasPrice),
url: resolveConfigurationVariable(networkConfig.url),
timeout: networkConfig.timeout ?? 20_000,
httpHeaders: networkConfig.httpHeaders ?? {},
};
}

export function resolveEdrNetwork(
networkConfig: EdrNetworkUserConfig,
cachePath: string,
resolveConfigurationVariable: ConfigurationVariableResolver,
): EdrNetworkConfig {
return {
type: "edr",
accounts: resolveEdrNetworkAccounts(
networkConfig.accounts,
resolveConfigurationVariable,
),
chainId: networkConfig.chainId ?? 31337,
chainType: networkConfig.chainType,
from: networkConfig.from,
gas: resolveGasConfig(networkConfig.gas),
gasMultiplier: networkConfig.gasMultiplier ?? 1,
gasPrice: resolveGasConfig(networkConfig.gasPrice),

allowBlocksWithSameTimestamp:
networkConfig.allowBlocksWithSameTimestamp ?? false,
allowUnlimitedContractSize:
networkConfig.allowUnlimitedContractSize ?? false,
blockGasLimit: BigInt(networkConfig.blockGasLimit ?? 30_000_000n),
chains: resolveChains(networkConfig.chains),
coinbase: resolveCoinbase(networkConfig.coinbase),
enableRip7212: networkConfig.enableRip7212 ?? false,
enableTransientStorage: networkConfig.enableTransientStorage ?? false,
forking: resolveForkingConfig(
networkConfig.forking,
cachePath,
resolveConfigurationVariable,
),
hardfork: resolveHardfork(
networkConfig.hardfork,
networkConfig.enableTransientStorage,
),
initialBaseFeePerGas: resolveInitialBaseFeePerGas(
networkConfig.initialBaseFeePerGas,
),
initialDate: networkConfig.initialDate ?? new Date(),
loggingEnabled: networkConfig.loggingEnabled ?? false,
minGasPrice: BigInt(networkConfig.minGasPrice ?? 0),
mining: resolveMiningConfig(networkConfig.mining),
networkId: networkConfig.networkId ?? networkConfig.chainId ?? 31337,
throwOnCallFailures: networkConfig.throwOnCallFailures ?? true,
throwOnTransactionFailures:
networkConfig.throwOnTransactionFailures ?? true,
};
}

/**
* Resolves a NetworkUserConfig into a Partial<NetworkConfig> object.
* This function processes the network configuration override using the appropriate
* resolver (either HTTP or EDR) and ensures only the values explicitly specified
* in the override are included in the final result, preventing defaults from
* overwriting the user's configuration.
*
* @param networkUserConfigOverride The user's network configuration override.
* @param resolveConfigurationVariable A function to resolve configuration variables.
* @returns A Partial<NetworkConfig> containing the resolved values from the override.
*/
export function resolveNetworkConfigOverride(
networkUserConfigOverride: NetworkUserConfig,
resolveConfigurationVariable: ConfigurationVariableResolver,
): Partial<NetworkConfig> {
let resolvedConfigOverride: NetworkConfig;

if (networkUserConfigOverride.type === "http") {
resolvedConfigOverride = resolveHttpNetwork(
networkUserConfigOverride,
resolveConfigurationVariable,
);
} else {
resolvedConfigOverride = resolveEdrNetwork(
networkUserConfigOverride,
"",
resolveConfigurationVariable,
);
}

/* Return only the resolved config of the values overridden by the user. This
ensures that only the overridden values are merged into the config and
indirectly removes cacheDir from the resolved forking config, as cacheDir
is not part of the NetworkUserConfig. */
return pickResolvedFromOverrides(
resolvedConfigOverride,
networkUserConfigOverride,
);
}

function pickResolvedFromOverrides<
TResolved extends object,
TOverride extends object,
>(resolvedConfig: TResolved, overrides: TOverride): Partial<TResolved> {
const result: Partial<TResolved> = {};

for (const key of Object.keys(overrides)) {
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions
-- As TResolved and TOverride are objects share the same keys, we can
safely cast the key */
const resolvedKey = key as keyof TResolved;
const resolvedValue = resolvedConfig[resolvedKey];
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions
-- As TResolved and TOverride are objects share the same keys, we can
safely cast the key */
const overrideValue = overrides[key as keyof TOverride];

if (!(isObject(resolvedValue) && isObject(overrideValue))) {
result[resolvedKey] = resolvedValue;
continue;
}

/* Some properties in NetworkConfig, such as accounts, forking, and mining,
are objects themselves. To handle these, we process them recursively. */
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions
-- The return type adheres to TResolved[keyof TResolved], but TS can't
infer it */
result[resolvedKey] = pickResolvedFromOverrides(
resolvedValue,
overrideValue,
) as TResolved[keyof TResolved];
}

return result;
}

export function resolveGasConfig(value: GasUserConfig = "auto"): GasConfig {
return value === "auto" ? value : BigInt(value);
Expand All @@ -48,7 +203,7 @@ export function resolveHttpNetworkAccounts(
});
}

if (isHdAccountsUserConfig(accounts)) {
if (isHttpNetworkHdAccountsUserConfig(accounts)) {
const { passphrase: defaultPassphrase, ...defaultHdAccountRest } =
DEFAULT_HD_ACCOUNTS_CONFIG_PARAMS;
const { mnemonic, passphrase, ...hdAccountRest } = accounts;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,18 @@
import type {
ConfigurationVariable,
EdrNetworkConfig,
ConfigurationVariableResolver,
EdrNetworkUserConfig,
HardhatConfig,
HardhatUserConfig,
HttpNetworkConfig,
HttpNetworkUserConfig,
NetworkConfig,
NetworkUserConfig,
ResolvedConfigurationVariable,
} from "../../../../types/config.js";
import type { ConfigHooks } from "../../../../types/hooks.js";

import { HardhatError } from "@ignored/hardhat-vnext-errors";

import { GENERIC_CHAIN_TYPE } from "../../../constants.js";
import {
resolveChains,
resolveCoinbase,
resolveEdrNetworkAccounts,
resolveForkingConfig,
resolveGasConfig,
resolveHardfork,
resolveHttpNetworkAccounts,
resolveInitialBaseFeePerGas,
resolveMiningConfig,
} from "../config-resolution.js";
import { resolveEdrNetwork, resolveHttpNetwork } from "../config-resolution.js";
import { validateNetworkUserConfig } from "../type-validation.js";

export default async (): Promise<Partial<ConfigHooks>> => ({
Expand Down Expand Up @@ -76,14 +63,10 @@ export async function extendUserConfig(

export async function resolveUserConfig(
userConfig: HardhatUserConfig,
resolveConfigurationVariable: (
variableOrString: ConfigurationVariable | string,
) => ResolvedConfigurationVariable,
resolveConfigurationVariable: ConfigurationVariableResolver,
next: (
nextUserConfig: HardhatUserConfig,
nextResolveConfigurationVariable: (
variableOrString: ConfigurationVariable | string,
) => ResolvedConfigurationVariable,
nextResolveConfigurationVariable: ConfigurationVariableResolver,
) => Promise<HardhatConfig>,
): Promise<HardhatConfig> {
const resolvedConfig = await next(userConfig, resolveConfigurationVariable);
Expand All @@ -101,74 +84,14 @@ export async function resolveUserConfig(
});
}

if (networkConfig.type === "http") {
const resolvedNetworkConfig: HttpNetworkConfig = {
type: "http",
accounts: resolveHttpNetworkAccounts(
networkConfig.accounts,
resolveConfigurationVariable,
),
chainId: networkConfig.chainId,
chainType: networkConfig.chainType,
from: networkConfig.from,
gas: resolveGasConfig(networkConfig.gas),
gasMultiplier: networkConfig.gasMultiplier ?? 1,
gasPrice: resolveGasConfig(networkConfig.gasPrice),
url: resolveConfigurationVariable(networkConfig.url),
timeout: networkConfig.timeout ?? 20_000,
httpHeaders: networkConfig.httpHeaders ?? {},
};

resolvedNetworks[networkName] = resolvedNetworkConfig;
}

if (networkConfig.type === "edr") {
const resolvedNetworkConfig: EdrNetworkConfig = {
type: "edr",
accounts: resolveEdrNetworkAccounts(
networkConfig.accounts,
resolveConfigurationVariable,
),
chainId: networkConfig.chainId ?? 31337,
chainType: networkConfig.chainType,
from: networkConfig.from,
gas: resolveGasConfig(networkConfig.gas),
gasMultiplier: networkConfig.gasMultiplier ?? 1,
gasPrice: resolveGasConfig(networkConfig.gasPrice),

allowBlocksWithSameTimestamp:
networkConfig.allowBlocksWithSameTimestamp ?? false,
allowUnlimitedContractSize:
networkConfig.allowUnlimitedContractSize ?? false,
blockGasLimit: BigInt(networkConfig.blockGasLimit ?? 30_000_000n),
chains: resolveChains(networkConfig.chains),
coinbase: resolveCoinbase(networkConfig.coinbase),
enableRip7212: networkConfig.enableRip7212 ?? false,
enableTransientStorage: networkConfig.enableTransientStorage ?? false,
forking: resolveForkingConfig(
networkConfig.forking,
resolvedConfig.paths.cache,
resolveConfigurationVariable,
),
hardfork: resolveHardfork(
networkConfig.hardfork,
networkConfig.enableTransientStorage,
),
initialBaseFeePerGas: resolveInitialBaseFeePerGas(
networkConfig.initialBaseFeePerGas,
),
initialDate: networkConfig.initialDate ?? new Date(),
loggingEnabled: networkConfig.loggingEnabled ?? false,
minGasPrice: BigInt(networkConfig.minGasPrice ?? 0),
mining: resolveMiningConfig(networkConfig.mining),
networkId: networkConfig.networkId ?? networkConfig.chainId ?? 31337,
throwOnCallFailures: networkConfig.throwOnCallFailures ?? true,
throwOnTransactionFailures:
networkConfig.throwOnTransactionFailures ?? true,
};

resolvedNetworks[networkName] = resolvedNetworkConfig;
}
resolvedNetworks[networkName] =
networkConfig.type === "http"
? resolveHttpNetwork(networkConfig, resolveConfigurationVariable)
: resolveEdrNetwork(
networkConfig,
resolvedConfig.paths.cache,
resolveConfigurationVariable,
);
}

return {
Expand Down
Loading
Loading