Skip to content

Commit

Permalink
Merge pull request #5680 from NomicFoundation/network-manager-m1
Browse files Browse the repository at this point in the history
Network manager implementation (M1)
  • Loading branch information
alcuadrado authored Sep 3, 2024
2 parents c447a7f + 7b8701d commit e1b0e74
Show file tree
Hide file tree
Showing 32 changed files with 972 additions and 85 deletions.
26 changes: 26 additions & 0 deletions pnpm-lock.yaml

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

17 changes: 17 additions & 0 deletions v-next/example-project/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
} from "@ignored/hardhat-vnext/config";
import HardhatNodeTestRunner from "@ignored/hardhat-vnext-node-test-runner";
import HardhatMochaTestRunner from "@ignored/hardhat-vnext-mocha-test-runner";
import { viemScketchPlugin } from "./viem-scketch-plugin.js";

const exampleEmptyTask = emptyTask("empty", "An example empty task").build();

Expand Down Expand Up @@ -66,6 +67,19 @@ const greeting = task("hello", "Prints a greeting")
})
.build();

const printConfig = task("config", "Prints the config")
.setAction(async ({}, hre) => {
console.log(hre.config);
})
.build();

const printAccounts = task("accounts", "Prints the accounts")
.setAction(async ({}, hre) => {
const { provider } = await hre.network.connect();
console.log(await provider.request({ method: "eth_accounts" }));
})
.build();

const pluginExample = {
id: "community-plugin",
tasks: [
Expand Down Expand Up @@ -103,12 +117,15 @@ const config: HardhatUserConfig = {
exampleEmptyTask,
exampleEmptySubtask,
greeting,
printConfig,
printAccounts,
],
plugins: [
pluginExample,
HardhatMochaTestRunner,
// if testing node plugin, use the following line instead
// HardhatNodeTestRunner,
viemScketchPlugin,
],
paths: {
tests: "test/mocha",
Expand Down
3 changes: 2 additions & 1 deletion v-next/example-project/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@types/node": "^20.14.9",
"mocha": "^10.0.0",
"prettier": "3.2.5",
"typescript": "~5.5.0"
"typescript": "~5.5.0",
"viem": "^2.7.6"
}
}
10 changes: 10 additions & 0 deletions v-next/example-project/scripts/viem-plugin-example.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { network } from "@ignored/hardhat-vnext";

// This network connection has access to an optimism-specific viem api
const optimism = await network.connect("localhost", "optimism");
optimism.viem.client.getL1BaseFee({ chain: null });

// This one doesn't
const mainnet = await network.connect("localhost", "l1");
// @ts-expect-error
mainnet.viem.client.getL1BaseFee({ chain: null });
69 changes: 69 additions & 0 deletions v-next/example-project/viem-scketch-plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { HardhatPlugin } from "@ignored/hardhat-vnext/types/plugins";
import { ChainType } from "@ignored/hardhat-vnext/types/config";
import { HookContext } from "@ignored/hardhat-vnext/types/hooks";

import { NetworkConnection } from "@ignored/hardhat-vnext/types/network";

import "@ignored/hardhat-vnext/types/network";

import {
Client,
createPublicClient,
custom,
CustomTransport,
PublicActions,
PublicClient,
PublicRpcSchema,
} from "viem";
import { PublicActionsL2, publicActionsL2 } from "viem/op-stack";

export type ViemPublicClient<ChainTypeT extends ChainType | string> =
ChainTypeT extends "optimism"
? Client<
CustomTransport,
undefined,
undefined,
PublicRpcSchema,
PublicActions<CustomTransport, undefined> &
PublicActionsL2<undefined, undefined>
>
: PublicClient;

declare module "@ignored/hardhat-vnext/types/network" {
export interface NetworkConnection<ChainTypeT extends ChainType | string> {
viem: {
client: ViemPublicClient<ChainTypeT>;
};
}
}

export const viemScketchPlugin: HardhatPlugin = {
id: "viem-scketch",
hookHandlers: {
network: async () => ({
async newConnection<ChainTypeT extends ChainType | string>(
context: HookContext,
next: (
nextContext: HookContext,
) => Promise<NetworkConnection<ChainTypeT>>,
) {
const connection: NetworkConnection<ChainTypeT> = await next(context);

const transport = custom(connection.provider);

const client =
connection.chainType === "optimism"
? createPublicClient({
transport: custom(connection.provider),
}).extend(publicActionsL2())
: createPublicClient({
transport,
});

connection.viem = { client: client as ViemPublicClient<ChainTypeT> };

return connection;
},
}),
},
};
25 changes: 25 additions & 0 deletions v-next/hardhat-errors/src/descriptors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -524,5 +524,30 @@ Please check your internet connection and networks config`,
Please make sure your node is running, and check your internet connection and networks config.`,
},
NETWORK_NOT_FOUND: {
number: 705,
messageTemplate: `The network {networkName} is not defined in your networks config.`,
websiteTitle: "Network not found",
websiteDescription: `The network you are trying to connect to is not found.
Please double check that the network is correctly defined in your networks config.`,
},
INVALID_CHAIN_TYPE: {
number: 706,
messageTemplate:
"The provided chain type {chainType} does not match the network's chain type {networkChainType} for network {networkName}.",
websiteTitle: "Invalid chain type",
websiteDescription: `The chain type does not match the network's chain type.
If you want to use a different chain type, please update your networks config.`,
},

