Skip to content

Commit a802d87

Browse files
authored
fix: increase gas multiple on each resend attempt (#746)
* fix: increase gas multiple on each resend attempt * fix clamp * update gas retry logic * remove clamp * unused vars * update gasPrice
1 parent 8f668fe commit a802d87

File tree

2 files changed

+133
-14
lines changed

2 files changed

+133
-14
lines changed
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import type { Hex } from "thirdweb";
2+
import { describe, expect, it } from "vitest";
3+
import { _updateGasFees } from "../worker/tasks/sendTransactionWorker";
4+
5+
describe("_updateGasFees", () => {
6+
const base = {
7+
// Irrelevant values for testing.
8+
chainId: 1,
9+
data: "0x0" as Hex,
10+
gas: 21000n,
11+
to: undefined,
12+
nonce: undefined,
13+
accessList: undefined,
14+
value: undefined,
15+
};
16+
17+
it("returns the original transaction on first send (resendCount = 0)", () => {
18+
let result = _updateGasFees({ ...base, gasPrice: 100n }, 0, undefined);
19+
expect(result.gasPrice).toEqual(100n);
20+
21+
result = _updateGasFees(
22+
{ ...base, maxFeePerGas: 100n, maxPriorityFeePerGas: 10n },
23+
0,
24+
undefined,
25+
);
26+
expect(result.maxFeePerGas).toEqual(100n);
27+
expect(result.maxPriorityFeePerGas).toEqual(10n);
28+
});
29+
30+
it("doubles gasPrice for legacy transactions", () => {
31+
const result = _updateGasFees({ ...base, gasPrice: 100n }, 1, {});
32+
expect(result.gasPrice).toBe(200n);
33+
});
34+
35+
it("caps gasPrice multiplier at 10x", () => {
36+
const result = _updateGasFees({ ...base, gasPrice: 100n }, 10, {});
37+
expect(result.gasPrice).toBe(1000n);
38+
});
39+
40+
it("updates maxPriorityFeePerGas and maxFeePerGas for EIP-1559 transactions", () => {
41+
const result = _updateGasFees(
42+
{ ...base, maxFeePerGas: 100n, maxPriorityFeePerGas: 10n },
43+
3,
44+
{},
45+
);
46+
expect(result.maxPriorityFeePerGas).toBe(60n);
47+
expect(result.maxFeePerGas).toBe(260n);
48+
});
49+
50+
it("respects overrides for maxPriorityFeePerGas", () => {
51+
const result = _updateGasFees(
52+
{ ...base, maxFeePerGas: 100n, maxPriorityFeePerGas: 10n },
53+
3,
54+
{ maxPriorityFeePerGas: 10n },
55+
);
56+
expect(result.maxPriorityFeePerGas).toBe(10n); // matches override
57+
expect(result.maxFeePerGas).toBe(210n);
58+
});
59+
60+
it("respects overrides for maxFeePerGas", () => {
61+
const result = _updateGasFees(
62+
{ ...base, maxFeePerGas: 100n, maxPriorityFeePerGas: 10n },
63+
3,
64+
{ maxFeePerGas: 100n },
65+
);
66+
expect(result.maxPriorityFeePerGas).toBe(60n);
67+
expect(result.maxFeePerGas).toBe(100n); // matches override
68+
});
69+
70+
it("returns correct values when only maxPriorityFeePerGas is set", () => {
71+
const result = _updateGasFees(
72+
{ ...base, maxPriorityFeePerGas: 10n },
73+
3,
74+
{},
75+
);
76+
expect(result.maxPriorityFeePerGas).toBe(60n);
77+
expect(result.maxFeePerGas).toBeUndefined();
78+
});
79+
80+
it("returns correct values when only maxFeePerGas is set", () => {
81+
const result = _updateGasFees({ ...base, maxFeePerGas: 80n }, 3, {});
82+
expect(result.maxFeePerGas).toBe(160n);
83+
});
84+
});

src/worker/tasks/sendTransactionWorker.ts

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ const _resendTransaction = async (
422422

423423
// Populate the transaction with double gas.
424424
const { chainId, from, overrides, sentTransactionHashes } = sentTransaction;
425-
const populatedTransaction = await toSerializableTransaction({
425+
let populatedTransaction = await toSerializableTransaction({
426426
from: getChecksumAddress(from),
427427
transaction: {
428428
client: thirdwebClient,
@@ -436,19 +436,12 @@ const _resendTransaction = async (
436436
},
437437
});
438438

439-
// Double gas fee settings if they were not provded in `overrides`.
440-
if (populatedTransaction.gasPrice) {
441-
populatedTransaction.gasPrice *= 2n;
442-
}
443-
if (populatedTransaction.maxFeePerGas && !overrides?.maxFeePerGas) {
444-
populatedTransaction.maxFeePerGas *= 2n;
445-
}
446-
if (
447-
populatedTransaction.maxPriorityFeePerGas &&
448-
!overrides?.maxPriorityFeePerGas
449-
) {
450-
populatedTransaction.maxPriorityFeePerGas *= 2n;
451-
}
439+
// Increase gas fees for this resend attempt.
440+
populatedTransaction = _updateGasFees(
441+
populatedTransaction,
442+
sentTransaction.resendCount + 1,
443+
sentTransaction.overrides,
444+
);
452445

453446
job.log(`Populated transaction: ${stringify(populatedTransaction)}`);
454447

@@ -559,6 +552,48 @@ const _hasExceededTimeout = (
559552
const _minutesFromNow = (minutes: number) =>
560553
new Date(Date.now() + minutes * 60_000);
561554

555+
/**
556+
* Computes aggressive gas fees when resending a transaction.
557+
*
558+
* For legacy transactions (pre-EIP1559):
559+
* - Gas price = (2 * attempt) * estimatedGasPrice, capped at 10x.
560+
*
561+
* For other transactions:
562+
* - maxPriorityFeePerGas = (2 * attempt) * estimatedMaxPriorityFeePerGas, capped at 10x.
563+
* - maxFeePerGas = (2 * estimatedMaxFeePerGas) + maxPriorityFeePerGas.
564+
*
565+
* @param populatedTransaction The transaction with estimated gas from RPC.
566+
* @param resendCount The resend attempt #. Example: 2 = the transaction was initially sent, then resent once. This is the second resend attempt.
567+
*/
568+
export const _updateGasFees = (
569+
populatedTransaction: PopulatedTransaction,
570+
resendCount: number,
571+
overrides: SentTransaction["overrides"],
572+
): PopulatedTransaction => {
573+
if (resendCount === 0) {
574+
return populatedTransaction;
575+
}
576+
577+
const multiplier = BigInt(Math.min(10, resendCount * 2));
578+
579+
const updated = { ...populatedTransaction };
580+
581+
// Update gas fees (unless they were explicitly overridden).
582+
583+
if (updated.gasPrice && !overrides?.gasPrice) {
584+
updated.gasPrice *= multiplier;
585+
}
586+
if (updated.maxPriorityFeePerGas && !overrides?.maxPriorityFeePerGas) {
587+
updated.maxPriorityFeePerGas *= multiplier;
588+
}
589+
if (updated.maxFeePerGas && !overrides?.maxFeePerGas) {
590+
updated.maxFeePerGas =
591+
updated.maxFeePerGas * 2n + (updated.maxPriorityFeePerGas ?? 0n);
592+
}
593+
594+
return updated;
595+
};
596+
562597
// Must be explicitly called for the worker to run on this host.
563598
export const initSendTransactionWorker = () => {
564599
const _worker = new Worker(SendTransactionQueue.q.name, handler, {

0 commit comments

Comments
 (0)