Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion sandbox/src/chain/jsonRpcWriteClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface CreateJsonRpcWriteClientOptions {
chainId: number;
privateKey: string;
pollIntervalMs?: number;
receiptTimeoutMs?: number;
fetchImpl?: typeof fetch;
}

Expand Down Expand Up @@ -109,8 +110,11 @@ async function waitForReceipt(
rpcUrl: string,
transactionHash: `0x${string}`,
pollIntervalMs: number,
receiptTimeoutMs: number,
fetchImpl: typeof fetch
): Promise<TransactionReceiptResult> {
const startedAt = Date.now();

for (;;) {
const receipt = await jsonRpcRequest<RawTransactionReceipt | null>(
rpcUrl,
Expand All @@ -120,6 +124,11 @@ async function waitForReceipt(
);

if (!receipt) {
const elapsedMs = Date.now() - startedAt;
if (elapsedMs >= receiptTimeoutMs) {
throw new Error(`transaction ${transactionHash} was not mined within ${receiptTimeoutMs}ms`);
}

await sleep(pollIntervalMs);
continue;
}
Expand All @@ -146,6 +155,7 @@ export function createJsonRpcWriteClient(
const wallet = new Wallet(options.privateKey);
const fetchImpl = options.fetchImpl ?? fetch;
const pollIntervalMs = options.pollIntervalMs ?? 1000;
const receiptTimeoutMs = options.receiptTimeoutMs ?? 120_000;
const signerAddress = wallet.address.toLowerCase();

return {
Expand Down Expand Up @@ -195,7 +205,7 @@ export function createJsonRpcWriteClient(
fetchImpl
);

return waitForReceipt(options.rpcUrl, transactionHash, pollIntervalMs, fetchImpl);
return waitForReceipt(options.rpcUrl, transactionHash, pollIntervalMs, receiptTimeoutMs, fetchImpl);
}
};
}
33 changes: 33 additions & 0 deletions sandbox/tests/chain/jsonRpcWriteClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,39 @@ test("createJsonRpcWriteClient sends a signed raw transaction and waits for a su
assert.equal(parsedTx.from?.toLowerCase(), SIGNER_ADDRESS);
});


test("createJsonRpcWriteClient times out when the receipt never arrives", async () => {
const txHash = `0x${"d".repeat(64)}`;
const fetchImpl = buildMockFetch(
[
{ jsonrpc: "2.0", id: 1, result: TEST_NONCE_HEX },
{ jsonrpc: "2.0", id: 1, result: TEST_GAS_LIMIT_HEX },
{ jsonrpc: "2.0", id: 1, result: TEST_GAS_PRICE_HEX },
{ jsonrpc: "2.0", id: 1, result: txHash },
{ jsonrpc: "2.0", id: 1, result: null }
],
[]
);

const client = createJsonRpcWriteClient({
rpcUrl: "http://localhost:8545",
chainId: TEST_CHAIN_ID,
privateKey: PRIVATE_KEY,
pollIntervalMs: 0,
receiptTimeoutMs: 0,
fetchImpl
});

await assert.rejects(
() =>
client.submitTransaction({
to: CONTRACT_ADDRESS,
data: RECORD_AUDIT_RESULT_CALL_DATA
}),
/was not mined within 0ms/i
);
});

test("createJsonRpcWriteClient throws a clear error when the mined receipt has status 0x0", async () => {
const txHash = `0x${"e".repeat(64)}`;
const fetchImpl = buildMockFetch(
Expand Down