INVALID_CONFIG_OVERRIDE: {
number: 707,
messageTemplate: `Invalid config override:
{errors}`,
websiteTitle: "Invalid config override",
websiteDescription: `The configuration override you provided is invalid.`,
},
},
} as const;
2 changes: 2 additions & 0 deletions v-next/hardhat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
"./types/global-options": "./dist/src/types/global-options.js",
"./types/hooks": "./dist/src/types/hooks.js",
"./types/hre": "./dist/src/types/hre.js",
"./types/network": "./dist/src/types/network.js",
"./types/plugins": "./dist/src/types/plugins.js",
"./types/providers": "./dist/src/types/providers.js",
"./types/tasks": "./dist/src/types/tasks.js",
"./types/user-interruptions": "./dist/src/types/user-interruptions.js",
"./types/utils": "./dist/src/types/utils.js"
Expand Down
5 changes: 5 additions & 0 deletions v-next/hardhat/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { HardhatConfig } from "./types/config.js";
import type { GlobalOptions } from "./types/global-options.js";
import type { HookManager } from "./types/hooks.js";
import type { HardhatRuntimeEnvironment } from "./types/hre.js";
import type { NetworkManager } from "./types/network.js";
import type { TaskManager } from "./types/tasks.js";
import type { UserInterruptionManager } from "./types/user-interruptions.js";

Expand All @@ -21,4 +22,8 @@ export const globalOptions: GlobalOptions = hre.globalOptions;
export const hooks: HookManager = hre.hooks;
export const interruptions: UserInterruptionManager = hre.interruptions;

// NOTE: This is a small architectural violation, as the network manager comes
// from a builtin plugin, and plugins can't add their own exports here.
export const network: NetworkManager = hre.network;

export default hre;
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ const consoleAction: NewTaskActionFunction<ConsoleActionArguments> = async (
replServer.context.hooks = hre.hooks;
replServer.context.interruptions = hre.interruptions;

// NOTE: This is a small architectural violation, as the network manager
// comes from a builtin plugin, and plugins can't add their own exports
// here. We may consider adding a hook for this in the future.
replServer.context.network = hre.network;

// Set up the REPL history file if the historyPath has been set
if (historyPath !== undefined) {
await new Promise<void>((resolveSetupHistory) => {
Expand Down
10 changes: 9 additions & 1 deletion v-next/hardhat/src/internal/builtin-plugins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,20 @@ import type { HardhatPlugin } from "../../types/plugins.js";

import clean from "./clean/index.js";
import console from "./console/index.js";
import networkManager from "./network-manager/index.js";
import run from "./run/index.js";

// Note: When importing a plugin, you have to export its types, so that its
// type extensions, if any, also get loaded.
export type * from "./network-manager/index.js";
export type * from "./clean/index.js";
export type * from "./console/index.js";
export type * from "./network-manager/index.js";
export type * from "./run/index.js";

export const builtinPlugins: HardhatPlugin[] = [clean, console, run];
export const builtinPlugins: HardhatPlugin[] = [
networkManager,
clean,
console,
run,
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import type {
HttpNetworkConfig,
NetworkConfig,
NetworkUserConfig,
} from "../../../../types/config.js";
import type { ConfigHooks } from "../../../../types/hooks.js";

import { validateUserConfig } from "../type-validation.js";

function resolveBigIntOrAuto(
value: number | bigint | "auto" | undefined,
): bigint | "auto" {
if (value === undefined || value === "auto") {
return "auto";
}

// TODO: Validate that it's a valid BigInt
return BigInt(value);
}

export default async (): Promise<Partial<ConfigHooks>> => ({
extendUserConfig: async (config, next) => {
const extendedConfig = await next(config);

const networks: Record<string, NetworkUserConfig> =
extendedConfig.networks ?? {};

return {
...extendedConfig,
networks: {
...networks,
localhost: {
url: "http://localhost:8545",
...networks.localhost,
type: "http",
},
},
};
},
validateUserConfig,
resolveUserConfig: async (userConfig, resolveConfigurationVariable, next) => {
const resolvedConfig = await next(userConfig, resolveConfigurationVariable);

const networks: Record<string, NetworkUserConfig> =
userConfig.networks ?? {};

const resolvedNetworks: Record<string, NetworkConfig> = {};

for (const [networkName, networkConfig] of Object.entries(networks)) {
if (networkConfig.type !== "http") {
// eslint-disable-next-line no-restricted-syntax -- TODO
throw new Error("Only HTTP network is supported for now");
}

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

resolvedNetworks[networkName] = resolvedNetworkConfig;
}

return {
...resolvedConfig,
defaultNetwork: resolvedConfig.defaultNetwork ?? "localhost",
defaultChainType: resolvedConfig.defaultChainType ?? "unknown",
networks: resolvedNetworks,
};
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { HardhatRuntimeEnvironmentHooks } from "../../../../types/hooks.js";

import { NetworkManagerImplementation } from "../network-manager.js";

export default async (): Promise<Partial<HardhatRuntimeEnvironmentHooks>> => ({
created: async (context, hre) => {
hre.network = new NetworkManagerImplementation(
hre.globalOptions.network !== ""
? hre.globalOptions.network
: hre.config.defaultNetwork,
hre.config.defaultChainType,
hre.config.networks,
context.hooks,
);
},
});
Loading

0 comments on commit e1b0e74

Please sign in to comment.