From c0f031735a5615524db7cde388bea6e4583025f6 Mon Sep 17 00:00:00 2001 From: hpmaxi <358059+hpmaxi@users.noreply.github.com> Date: Thu, 9 Apr 2026 16:16:12 -0300 Subject: [PATCH] fix: gracefully pass through provider requests when FHEVM is not initialized When the FHEVM plugin is not deployed, non-FHE tasks (e.g. contract.pause()) would crash with "The Hardhat Fhevm plugin is not initialized" on any revert. - Add tryGetContractsRepository() that returns undefined instead of throwing - Skip error formatting/stack rewriting when contracts repo is unavailable - Bypass FHEVM provider handling entirely when isDeployed is false Fixes zama-ai/fhevm-mocks#80 --- .../src/internal/FhevmEnvironment.ts | 5 +++ .../src/internal/errors/FhevmContractError.ts | 33 ++++++++++++------- .../provider/FhevmProviderExtender.ts | 10 ++++-- 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/packages/hardhat-plugin/src/internal/FhevmEnvironment.ts b/packages/hardhat-plugin/src/internal/FhevmEnvironment.ts index b60f506..760b44e 100644 --- a/packages/hardhat-plugin/src/internal/FhevmEnvironment.ts +++ b/packages/hardhat-plugin/src/internal/FhevmEnvironment.ts @@ -498,6 +498,11 @@ export class FhevmEnvironment { return this._contractsRepository; } + // Non-throwing variant for use in error-handling paths where FHEVM may not be initialized. + public tryGetContractsRepository(): contracts.FhevmContractsRepository | undefined { + return this._contractsRepository; + } + public get isDeployed(): boolean { return this._deployCompleted; } diff --git a/packages/hardhat-plugin/src/internal/errors/FhevmContractError.ts b/packages/hardhat-plugin/src/internal/errors/FhevmContractError.ts index 75a1bf7..9956cd2 100644 --- a/packages/hardhat-plugin/src/internal/errors/FhevmContractError.ts +++ b/packages/hardhat-plugin/src/internal/errors/FhevmContractError.ts @@ -225,9 +225,12 @@ function _parseEdrError( return undefined; } - const fhevmContractEntry = fhevmEnv - .getContractsRepository() - .getContractFromAddress(lastCallContractAddress)?.properties; + const repo = fhevmEnv.tryGetContractsRepository(); + if (!repo) { + return undefined; + } + + const fhevmContractEntry = repo.getContractFromAddress(lastCallContractAddress)?.properties; if (!fhevmContractEntry) { return undefined; } @@ -308,14 +311,17 @@ export async function mutateErrorInPlace(fhevmEnv: FhevmEnvironment, e: Error, a const i = err.stack.indexOf("\n"); err.stack = "Error: " + err.message + err.stack.substring(i); - const map = fhevmEnv.getContractsRepository().addressToContractMap(); + const repo = fhevmEnv.tryGetContractsRepository(); + if (repo) { + const map = repo.addressToContractMap(); - Object.keys(map).forEach((contractAddress) => { - err.stack = err.stack?.replaceAll( - `at . (${contractAddress})`, - `at ${map[contractAddress].name}. (${contractAddress}, ${map[contractAddress].package}/contracts/${map[contractAddress].name}.sol:0:0)`, - ); - }); + Object.keys(map).forEach((contractAddress) => { + err.stack = err.stack?.replaceAll( + `at . (${contractAddress})`, + `at ${map[contractAddress].name}. (${contractAddress}, ${map[contractAddress].package}/contracts/${map[contractAddress].name}.sol:0:0)`, + ); + }); + } } /** @@ -417,7 +423,12 @@ async function __formatFhevmErrorMessages( txHash?: string, txFromTo?: { from: string; to: string | null }, ): Promise { - const map = fhevmEnv.getContractsRepository().addressToContractMap(); + const repo = fhevmEnv.tryGetContractsRepository(); + if (!repo) { + return undefined; + } + + const map = repo.addressToContractMap(); const res: { errorDesc: EthersT.ErrorDescription; contractWrapper: contracts.FhevmContractWrapper; diff --git a/packages/hardhat-plugin/src/internal/provider/FhevmProviderExtender.ts b/packages/hardhat-plugin/src/internal/provider/FhevmProviderExtender.ts index 4edf279..d76b9a5 100644 --- a/packages/hardhat-plugin/src/internal/provider/FhevmProviderExtender.ts +++ b/packages/hardhat-plugin/src/internal/provider/FhevmProviderExtender.ts @@ -36,8 +36,14 @@ export class FhevmProviderExtender extends ProviderWrapper { } public async request(args: RequestArguments) { - // test init - // if not init forward! + // When FHEVM is not deployed, pass all requests through unchanged. + // This allows non-FHE tasks and scripts to work without initializing + // the FHEVM module (e.g. contract.pause(), custom hardhat tasks, etc.) + // See: https://github.com/zama-ai/fhevm-mocks/issues/80 + if (!fhevmContext.fhevmEnv?.isDeployed) { + return this._wrappedProvider.request(args); + } + switch (args.method) { // window.ethereum case "eth_estimateGas":