diff --git a/v-next/hardhat-viem/src/internal/chains.ts b/v-next/hardhat-viem/src/internal/chains.ts index d54e06e441b..3fac0cbe7e1 100644 --- a/v-next/hardhat-viem/src/internal/chains.ts +++ b/v-next/hardhat-viem/src/internal/chains.ts @@ -15,6 +15,7 @@ import { hardhat, anvil, optimism } from "viem/chains"; -- TODO: this assertion should not be necessary */ const chains = Object.values(chainsModule) as ViemChain[]; +const chainCache = new WeakMap(); const chainIdCache = new WeakMap(); const isHardhatNetworkCache = new WeakMap(); const isAnvilNetworkCache = new WeakMap(); @@ -26,47 +27,55 @@ export async function getChain( provider: EthereumProvider, chainType: ChainType | string, ): Promise { + const cachedChain = chainCache.get(provider); + if (cachedChain !== undefined) { + return cachedChain; + } + const chainId = await getChainId(provider); - const chain = extractChain({ + let chain = extractChain({ chains, id: chainId, }); if ((await isDevelopmentNetwork(provider)) || chain === undefined) { if (await isHardhatNetwork(provider)) { + // TODO: We shoud improve how we handle the chains for the different chain + // types, as this is both a hardhat and an optimism chain. + // + // We are currently creating our chain based off optimism's, but that's + // not always the correct behavior, as the user may be connecting to + // a different chain. if (chainType === "optimism") { - // TODO: We may need a better way to merge this info. - return { ...optimism, id: chainId }; + chain = { ...optimism, id: chainId }; + } else { + chain = { + ...hardhat, + id: chainId, + }; } - - return { - ...hardhat, - id: chainId, - }; - } - - if (await isAnvilNetwork(provider)) { - return { + } else if (await isAnvilNetwork(provider)) { + chain = { ...anvil, id: chainId, }; - } - - // If the chain couldn't be found and we can't detect the development - // network we throw an error - if (chain === undefined) { + } else if (chain === undefined) { + // If the chain couldn't be found and we can't detect the development + // network we throw an error. throw new HardhatError(HardhatError.ERRORS.VIEM.NETWORK_NOT_FOUND, { chainId, }); + } else { + assertHardhatInvariant( + false, + "This should not happen, as we check in isDevelopmentNetwork that it's either hardhat or anvil", + ); } - - assertHardhatInvariant( - false, - "This should not happen, as we check in isDevelopmentNetwork that it's either hardhat or anvil", - ); } + chainCache.set(provider, chain); + return chain; } diff --git a/v-next/hardhat-viem/src/internal/clients.ts b/v-next/hardhat-viem/src/internal/clients.ts index 23dd7fb72c5..688b2c0f17a 100644 --- a/v-next/hardhat-viem/src/internal/clients.ts +++ b/v-next/hardhat-viem/src/internal/clients.ts @@ -1,5 +1,5 @@ import type { - GetPublicClientReturnType, + PublicClientType, GetWalletClientReturnType, TestClient, } from "../types.js"; @@ -28,7 +28,7 @@ export async function getPublicClient( provider: EthereumProvider, chainType: ChainTypeT, publicClientConfig?: Partial, -): Promise> { +): Promise> { const chain = publicClientConfig?.chain ?? (await getChain(provider, chainType)); const parameters = { @@ -48,7 +48,7 @@ export async function getPublicClient( /* eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- We need to assert the type because TS gets confused with the conditional type */ - return publicClient as GetPublicClientReturnType; + return publicClient as PublicClientType; } export async function getWalletClients( diff --git a/v-next/hardhat-viem/src/internal/contracts.ts b/v-next/hardhat-viem/src/internal/contracts.ts index d229d1dc608..261c293bee3 100644 --- a/v-next/hardhat-viem/src/internal/contracts.ts +++ b/v-next/hardhat-viem/src/internal/contracts.ts @@ -19,11 +19,12 @@ import { resolveLinkedBytecode } from "@ignored/hardhat-vnext-utils/bytecode"; import { ensureError } from "@ignored/hardhat-vnext-utils/error"; import { getContractAddress, getContract } from "viem"; -import { getDefaultWalletClient, getPublicClient } from "./clients.js"; +import { getDefaultWalletClient } from "./clients.js"; export async function deployContract( provider: EthereumProvider, artifactManager: ArtifactManager, + defaultPublicClient: PublicClient, contractName: ContractName, constructorArgs: unknown[] = [], deployContractConfig: DeployContractConfig = {}, @@ -47,8 +48,8 @@ export async function deployContract( }); } - const [publicClient, walletClient, { abi, bytecode }] = await Promise.all([ - client?.public ?? getPublicClient(provider, "l1"), + const publicClient = client?.public ?? defaultPublicClient; + const [walletClient, { abi, bytecode }] = await Promise.all([ client?.wallet ?? getDefaultWalletClient(provider, "l1"), getContractAbiAndBytecode(artifactManager, contractName, libraries), ]); @@ -104,6 +105,7 @@ export async function deployContract( export async function sendDeploymentTransaction( provider: EthereumProvider, artifactManager: ArtifactManager, + defaultPublicClient: PublicClient, contractName: ContractName, constructorArgs: unknown[] = [], sendDeploymentTransactionConfig: SendDeploymentTransactionConfig = {}, @@ -116,8 +118,8 @@ export async function sendDeploymentTransaction( libraries = {}, ...deployContractParameters } = sendDeploymentTransactionConfig; - const [publicClient, walletClient, { abi, bytecode }] = await Promise.all([ - client?.public ?? getPublicClient(provider, "l1"), + const publicClient = client?.public ?? defaultPublicClient; + const [walletClient, { abi, bytecode }] = await Promise.all([ client?.wallet ?? getDefaultWalletClient(provider, "l1"), getContractAbiAndBytecode(artifactManager, contractName, libraries), ]); @@ -167,12 +169,14 @@ export async function sendDeploymentTransaction( export async function getContractAt( provider: EthereumProvider, artifactManager: ArtifactManager, + defaultPublicClient: PublicClient, contractName: ContractName, address: ViemAddress, getContractAtConfig: GetContractAtConfig = {}, ): Promise> { - const [publicClient, walletClient, artifact] = await Promise.all([ - getContractAtConfig.client?.public ?? getPublicClient(provider, "l1"), + const publicClient = + getContractAtConfig.client?.public ?? defaultPublicClient; + const [walletClient, artifact] = await Promise.all([ getContractAtConfig.client?.wallet ?? getDefaultWalletClient(provider, "l1"), artifactManager.readArtifact(contractName), diff --git a/v-next/hardhat-viem/src/internal/hook-handlers/network.ts b/v-next/hardhat-viem/src/internal/hook-handlers/network.ts index 81a7132400e..99f94208524 100644 --- a/v-next/hardhat-viem/src/internal/hook-handlers/network.ts +++ b/v-next/hardhat-viem/src/internal/hook-handlers/network.ts @@ -19,7 +19,7 @@ export default async (): Promise> => { ) { const connection: NetworkConnection = await next(context); - connection.viem = initializeViem( + connection.viem = await initializeViem( connection.chainType, connection.provider, context.artifacts, diff --git a/v-next/hardhat-viem/src/internal/initialization.ts b/v-next/hardhat-viem/src/internal/initialization.ts index 562775d8dad..d1215e68845 100644 --- a/v-next/hardhat-viem/src/internal/initialization.ts +++ b/v-next/hardhat-viem/src/internal/initialization.ts @@ -15,12 +15,16 @@ import { sendDeploymentTransaction, } from "./contracts.js"; -export function initializeViem( +export async function initializeViem( chainType: ChainTypeT, provider: EthereumProvider, artifactManager: ArtifactManager, -): HardhatViemHelpers { +): Promise> { + const defaultPublicClient = await getPublicClient(provider, chainType); + return { + publicClient: defaultPublicClient, + getPublicClient: (publicClientConfig) => getPublicClient(provider, chainType, publicClientConfig), @@ -37,6 +41,7 @@ export function initializeViem( deployContract( provider, artifactManager, + defaultPublicClient, contractName, constructorArgs, deployContractConfig, @@ -50,6 +55,7 @@ export function initializeViem( sendDeploymentTransaction( provider, artifactManager, + defaultPublicClient, contractName, constructorArgs, sendDeploymentTransactionConfig, @@ -59,6 +65,7 @@ export function initializeViem( getContractAt( provider, artifactManager, + defaultPublicClient, contractName, address, getContractAtConfig, diff --git a/v-next/hardhat-viem/src/types.ts b/v-next/hardhat-viem/src/types.ts index d906cd97e9c..2070b45249a 100644 --- a/v-next/hardhat-viem/src/types.ts +++ b/v-next/hardhat-viem/src/types.ts @@ -32,6 +32,8 @@ import type { export interface HardhatViemHelpers< ChainTypeT extends ChainType | string = DefaultChainType, > { + publicClient: PublicClientType; + /** * Creates a public client configured with the provided settings. * @@ -42,7 +44,7 @@ export interface HardhatViemHelpers< */ getPublicClient: ( publicClientConfig?: Partial, - ) => Promise>; + ) => Promise>; /** * Creates a wallet client configured with the provided settings for each * account in the provider. @@ -135,7 +137,7 @@ export interface HardhatViemHelpers< ) => Promise>; } -export type GetPublicClientReturnType = +export type PublicClientType = ChainTypeT extends "optimism" ? OpPublicClient : PublicClient; export type GetWalletClientReturnType = @@ -146,7 +148,7 @@ export type PublicClient = ViemPublicClient; export type OpPublicClient = ViemClient< ViemTransport, ViemChain, - ViemAccount, + undefined, ViemRpcSchema, ViemPublicActions & ViemOpStackPublicActionsL2 diff --git a/v-next/hardhat-viem/test/fixture-projects/.gitignore b/v-next/hardhat-viem/test/fixture-projects/.gitignore new file mode 100644 index 00000000000..a848742089f --- /dev/null +++ b/v-next/hardhat-viem/test/fixture-projects/.gitignore @@ -0,0 +1 @@ +/*/cache diff --git a/v-next/hardhat-viem/test/hook-handlers/network.ts b/v-next/hardhat-viem/test/hook-handlers/network.ts index 3bf3fd9171e..29c71c4a1b6 100644 --- a/v-next/hardhat-viem/test/hook-handlers/network.ts +++ b/v-next/hardhat-viem/test/hook-handlers/network.ts @@ -24,7 +24,10 @@ describe("hook-handlers/network", () => { it("should be extended with viem", () => { assert.ok(isObject(connection.viem), "viem should be defined"); - + assert.ok( + isObject(connection.viem.publicClient), + "viem should have publicClient object", + ); assert.ok( typeof connection.viem.getPublicClient === "function", "viem should have a getPublicClient function",