Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.

Commit 06a74f0

Browse files
committed
fix: fallback to bufferification if JSON.stringify fails due to Invalid string length error
1 parent 74c3a7b commit 06a74f0

File tree

1 file changed

+44
-23
lines changed

1 file changed

+44
-23
lines changed

src/chains/ethereum/ethereum/src/connector.ts

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export class Connector<
4646
{
4747
#provider: EthereumProvider;
4848

49-
static BUFFERIFY_THRESHOLD: number = 100000;
49+
static BUFFERIFY_THRESHOLD: number = 30000;
5050

5151
get provider() {
5252
return this.#provider;
@@ -130,29 +130,50 @@ export class Connector<
130130
if (
131131
payload.method === "debug_traceTransaction" &&
132132
typeof results === "object" &&
133-
Array.isArray(results.structLogs) &&
134-
// for "large" debug_traceTransaction results we convert to individual
135-
// parts of the response to Buffers, yielded via a Generator function,
136-
// instead of using JSON.stringify. This is necessary because we:
137-
// * avoid V8's maximum string length limit of 1GB
138-
// * avoid and the max Buffer length limit of ~2GB (on 64bit
139-
// architectures).
140-
// * avoid heap allocation failures due to trying to hold too much
141-
// data in memory (which can happen if we don't immediately consume
142-
// the `format` result -- by buffering everything into one array,
143-
// for example)
144-
//
145-
// We don't do this for everything because the bufferfication is so very
146-
// very slow.
147-
//
148-
// TODO(perf): an even better way of solving this would be to convert
149-
// `debug_traceTransaction` to a generator that yields chunks (of
150-
// Buffer) as soon as they're available. We could then `write` these
151-
// individual chunks immediately and our memory use would stay
152-
// relatively low and constant.
153-
results.structLogs.length > this.BUFFERIFY_THRESHOLD
133+
Array.isArray(results.structLogs)
154134
) {
155-
return bufferify(json, "");
135+
if (
136+
// for "large" debug_traceTransaction results we convert to individual
137+
// parts of the response to Buffers, yielded via a Generator function,
138+
// instead of using JSON.stringify. This is necessary because we:
139+
// * avoid V8's maximum string length limit of 1GB
140+
// * avoid and the max Buffer length limit of ~2GB (on 64bit
141+
// architectures).
142+
// * avoid heap allocation failures due to trying to hold too much
143+
// data in memory (which can happen if we don't immediately consume
144+
// the `format` result -- by buffering everything into one array,
145+
// for example)
146+
//
147+
// We don't do this for everything because the bufferfication is so very
148+
// very slow.
149+
//
150+
// TODO(perf): an even better way of solving this would be to convert
151+
// `debug_traceTransaction` to a generator that yields chunks (of
152+
// Buffer) as soon as they're available. We could then `write` these
153+
// individual chunks immediately and our memory use would stay
154+
// relatively low and constant.
155+
results.structLogs.length > this.BUFFERIFY_THRESHOLD
156+
) {
157+
return bufferify(json, "");
158+
} else {
159+
try {
160+
// struct logs that are under the BUFFERIFY_THRESHOLD can still
161+
// cause stringification to fail, so we need to try/catch this
162+
// and fall back to `bufferify` if it does.
163+
// We don't go straight to `bufferify` because it's much slower than
164+
// `JSON.stringify`.
165+
return JSON.stringify(json);
166+
} catch (e) {
167+
if (
168+
e instanceof RangeError &&
169+
e.message === "Invalid string length"
170+
) {
171+
return bufferify(json, "");
172+
} else {
173+
throw e;
174+
}
175+
}
176+
}
156177
} else {
157178
return JSON.stringify(json);
158179
}

0 commit comments

Comments
 (0)