Skip to content

Commit 546af5d

Browse files
authored
Merge pull request #6965 from NomicFoundation/fix-node-crash
fix: node crash when sending tx with insufficient funds
2 parents 55ad313 + 955fbb8 commit 546af5d

File tree

4 files changed

+423
-113
lines changed

4 files changed

+423
-113
lines changed

.changeset/twenty-lies-destroy.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"hardhat": patch
3+
---
4+
5+
Fix node crash when sending a tx with insufficient funds

v-next/hardhat/src/internal/builtin-plugins/node/json-rpc/handler.ts

Lines changed: 61 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import type {
77
import type { IncomingMessage, ServerResponse } from "node:http";
88
import type WebSocket from "ws";
99

10+
import { ensureError } from "@nomicfoundation/hardhat-utils/error";
11+
import { isObject } from "@nomicfoundation/hardhat-utils/lang";
12+
1013
import {
1114
isJsonRpcRequest,
1215
isJsonRpcResponse,
@@ -36,10 +39,11 @@ export class JsonRpcHandler {
3639
return;
3740
}
3841

39-
let jsonHttpRequest: any;
42+
let jsonHttpRequest: unknown;
4043
try {
4144
jsonHttpRequest = await _readJsonHttpRequest(req);
4245
} catch (error) {
46+
ensureError(error);
4347
this.#sendResponse(res, _handleError(error));
4448
return;
4549
}
@@ -48,7 +52,9 @@ export class JsonRpcHandler {
4852
// the following code block could be safely removed.
4953
if (Array.isArray(jsonHttpRequest)) {
5054
const responses = await Promise.all(
51-
jsonHttpRequest.map((singleReq: any) => this.#handleRequest(singleReq)),
55+
jsonHttpRequest.map((singleReq: unknown) =>
56+
this.#handleRequest(singleReq),
57+
),
5258
);
5359

5460
this.#sendResponse(res, responses);
@@ -80,6 +86,7 @@ export class JsonRpcHandler {
8086
}),
8187
);
8288
} catch (error) {
89+
ensureError(error);
8390
_handleError(error);
8491
}
8592
};
@@ -100,6 +107,7 @@ export class JsonRpcHandler {
100107
)
101108
: await this.#handleWsRequest(rpcReq, subscriptions);
102109
} catch (error) {
110+
ensureError(error);
103111
rpcResp = _handleError(error);
104112
}
105113

@@ -142,28 +150,36 @@ export class JsonRpcHandler {
142150
res.end(JSON.stringify(rpcResp));
143151
}
144152

145-
async #handleRequest(req: JsonRpcRequest): Promise<JsonRpcResponse> {
146-
req.params = req.params ?? [];
153+
async #handleRequest(payload: unknown): Promise<JsonRpcResponse> {
154+
if (!isObject(payload)) {
155+
return _handleError(new InvalidRequestError());
156+
}
157+
158+
const maybeReq = {
159+
...payload,
160+
params: payload.params ?? [],
161+
};
147162

148-
if (!isJsonRpcRequest(req)) {
163+
if (!isJsonRpcRequest(maybeReq)) {
149164
return _handleError(new InvalidRequestError());
150165
}
151166

152-
const rpcReq: JsonRpcRequest = req;
167+
const rpcReq: JsonRpcRequest = maybeReq;
153168
let rpcResp: JsonRpcResponse | undefined;
154169

155170
try {
156171
const result = await this.#provider.request({
157-
method: req.method,
158-
params: req.params,
172+
method: rpcReq.method,
173+
params: rpcReq.params,
159174
});
160175

161176
rpcResp = {
162177
jsonrpc: "2.0",
163-
id: req.id,
178+
id: rpcReq.id,
164179
result,
165180
};
166181
} catch (error) {
182+
ensureError(error);
167183
rpcResp = _handleError(error);
168184
}
169185

@@ -195,8 +211,8 @@ export class JsonRpcHandler {
195211
}
196212
}
197213

198-
const _readJsonHttpRequest = async (req: IncomingMessage): Promise<any> => {
199-
let json;
214+
const _readJsonHttpRequest = async (req: IncomingMessage): Promise<unknown> => {
215+
let json: unknown;
200216

201217
try {
202218
const bytes: number[] = [];
@@ -233,26 +249,7 @@ const _readWsRequest = (msg: string): JsonRpcRequest | JsonRpcRequest[] => {
233249
return json;
234250
};
235251

236-
const _handleError = (error: any): JsonRpcResponse => {
237-
// extract the relevant fields from the error before wrapping it
238-
let txHash: string | undefined;
239-
let returnData: string | undefined;
240-
241-
if (error.transactionHash !== undefined) {
242-
txHash = error.transactionHash;
243-
}
244-
if (error.data !== undefined) {
245-
if (error.data.data !== undefined) {
246-
returnData = error.data.data;
247-
} else {
248-
returnData = error.data;
249-
}
250-
251-
if (txHash === undefined && error.data.transactionHash !== undefined) {
252-
txHash = error.data.transactionHash;
253-
}
254-
}
255-
252+
const _handleError = (error: Error): JsonRpcResponse => {
256253
// In case of non-hardhat error, treat it as internal and associate the appropriate error code.
257254
if (!ProviderError.isProviderError(error)) {
258255
error = new InternalError(undefined, error);
@@ -262,24 +259,46 @@ const _handleError = (error: any): JsonRpcResponse => {
262259
jsonrpc: "2.0",
263260
id: null,
264261
error: {
265-
code: error.code,
262+
code:
263+
"code" in error && typeof error.code === "number"
264+
? error.code
265+
: InternalError.CODE,
266266
message: error.message,
267+
data: {
268+
message: error.message,
269+
txHash: extractTxHash(error),
270+
data: extractReturnData(error),
271+
},
267272
},
268273
};
269274

270-
const data: any = {
271-
message: error.message,
272-
};
275+
return response;
276+
};
273277

274-
if (txHash !== undefined) {
275-
data.txHash = txHash;
278+
function extractTxHash(error: Error): string | undefined {
279+
if ("transactionHash" in error && typeof error.transactionHash === "string") {
280+
return error.transactionHash;
281+
}
282+
283+
if (
284+
"data" in error &&
285+
isObject(error.data) &&
286+
typeof error.data.transactionHash === "string"
287+
) {
288+
return error.data.transactionHash;
276289
}
290+
}
277291

278-
if (returnData !== undefined) {
279-
data.data = returnData;
292+
function extractReturnData(error: Error): string | undefined {
293+
if (!("data" in error)) {
294+
return undefined;
280295
}
281296

282-
response.error.data = data;
297+
if (typeof error.data === "string") {
298+
return error.data;
299+
}
283300

284-
return response;
285-
};
301+
if (isObject(error.data) && typeof error.data.data === "string") {
302+
return error.data.data;
303+
}
304+
}

0 commit comments

Comments
 (0)