TL;DR
chain/jsonRpcWriteClient.ts polls for a transaction receipt in an infinite for (;;) loop with no timeout or maximum retry count.
Details
// sandbox/src/chain/jsonRpcWriteClient.ts ~line 108
for (;;) {
const receipt = await jsonRpcRequest<RawTransactionReceipt | null>(...);
if (!receipt) {
await sleep(pollIntervalMs);
continue; // no exit condition other than receipt arriving
}
// ...
}
If the transaction is dropped (nonce collision, gas price too low, RPC node restart), receipt is always null and the function never returns. The caller — the audit writeback path — hangs indefinitely, blocking the entire listener daemon.
Suggested Fix
Add a deadline parameter:
const deadline = Date.now() + timeoutMs;
for (;;) {
if (Date.now() > deadline) throw new Error(`waitForReceipt timed out after ${timeoutMs}ms`);
const receipt = await jsonRpcRequest<RawTransactionReceipt | null>(...);
if (receipt) return receipt;
await sleep(pollIntervalMs);
}
References
sandbox/src/chain/jsonRpcWriteClient.ts:108 — the loop
sandbox/src/listener/retryWritebackQueue.ts — the caller that would hang
TL;DR
chain/jsonRpcWriteClient.tspolls for a transaction receipt in an infinitefor (;;)loop with no timeout or maximum retry count.Details
If the transaction is dropped (nonce collision, gas price too low, RPC node restart),
receiptis alwaysnulland the function never returns. The caller — the audit writeback path — hangs indefinitely, blocking the entire listener daemon.Suggested Fix
Add a deadline parameter:
References
sandbox/src/chain/jsonRpcWriteClient.ts:108— the loopsandbox/src/listener/retryWritebackQueue.ts— the caller that would hang