Skip to content

Commit

Permalink
Merge pull request #5916 from NomicFoundation/fix-viem-problems
Browse files Browse the repository at this point in the history
Fix hardhat-viem so that it works with EDR simulating Op
  • Loading branch information
alcuadrado authored Oct 31, 2024
2 parents f5be161 + bedf22b commit 86aed02
Show file tree
Hide file tree
Showing 15 changed files with 202 additions and 68 deletions.
22 changes: 15 additions & 7 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 11 additions & 3 deletions v-next/example-project/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,19 @@ const pluginExample = {

const config: HardhatUserConfig = {
networks: {
opSepolia: {
op: {
type: "http",
chainType: "optimism",
url: "https://sepolia.optimism.io",
accounts: [configVariable("OP_SEPOLIA_SENDER")],
url: "https://mainnet.optimism.io/",
accounts: [configVariable("OP_SENDER")],
},
edrOp: {
type: "edr",
chainType: "optimism",
chainId: 10,
forkConfig: {
jsonRpcUrl: "https://mainnet.optimism.io",
},
},
},
tasks: [
Expand Down
2 changes: 1 addition & 1 deletion v-next/example-project/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"prettier": "3.2.5",
"rimraf": "^5.0.5",
"typescript": "~5.5.0",
"viem": "^2.21.17",
"viem": "^2.21.37",
"forge-std": "foundry-rs/forge-std#v1.9.4"
}
}
53 changes: 53 additions & 0 deletions v-next/example-project/scripts/send-op-tx-viem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { network } from "@ignored/hardhat-vnext";

async function sendL2Transaction(networkConfigName: string) {
console.log("Sending transaction using network", networkConfigName);

const { viem, networkConfig } = await network.connect(
networkConfigName,
"optimism",
);

if (networkConfig.type === "edr") {
console.log("Using an EDR network simulating Optimism, forking it");
} else {
console.log("Using an HTTP connection to Optimism");
}

const publicClient = await viem.getPublicClient();
const [senderClient] = await viem.getWalletClients();

console.log("Sender:", await senderClient.account.address);

console.log(
"Sender balance:",
await publicClient.getBalance(senderClient.account),
);

console.log("Sending 1 wei from", senderClient.account.address, "to itself");

console.log("Estimating L1 gas first");
const l1Gas = await publicClient.estimateL1Gas({
account: senderClient.account.address,
to: senderClient.account.address,
value: 1n,
});

console.log("Estimated L1 gas:", l1Gas);

console.log("Sending L2 transaction");
const tx = await senderClient.sendTransaction({
to: senderClient.account.address,
value: 1n,
});

const receipt = await publicClient.waitForTransactionReceipt({ hash: tx });

console.log("Transaction receipt:", receipt);
}

await sendL2Transaction("op");
console.log("");
console.log("");
console.log("");
await sendL2Transaction("edrOp");
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { network } from "@ignored/hardhat-vnext";

const { provider } = await network.connect("opSepolia", "optimism");
const { provider } = await network.connect("op", "optimism");

const accounts = (await provider.request({
method: "eth_accounts",
Expand Down
2 changes: 1 addition & 1 deletion v-next/hardhat-utils/src/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export abstract class CustomError extends Error {
public override stack!: string;

constructor(message: string, cause?: Error) {
super(message, { cause });
super(message, cause !== undefined ? { cause } : undefined);
this.name = this.constructor.name;
Error.captureStackTrace(this, this.constructor);
}
Expand Down
25 changes: 25 additions & 0 deletions v-next/hardhat-utils/test/error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import assert from "node:assert/strict";
import { describe, it } from "node:test";

import { CustomError } from "../src/error.js";

describe("CustomError", () => {
class MockCustomError extends CustomError {}

it("should not set the `cause` property to `undefined` if not provided", () => {
const error = new MockCustomError("test");

assert.ok(!("cause" in error), "The `cause` property shouldn't be present");
});

it("should set the `cause` property to if provided", () => {
const cause = new Error("cause");
const error = new MockCustomError("test", cause);

assert.equal(
error.cause,
error.cause,
"The `cause` property should be set to the provided error",
);
});
});
4 changes: 2 additions & 2 deletions v-next/hardhat-viem/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,14 @@
"tsx": "^4.11.0",
"typescript": "~5.5.0",
"typescript-eslint": "7.7.1",
"viem": "^2.21.17"
"viem": "^2.21.37"
},
"dependencies": {
"@ignored/hardhat-vnext-errors": "workspace:^3.0.0-next.9",
"@ignored/hardhat-vnext-utils": "workspace:^3.0.0-next.9"
},
"peerDependencies": {
"@ignored/hardhat-vnext": "workspace:^3.0.0-next.10",
"viem": "^2.21.17"
"viem": "^2.21.37"
}
}
41 changes: 31 additions & 10 deletions v-next/hardhat-viem/src/internal/chains.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import type { TestClientMode } from "../types.js";
import type { ChainType } from "@ignored/hardhat-vnext/types/network";
import type { EthereumProvider } from "@ignored/hardhat-vnext/types/providers";
import type { Chain as ViemChain } from "viem";

import { HardhatError } from "@ignored/hardhat-vnext-errors";
import {
assertHardhatInvariant,
HardhatError,
} from "@ignored/hardhat-vnext-errors";
import { extractChain } from "viem";
import * as chainsModule from "viem/chains";
import { hardhat, anvil } from "viem/chains";
import { hardhat, anvil, optimism } from "viem/chains";

/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions
-- TODO: this assertion should not be necessary */
Expand All @@ -18,16 +22,24 @@ const isAnvilNetworkCache = new WeakMap<EthereumProvider, boolean>();
const HARDHAT_METADATA_METHOD = "hardhat_metadata";
const ANVIL_NODE_INFO_METHOD = "anvil_nodeInfo";

export async function getChain(provider: EthereumProvider): Promise<ViemChain> {
export async function getChain(
provider: EthereumProvider,
chainType: ChainType | string,
): Promise<ViemChain> {
const chainId = await getChainId(provider);

const chain = extractChain({
chains,
id: chainId,
});

if (isDevelopmentNetwork(chainId) || chain === undefined) {
if ((await isDevelopmentNetwork(provider)) || chain === undefined) {
if (await isHardhatNetwork(provider)) {
if (chainType === "optimism") {
// TODO: We may need a better way to merge this info.
return { ...optimism, id: chainId };
}

return {
...hardhat,
id: chainId,
Expand All @@ -49,10 +61,9 @@ export async function getChain(provider: EthereumProvider): Promise<ViemChain> {
});
}

// If the chain is a development network but not one of our supported
// development networks (e.g. Hardhat, Anvil) then throw
throw new HardhatError(
HardhatError.ERRORS.VIEM.UNSUPPORTED_DEVELOPMENT_NETWORK,
assertHardhatInvariant(
false,
"This should not happen, as we check in isDevelopmentNetwork that it's either hardhat or anvil",
);
}

Expand All @@ -71,8 +82,18 @@ export async function getChainId(provider: EthereumProvider): Promise<number> {
return chainId;
}

export function isDevelopmentNetwork(chainId: number): boolean {
return chainId === 31337;
export async function isDevelopmentNetwork(
provider: EthereumProvider,
): Promise<boolean> {
if (await isHardhatNetwork(provider)) {
return true;
}

if (await isAnvilNetwork(provider)) {
return true;
}

return false;
}

export async function isHardhatNetwork(
Expand Down
30 changes: 19 additions & 11 deletions v-next/hardhat-viem/src/internal/clients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ export async function getPublicClient<ChainTypeT extends ChainType | string>(
chainType: ChainTypeT,
publicClientConfig?: Partial<ViemPublicClientConfig>,
): Promise<GetPublicClientReturnType<ChainTypeT>> {
const chain = publicClientConfig?.chain ?? (await getChain(provider));
const chain =
publicClientConfig?.chain ?? (await getChain(provider, chainType));
const parameters = {
...getDefaultClientParameters(chain.id),
...(await getDefaultClientParameters(provider)),
...publicClientConfig,
};

Expand All @@ -55,10 +56,11 @@ export async function getWalletClients<ChainTypeT extends ChainType | string>(
chainType: ChainTypeT,
walletClientConfig?: Partial<ViemWalletClientConfig>,
): Promise<Array<GetWalletClientReturnType<ChainTypeT>>> {
const chain = walletClientConfig?.chain ?? (await getChain(provider));
const chain =
walletClientConfig?.chain ?? (await getChain(provider, chainType));
const accounts = await getAccounts(provider);
const parameters = {
...getDefaultClientParameters(chain.id),
...(await getDefaultClientParameters(provider)),
...walletClientConfig,
};

Expand Down Expand Up @@ -88,9 +90,10 @@ export async function getWalletClient<ChainTypeT extends ChainType | string>(
address: ViemAddress,
walletClientConfig?: Partial<ViemWalletClientConfig>,
): Promise<GetWalletClientReturnType<ChainTypeT>> {
const chain = walletClientConfig?.chain ?? (await getChain(provider));
const chain =
walletClientConfig?.chain ?? (await getChain(provider, chainType));
const parameters = {
...getDefaultClientParameters(chain.id),
...(await getDefaultClientParameters(provider)),
...walletClientConfig,
};

Expand All @@ -117,7 +120,8 @@ export async function getDefaultWalletClient<
chainType: ChainTypeT,
walletClientConfig?: Partial<ViemWalletClientConfig>,
): Promise<GetWalletClientReturnType<ChainTypeT>> {
const chain = walletClientConfig?.chain ?? (await getChain(provider));
const chain =
walletClientConfig?.chain ?? (await getChain(provider, chainType));
const [defaultAccount] = await getAccounts(provider);

if (defaultAccount === undefined) {
Expand All @@ -137,11 +141,13 @@ export async function getDefaultWalletClient<
);
}

export async function getTestClient(
export async function getTestClient<ChainTypeT extends ChainType | string>(
provider: EthereumProvider,
chainType: ChainTypeT,
testClientConfig?: Partial<ViemTestClientConfig>,
): Promise<TestClient> {
const chain = testClientConfig?.chain ?? (await getChain(provider));
const chain =
testClientConfig?.chain ?? (await getChain(provider, chainType));
const mode = await getMode(provider);
const parameters = { ...DEFAULT_CLIENT_PARAMETERS, ...testClientConfig };

Expand All @@ -157,6 +163,8 @@ export async function getTestClient(

const DEFAULT_CLIENT_PARAMETERS = { pollingInterval: 50, cacheTime: 0 };

function getDefaultClientParameters(chainId: number) {
return isDevelopmentNetwork(chainId) ? DEFAULT_CLIENT_PARAMETERS : {};
async function getDefaultClientParameters(provider: EthereumProvider) {
return (await isDevelopmentNetwork(provider))
? DEFAULT_CLIENT_PARAMETERS
: {};
}
2 changes: 1 addition & 1 deletion v-next/hardhat-viem/src/internal/initialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export function initializeViem<ChainTypeT extends ChainType | string>(
getWalletClient(provider, chainType, address, walletClientConfig),

getTestClient: (testClientConfig) =>
getTestClient(provider, testClientConfig),
getTestClient(provider, chainType, testClientConfig),

deployContract: (contractName, constructorArgs, deployContractConfig) =>
deployContract(
Expand Down
6 changes: 4 additions & 2 deletions v-next/hardhat-viem/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,8 @@ export type OpPublicClient = ViemClient<
ViemChain,
ViemAccount,
ViemRpcSchema,
ViemPublicActions & ViemOpStackPublicActionsL2
ViemPublicActions<ViemTransport, ViemChain, ViemAccount> &
ViemOpStackPublicActionsL2<ViemChain, ViemAccount>
>;

export type WalletClient = ViemWalletClient<
Expand All @@ -162,7 +163,8 @@ export type OpWalletClient = ViemClient<
ViemChain,
ViemAccount,
ViemRpcSchema,
ViemWalletActions & ViemOpStackWalletActionsL2
ViemWalletActions<ViemChain, ViemAccount> &
ViemOpStackWalletActionsL2<ViemChain, ViemAccount>
>;

export type TestClient = ViemTestClient<
Expand Down
Loading

0 comments on commit 86aed02

Please sign in to comment.