From 907e36846e15053c6df6925bd983fc32706bb872 Mon Sep 17 00:00:00 2001 From: Patricio Palladino Date: Thu, 31 Oct 2024 23:01:16 +0000 Subject: [PATCH] WIP --- pnpm-lock.yaml | 22 ++++--- v-next/example-project/hardhat.config.ts | 16 ++++- v-next/example-project/package.json | 2 +- .../scripts/send-op-mainnet-tx-viem.ts | 61 +++++++++++++++++++ .../{send-op-sepolia-tx.ts => send-op-tx.ts} | 2 +- v-next/hardhat-viem/package.json | 4 +- v-next/hardhat-viem/src/internal/chains.ts | 41 ++++++++++--- v-next/hardhat-viem/src/internal/clients.ts | 32 ++++++---- .../src/internal/initialization.ts | 2 +- v-next/hardhat-viem/src/types.ts | 6 +- v-next/hardhat-viem/test/chains.ts | 59 ++++++++++-------- v-next/hardhat-viem/test/clients.ts | 8 +-- .../node-test-runner-viem/package.json | 2 +- 13 files changed, 191 insertions(+), 66 deletions(-) create mode 100644 v-next/example-project/scripts/send-op-mainnet-tx-viem.ts rename v-next/example-project/scripts/{send-op-sepolia-tx.ts => send-op-tx.ts} (93%) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ab037b1785..41d32f4733 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1471,8 +1471,8 @@ importers: specifier: ~5.5.0 version: 5.5.4 viem: - specifier: ^2.21.17 - version: 2.21.35(typescript@5.5.4)(zod@3.23.8) + specifier: ^2.21.37 + version: 2.21.37(typescript@5.5.4)(zod@3.23.8) v-next/hardhat: dependencies: @@ -2271,8 +2271,8 @@ importers: specifier: 7.7.1 version: 7.7.1(eslint@8.57.0)(typescript@5.5.4) viem: - specifier: ^2.21.17 - version: 2.21.35(typescript@5.5.4)(zod@3.23.8) + specifier: ^2.21.37 + version: 2.21.37(typescript@5.5.4)(zod@3.23.8) v-next/hardhat-zod-utils: dependencies: @@ -2395,8 +2395,8 @@ importers: specifier: ~5.5.0 version: 5.5.4 viem: - specifier: ^2.21.17 - version: 2.21.35(typescript@5.5.4)(zod@3.23.8) + specifier: ^2.21.37 + version: 2.21.37(typescript@5.5.4)(zod@3.23.8) v-next/template-package: devDependencies: @@ -6627,6 +6627,14 @@ packages: typescript: optional: true + viem@2.21.37: + resolution: {integrity: sha512-JupwyttT4aJNnP9+kD7E8jorMS5VmgpC3hm3rl5zXsO8WNBTsP3JJqZUSg4AG6s2lTrmmpzS/qpmXMZu5gJw5Q==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} @@ -11865,7 +11873,7 @@ snapshots: - utf-8-validate - zod - viem@2.21.35(typescript@5.5.4)(zod@3.23.8): + viem@2.21.37(typescript@5.5.4)(zod@3.23.8): dependencies: '@adraffy/ens-normalize': 1.11.0 '@noble/curves': 1.6.0 diff --git a/v-next/example-project/hardhat.config.ts b/v-next/example-project/hardhat.config.ts index 359006dc42..2cd08d025f 100644 --- a/v-next/example-project/hardhat.config.ts +++ b/v-next/example-project/hardhat.config.ts @@ -115,9 +115,23 @@ const config: HardhatUserConfig = { opSepolia: { type: "http", chainType: "optimism", - url: "https://sepolia.optimism.io", + url: "https://sepolia.optimism.io/", accounts: [configVariable("OP_SEPOLIA_SENDER")], }, + op: { + type: "http", + chainType: "optimism", + url: "https://mainnet.optimism.io/", + accounts: [configVariable("OP_SENDER")], + }, + edrOp: { + type: "edr", + chainType: "optimism", + chainId: 10, + forkConfig: { + jsonRpcUrl: "https://mainnet.optimism.io", + }, + }, }, tasks: [ exampleTaskOverride, diff --git a/v-next/example-project/package.json b/v-next/example-project/package.json index 729a92536f..c6add2892e 100644 --- a/v-next/example-project/package.json +++ b/v-next/example-project/package.json @@ -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" } } diff --git a/v-next/example-project/scripts/send-op-mainnet-tx-viem.ts b/v-next/example-project/scripts/send-op-mainnet-tx-viem.ts new file mode 100644 index 0000000000..120692345d --- /dev/null +++ b/v-next/example-project/scripts/send-op-mainnet-tx-viem.ts @@ -0,0 +1,61 @@ +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(""); +console.log(""); +console.log(""); +console.log("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); +console.log(""); +console.log(""); +console.log(""); +console.log(""); +console.log(""); +await sendL2Transaction("edrOp"); diff --git a/v-next/example-project/scripts/send-op-sepolia-tx.ts b/v-next/example-project/scripts/send-op-tx.ts similarity index 93% rename from v-next/example-project/scripts/send-op-sepolia-tx.ts rename to v-next/example-project/scripts/send-op-tx.ts index 4bd7a24d82..503c584fd7 100644 --- a/v-next/example-project/scripts/send-op-sepolia-tx.ts +++ b/v-next/example-project/scripts/send-op-tx.ts @@ -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", diff --git a/v-next/hardhat-viem/package.json b/v-next/hardhat-viem/package.json index 61fbd983e5..cacb9bc17a 100644 --- a/v-next/hardhat-viem/package.json +++ b/v-next/hardhat-viem/package.json @@ -60,7 +60,7 @@ "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", @@ -68,6 +68,6 @@ }, "peerDependencies": { "@ignored/hardhat-vnext": "workspace:^3.0.0-next.10", - "viem": "^2.21.17" + "viem": "^2.21.37" } } diff --git a/v-next/hardhat-viem/src/internal/chains.ts b/v-next/hardhat-viem/src/internal/chains.ts index 9ef4eb6177..753e3fc6db 100644 --- a/v-next/hardhat-viem/src/internal/chains.ts +++ b/v-next/hardhat-viem/src/internal/chains.ts @@ -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 */ @@ -18,7 +22,10 @@ const isAnvilNetworkCache = new WeakMap(); const HARDHAT_METADATA_METHOD = "hardhat_metadata"; const ANVIL_NODE_INFO_METHOD = "anvil_nodeInfo"; -export async function getChain(provider: EthereumProvider): Promise { +export async function getChain( + provider: EthereumProvider, + chainType: ChainType | string, +): Promise { const chainId = await getChainId(provider); const chain = extractChain({ @@ -26,8 +33,13 @@ export async function getChain(provider: EthereumProvider): Promise { 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 { ...hardhat, ...optimism, id: chainId }; + } + return { ...hardhat, id: chainId, @@ -49,10 +61,9 @@ export async function getChain(provider: EthereumProvider): Promise { }); } - // 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", ); } @@ -71,8 +82,18 @@ export async function getChainId(provider: EthereumProvider): Promise { return chainId; } -export function isDevelopmentNetwork(chainId: number): boolean { - return chainId === 31337; +export async function isDevelopmentNetwork( + provider: EthereumProvider, +): Promise { + if (await isHardhatNetwork(provider)) { + return true; + } + + if (await isAnvilNetwork(provider)) { + return true; + } + + return false; } export async function isHardhatNetwork( diff --git a/v-next/hardhat-viem/src/internal/clients.ts b/v-next/hardhat-viem/src/internal/clients.ts index 60ef67a7b4..59cc297216 100644 --- a/v-next/hardhat-viem/src/internal/clients.ts +++ b/v-next/hardhat-viem/src/internal/clients.ts @@ -29,9 +29,10 @@ export async function getPublicClient( chainType: ChainTypeT, publicClientConfig?: Partial, ): Promise> { - const chain = publicClientConfig?.chain ?? (await getChain(provider)); + const chain = + publicClientConfig?.chain ?? (await getChain(provider, chainType)); const parameters = { - ...getDefaultClientParameters(chain.id), + ...getDefaultClientParameters(provider), ...publicClientConfig, }; @@ -55,10 +56,11 @@ export async function getWalletClients( chainType: ChainTypeT, walletClientConfig?: Partial, ): Promise>> { - 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), + ...getDefaultClientParameters(provider), ...walletClientConfig, }; @@ -88,12 +90,15 @@ export async function getWalletClient( address: ViemAddress, walletClientConfig?: Partial, ): Promise> { - const chain = walletClientConfig?.chain ?? (await getChain(provider)); + const chain = + walletClientConfig?.chain ?? (await getChain(provider, chainType)); const parameters = { - ...getDefaultClientParameters(chain.id), + ...getDefaultClientParameters(provider), ...walletClientConfig, }; + console.log({ parameters, chain }); + let walletClient = createWalletClient({ chain, account: address, @@ -117,7 +122,8 @@ export async function getDefaultWalletClient< chainType: ChainTypeT, walletClientConfig?: Partial, ): Promise> { - const chain = walletClientConfig?.chain ?? (await getChain(provider)); + const chain = + walletClientConfig?.chain ?? (await getChain(provider, chainType)); const [defaultAccount] = await getAccounts(provider); if (defaultAccount === undefined) { @@ -137,11 +143,13 @@ export async function getDefaultWalletClient< ); } -export async function getTestClient( +export async function getTestClient( provider: EthereumProvider, + chainType: ChainTypeT, testClientConfig?: Partial, ): Promise { - 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 }; @@ -157,6 +165,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 + : {}; } diff --git a/v-next/hardhat-viem/src/internal/initialization.ts b/v-next/hardhat-viem/src/internal/initialization.ts index 02a2c1b167..c5086b51f9 100644 --- a/v-next/hardhat-viem/src/internal/initialization.ts +++ b/v-next/hardhat-viem/src/internal/initialization.ts @@ -31,7 +31,7 @@ export function initializeViem( getWalletClient(provider, chainType, address, walletClientConfig), getTestClient: (testClientConfig) => - getTestClient(provider, testClientConfig), + getTestClient(provider, chainType, testClientConfig), deployContract: (contractName, constructorArgs, deployContractConfig) => deployContract( diff --git a/v-next/hardhat-viem/src/types.ts b/v-next/hardhat-viem/src/types.ts index 7ca21f8d9c..d906cd97e9 100644 --- a/v-next/hardhat-viem/src/types.ts +++ b/v-next/hardhat-viem/src/types.ts @@ -148,7 +148,8 @@ export type OpPublicClient = ViemClient< ViemChain, ViemAccount, ViemRpcSchema, - ViemPublicActions & ViemOpStackPublicActionsL2 + ViemPublicActions & + ViemOpStackPublicActionsL2 >; export type WalletClient = ViemWalletClient< @@ -162,7 +163,8 @@ export type OpWalletClient = ViemClient< ViemChain, ViemAccount, ViemRpcSchema, - ViemWalletActions & ViemOpStackWalletActionsL2 + ViemWalletActions & + ViemOpStackWalletActionsL2 >; export type TestClient = ViemTestClient< diff --git a/v-next/hardhat-viem/test/chains.ts b/v-next/hardhat-viem/test/chains.ts index c9d8897b11..994d0904e1 100644 --- a/v-next/hardhat-viem/test/chains.ts +++ b/v-next/hardhat-viem/test/chains.ts @@ -21,53 +21,53 @@ describe("chains", () => { it("should return the chain corresponding to the chain id", async () => { const provider = new MockEthereumProvider({ eth_chainId: "0x1" }); // mainnet chain id - const chain = await getChain(provider); + const chain = await getChain(provider, "generic"); assert.deepEqual(chain, chains.mainnet); assert.equal(provider.callCount, 1); }); - it("should return the hardhat chain if the chain id is 31337 and the network is hardhat", async () => { + it("should return the hardhat chain if the network is hardhat and chaintype is optimisim", async () => { const provider = new MockEthereumProvider({ - eth_chainId: "0x7a69", // 31337 in hex + eth_chainId: "0xa", // Op mainnet: 10 hardhat_metadata: {}, }); - const chain = await getChain(provider); + const chain = await getChain(provider, "optimism"); - assert.deepEqual(chain, chains.hardhat); + assert.deepEqual(chain, chains.optimism); assert.equal(provider.callCount, 2); }); - it("should return the foundry chain if the chain id is 31337 and the network is anvil", async () => { + it("should return the hardhat chain if the network is hardhat and chaintype is generic", async () => { const provider = new MockEthereumProvider({ eth_chainId: "0x7a69", // 31337 in hex - anvil_nodeInfo: {}, + hardhat_metadata: {}, }); - const chain = await getChain(provider); + const chain = await getChain(provider, "generic"); - assert.deepEqual(chain, chains.anvil); + assert.deepEqual(chain, chains.hardhat); assert.equal(provider.callCount, 2); }); - it("should throw if the chain id is 31337 and the network is neither hardhat nor anvil", async () => { + it("should return the foundry chain if the network is anvil", async () => { const provider = new MockEthereumProvider({ eth_chainId: "0x7a69", // 31337 in hex + anvil_nodeInfo: {}, }); - await assertRejectsWithHardhatError( - getChain(provider), - HardhatError.ERRORS.VIEM.UNSUPPORTED_DEVELOPMENT_NETWORK, - {}, - ); + const chain = await getChain(provider, "generic"); + + assert.deepEqual(chain, chains.anvil); + assert.equal(provider.callCount, 2); }); - it("should throw if the chain id is not 31337 and there is no chain with that id", async () => { + it("should throw if it's not a dev network and there is no chain with that id", async () => { const provider = new MockEthereumProvider({ eth_chainId: "0x0" }); // fake chain id 0 await assertRejectsWithHardhatError( - getChain(provider), + getChain(provider, "generic"), HardhatError.ERRORS.VIEM.NETWORK_NOT_FOUND, { chainId: 0 }, ); @@ -77,7 +77,7 @@ describe("chains", () => { // chain id 999 corresponds to wanchainTestnet but also zoraTestnet const provider = new MockEthereumProvider({ eth_chainId: "0x3e7" }); // 999 in hex - const chainId = await getChain(provider); + const chainId = await getChain(provider, "generic"); assert.equal(chainId, chains.wanchainTestnet); }); @@ -87,7 +87,7 @@ describe("chains", () => { hardhat_metadata: {}, }); - const chain = await getChain(provider); + const chain = await getChain(provider, "generic"); assert.deepEqual(chain, { ...chains.hardhat, @@ -101,7 +101,7 @@ describe("chains", () => { anvil_nodeInfo: {}, }); - const chain = await getChain(provider); + const chain = await getChain(provider, "generic"); assert.deepEqual(chain, { ...chains.anvil, @@ -169,16 +169,25 @@ describe("chains", () => { }); describe("isDevelopmentNetwork", () => { - it("should return true if the chain id is 31337", () => { + it("should return true for Hardhat and Anvil nodes", async () => { + assert.ok( + await isDevelopmentNetwork( + new MockEthereumProvider({ hardhat_metadata: {} }), + ), + "Hardhat nodes should be considered development networks", + ); + assert.ok( - isDevelopmentNetwork(31337), - "chain id 31337 should be a development network", + await isDevelopmentNetwork( + new MockEthereumProvider({ anvil_nodeInfo: {} }), + ), + "Anvil nodes should be considered development networks", ); }); - it("should return false if the chain id is not 31337", () => { + it("should return false for other nodes", async () => { assert.ok( - !isDevelopmentNetwork(1), + !(await isDevelopmentNetwork(new MockEthereumProvider({}))), "chain id 1 should not be a development network", ); }); diff --git a/v-next/hardhat-viem/test/clients.ts b/v-next/hardhat-viem/test/clients.ts index 365a25f73c..dcc0ee3ed1 100644 --- a/v-next/hardhat-viem/test/clients.ts +++ b/v-next/hardhat-viem/test/clients.ts @@ -338,7 +338,7 @@ describe("clients", () => { assert.equal(client.cacheTime, 2000); }); - it("should return the default wallet client with default parameters for development networks", async () => { + it.only("should return the default wallet client with default parameters for development networks", async () => { const provider = new MockEthereumProvider({ eth_chainId: "0x7a69", // 31337 eth_accounts: ["0x123", "0x456"], @@ -367,7 +367,7 @@ describe("clients", () => { eth_chainId: "0x7a69", // 31337 in hex hardhat_metadata: {}, // hardhat network }); - const client = await getTestClient(provider); + const client = await getTestClient(provider, "generic"); assert.equal(client.type, "testClient"); assert.equal(client.chain.id, 31337); @@ -381,7 +381,7 @@ describe("clients", () => { eth_chainId: "0x7a69", // 31337 in hex anvil_nodeInfo: {}, // anvil network }); - const client = await getTestClient(provider); + const client = await getTestClient(provider, "generic"); assert.equal(client.type, "testClient"); assert.equal(client.chain.id, 31337); @@ -396,7 +396,7 @@ describe("clients", () => { hardhat_metadata: {}, // hardhat network }); - const client = await getTestClient(provider, { + const client = await getTestClient(provider, "generic", { pollingInterval: 1000, cacheTime: 2000, }); diff --git a/v-next/hardhat/templates/node-test-runner-viem/package.json b/v-next/hardhat/templates/node-test-runner-viem/package.json index 5e09632dac..537f5d58e5 100644 --- a/v-next/hardhat/templates/node-test-runner-viem/package.json +++ b/v-next/hardhat/templates/node-test-runner-viem/package.json @@ -11,6 +11,6 @@ "@ignored/hardhat-vnext-network-helpers": "workspace:^3.0.0-next.6", "forge-std": "foundry-rs/forge-std#v1.9.4", "typescript": "~5.5.0", - "viem": "^2.21.17" + "viem": "^2.21.37" } }