diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 90927f344a..521e1c18d8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1513,8 +1513,8 @@ importers: specifier: ^2.2.1 version: 2.2.1 micro-eth-signer: - specifier: ^0.12.0 - version: 0.12.0 + specifier: ^0.13.0 + version: 0.13.0 p-map: specifier: ^7.0.2 version: 7.0.2 @@ -3070,6 +3070,10 @@ packages: resolution: {integrity: sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ==} engines: {node: ^14.21.3 || >=16} + '@noble/curves@1.7.0': + resolution: {integrity: sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw==} + engines: {node: ^14.21.3 || >=16} + '@noble/hashes@1.2.0': resolution: {integrity: sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==} @@ -3085,6 +3089,14 @@ packages: resolution: {integrity: sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==} engines: {node: ^14.21.3 || >=16} + '@noble/hashes@1.6.0': + resolution: {integrity: sha512-YUULf0Uk4/mAA89w+k3+yUYh6NrEvxZa5T6SY3wlMvE2chHkxFUUIDI8/XW1QSC357iA5pSnqt7XEhvFOqmDyQ==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.6.1': + resolution: {integrity: sha512-pq5D8h10hHBjyqX+cfBm0i8JUXJ0UhczFc4r74zbuT9XgewFo2E3J1cOaGtdZynILNmQ685YWGzGE1Zv6io50w==} + engines: {node: ^14.21.3 || >=16} + '@noble/secp256k1@1.7.1': resolution: {integrity: sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==} @@ -3242,6 +3254,9 @@ packages: '@scure/base@1.1.9': resolution: {integrity: sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==} + '@scure/base@1.2.1': + resolution: {integrity: sha512-DGmGtC8Tt63J5GfHgfl5CuAXh96VF/LD8K9Hr/Gv0J2lAoRGlPOMpqMpMbCTOoOJMZCk2Xt+DskdDyn6dEFdzQ==} + '@scure/bip32@1.1.5': resolution: {integrity: sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==} @@ -5488,14 +5503,14 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - micro-eth-signer@0.12.0: - resolution: {integrity: sha512-zHKuqXthGRvPjs0lQRSBnDN8E1Ndi09vqAdr7pJctE6wFkkkoHwjqp8RpD/dED2CDN8MoL3g5L/UopXZBNEbBw==} + micro-eth-signer@0.13.0: + resolution: {integrity: sha512-cPLq4nx9bd8Je1uFX97DvGEGMWYVymdB6rfvoi85q81FCMIEdJfC8OcGdMj6m0f/oktmti6FNGl8G1ANX+HnCw==} micro-ftch@0.3.1: resolution: {integrity: sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==} - micro-packed@0.6.3: - resolution: {integrity: sha512-VmVkyc7lIzAq/XCPFuLc/CwQ7Ehs5XDK3IwqsZHiBIDttAI9Gs7go6Lv4lNRuAIKrGKcRTtthFKUNyHS0S4wJQ==} + micro-packed@0.7.0: + resolution: {integrity: sha512-GJB99Ox8AI3C2Xr9DFalmHjYaJTUwh6w18jqnOKJEU9fpR9uZaRGCE8xjSS4b9yH2KDmW2BdPR8BylzfYVBIdg==} micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} @@ -7788,6 +7803,10 @@ snapshots: dependencies: '@noble/hashes': 1.5.0 + '@noble/curves@1.7.0': + dependencies: + '@noble/hashes': 1.6.0 + '@noble/hashes@1.2.0': {} '@noble/hashes@1.3.2': {} @@ -7796,6 +7815,10 @@ snapshots: '@noble/hashes@1.5.0': {} + '@noble/hashes@1.6.0': {} + + '@noble/hashes@1.6.1': {} + '@noble/secp256k1@1.7.1': {} '@nodelib/fs.scandir@2.1.5': @@ -7966,6 +7989,8 @@ snapshots: '@scure/base@1.1.9': {} + '@scure/base@1.2.1': {} + '@scure/bip32@1.1.5': dependencies: '@noble/hashes': 1.2.0 @@ -10609,17 +10634,17 @@ snapshots: merge2@1.4.1: {} - micro-eth-signer@0.12.0: + micro-eth-signer@0.13.0: dependencies: - '@noble/curves': 1.6.0 - '@noble/hashes': 1.5.0 - micro-packed: 0.6.3 + '@noble/curves': 1.7.0 + '@noble/hashes': 1.6.1 + micro-packed: 0.7.0 micro-ftch@0.3.1: {} - micro-packed@0.6.3: + micro-packed@0.7.0: dependencies: - '@scure/base': 1.1.9 + '@scure/base': 1.2.1 micromatch@4.0.8: dependencies: diff --git a/v-next/hardhat-errors/src/descriptors.ts b/v-next/hardhat-errors/src/descriptors.ts index 8901fa0e1d..36b41a4ca2 100644 --- a/v-next/hardhat-errors/src/descriptors.ts +++ b/v-next/hardhat-errors/src/descriptors.ts @@ -135,7 +135,7 @@ Please double check whether you have multiple versions of the same plugin instal ENV_VAR_NOT_FOUND: { number: 7, messageTemplate: `Configuration Variable '{name}' not found. - + You can define it using a plugin like hardhat-keystore, or set it as an environment variable.`, websiteTitle: "Configuration variable not found", websiteDescription: `A configuration variable was expected to be set as an environment variable, but it wasn't.`, @@ -678,6 +678,13 @@ Try using another mnemonic or deriving less keys.`, websiteTitle: "Invalid network type", websiteDescription: `The network manager only supports the network types 'http' and 'edr'.`, }, + DATA_FIELD_CANNOT_BE_NULL_WITH_NULL_ADDRESS: { + number: 721, + messageTemplate: `The "to" field is undefined, and the "data" field is also undefined; however, a transaction to the null address cannot have an undefined "data" field.`, + websiteTitle: "Transaction to null address cannot have undefined data", + websiteDescription: + "The transaction to the null address cannot have undefined data", + }, }, KEYSTORE: { INVALID_KEYSTORE_FILE_FORMAT: { diff --git a/v-next/hardhat/package.json b/v-next/hardhat/package.json index aa7df3401b..a6c8d29923 100644 --- a/v-next/hardhat/package.json +++ b/v-next/hardhat/package.json @@ -94,7 +94,7 @@ "debug": "^4.1.1", "enquirer": "^2.3.0", "ethereum-cryptography": "^2.2.1", - "micro-eth-signer": "^0.12.0", + "micro-eth-signer": "^0.13.0", "p-map": "^7.0.2", "semver": "^7.6.3", "solc": "^0.8.27", diff --git a/v-next/hardhat/src/internal/builtin-plugins/network-manager/request-handlers/handlers/accounts/local-accounts.ts b/v-next/hardhat/src/internal/builtin-plugins/network-manager/request-handlers/handlers/accounts/local-accounts.ts index 3c210d708e..baeabdaa70 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/network-manager/request-handlers/handlers/accounts/local-accounts.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/network-manager/request-handlers/handlers/accounts/local-accounts.ts @@ -276,15 +276,25 @@ export class LocalAccountsHandler extends ChainId implements RequestHandler { }; }); - // TODO: Fix after the alpha release - assertHardhatInvariant( - txData.to !== undefined, - "The alpha version doesn't support deploying contracts with local accounts yet", - ); + if ( + (txData.to === undefined || txData.to === null) && + txData.data === undefined + ) { + throw new HardhatError( + HardhatError.ERRORS.NETWORK.DATA_FIELD_CANNOT_BE_NULL_WITH_NULL_ADDRESS, + ); + } - const checksummedAddress = addr.addChecksum( - bytesToHexString(txData.to).toLowerCase(), - ); + let checksummedAddress; + if (txData.to === undefined || txData.to === null) { + // This scenario arises during contract deployment. The npm package "micro-eth-signer" does not support + // null or undefined addresses. Therefore, these values must be converted to "0x", the expected format. + checksummedAddress = "0x"; + } else { + checksummedAddress = addr.addChecksum( + bytesToHexString(txData.to).toLowerCase(), + ); + } assertHardhatInvariant( txData.nonce !== undefined, diff --git a/v-next/hardhat/src/internal/builtin-plugins/network-manager/rpc/types/address.ts b/v-next/hardhat/src/internal/builtin-plugins/network-manager/rpc/types/address.ts index 19e04e592f..dcdd5bf600 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/network-manager/rpc/types/address.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/network-manager/rpc/types/address.ts @@ -17,3 +17,9 @@ export const rpcAddress: ZodType = conditionalUnionType( ], "Expected a Buffer with correct length or a valid RPC address string", ).transform((v) => (typeof v === "string" ? hexStringToBytes(v) : v)); + +export const nullableRpcAddress: ZodType = rpcAddress + .or(z.null()) + .describe( + "Expected a Buffer with correct length, a valid RPC address string, or the null value", + ); diff --git a/v-next/hardhat/src/internal/builtin-plugins/network-manager/rpc/types/tx-request.ts b/v-next/hardhat/src/internal/builtin-plugins/network-manager/rpc/types/tx-request.ts index b47607ef53..1eb4aba48a 100644 --- a/v-next/hardhat/src/internal/builtin-plugins/network-manager/rpc/types/tx-request.ts +++ b/v-next/hardhat/src/internal/builtin-plugins/network-manager/rpc/types/tx-request.ts @@ -3,7 +3,7 @@ import type { ZodType } from "zod"; import { z } from "zod"; import { rpcAccessList } from "./access-list.js"; -import { rpcAddress } from "./address.js"; +import { nullableRpcAddress, rpcAddress } from "./address.js"; import { rpcData } from "./data.js"; import { rpcHash } from "./hash.js"; import { rpcQuantity } from "./quantity.js"; @@ -12,7 +12,7 @@ const optional = >(schema: T) => schema.optional(); export interface RpcTransactionRequest { from: Uint8Array; - to?: Uint8Array; + to?: Uint8Array | null; gas?: bigint; gasPrice?: bigint; value?: bigint; @@ -28,7 +28,7 @@ export interface RpcTransactionRequest { export const rpcTransactionRequest: ZodType = z.object({ from: rpcAddress, - to: optional(rpcAddress), + to: optional(nullableRpcAddress), gas: optional(rpcQuantity), gasPrice: optional(rpcQuantity), value: optional(rpcQuantity), diff --git a/v-next/hardhat/test/internal/builtin-plugins/network-manager/request-handlers/handlers/accounts/local-accounts.ts b/v-next/hardhat/test/internal/builtin-plugins/network-manager/request-handlers/handlers/accounts/local-accounts.ts index 8d1b10fb0b..f35bb6edfd 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/network-manager/request-handlers/handlers/accounts/local-accounts.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/network-manager/request-handlers/handlers/accounts/local-accounts.ts @@ -451,6 +451,66 @@ describe("LocalAccountsHandler", () => { }); describe("modifyRequest", () => { + describe("sending transactions to the null address", () => { + const testCases = [null, undefined]; + + for (const testCase of testCases) { + it(`should succeed when the data field is not empty and the "to" field is ${testCase}`, async () => { + const tx = { + to: testCase, + from: "0xb5bc06d4548a3ac17d72b372ae1e416bf65b8ead", + gas: numberToHexString(30000), + nonce: numberToHexString(0), + value: numberToHexString(1), + chainId: numberToHexString(MOCK_PROVIDER_CHAIN_ID), + maxFeePerGas: numberToHexString(12), + maxPriorityFeePerGas: numberToHexString(2), + data: "0x01", + }; + + const jsonRpcRequest = getJsonRpcRequest(1, "eth_sendTransaction", [ + tx, + ]); + + await localAccountsHandler.handle(jsonRpcRequest); + + assert.ok( + Array.isArray(jsonRpcRequest.params), + "params should be an array", + ); + + const rawTransaction = hexStringToBytes(jsonRpcRequest.params[0]); + // The tx type is encoded in the first byte, and it must be the EIP-1559 one + assert.equal(rawTransaction[0], 2); + }); + + it(`should throw when the data field is undefined and the "to" field is ${testCase}`, async () => { + const tx = { + to: testCase, + // In this test scenario, the "data" field is omitted + from: "0xb5bc06d4548a3ac17d72b372ae1e416bf65b8ead", + gas: numberToHexString(30000), + nonce: numberToHexString(0), + value: numberToHexString(1), + chainId: numberToHexString(MOCK_PROVIDER_CHAIN_ID), + maxFeePerGas: numberToHexString(12), + maxPriorityFeePerGas: numberToHexString(2), + }; + + const jsonRpcRequest = getJsonRpcRequest(1, "eth_sendTransaction", [ + tx, + ]); + + assertRejectsWithHardhatError( + () => localAccountsHandler.handle(jsonRpcRequest), + HardhatError.ERRORS.NETWORK + .DATA_FIELD_CANNOT_BE_NULL_WITH_NULL_ADDRESS, + {}, + ); + }); + } + }); + it("should, given two identical tx, return the same raw transaction", async () => { const jsonRpcRequest = getJsonRpcRequest(1, "eth_sendTransaction", [ {