Skip to content

Commit 2cd0f8b

Browse files
fix: resolve CodeRabbit critical issues in rate limiting and batch optimizer
- Fix per-IP rate limit calculation to ensure minimum of 1 request even when global limit < 10 - Fix batch optimizer savings calculation by using proper individual gas estimation - Add bigint formatting helpers (formatWei, formatPercent) to avoid precision loss - Calculate individual gas as batch gas + savings percentage instead of just base gas - Use strategy-specific savings percentages (0% speed, 15% balanced, 25% cost) Resolves critical issues identified by CodeRabbit review
1 parent 270538d commit 2cd0f8b

File tree

2 files changed

+52
-8
lines changed

2 files changed

+52
-8
lines changed

src/server/middleware/rate-limit.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ export function withRateLimit(server: FastifyInstance) {
3434
const ipCount = await redis.incr(ipKey);
3535
redis.expire(ipKey, 2 * 60);
3636

37-
const perIpLimit = Math.floor(env.GLOBAL_RATE_LIMIT_PER_MIN / 10);
37+
// Ensure minimum of 1 request per IP even for low global limits
38+
const perIpLimit = Math.max(1, Math.floor(env.GLOBAL_RATE_LIMIT_PER_MIN / 10));
3839
if (ipCount > perIpLimit) {
3940
throw createCustomError(
4041
`Too many requests from your IP. Please reduce your calls to ${perIpLimit} requests/minute.`,

src/server/routes/transaction/batch-optimizer.ts

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,41 @@ import { eth_gasPrice, getRpcClient, type Address } from "thirdweb";
99
import { thirdwebClient } from "../../../shared/utils/sdk";
1010
import { getAddress } from "thirdweb";
1111

12+
// Helper constants and functions for bigint formatting
13+
const WEI_PER_ETH = 10n ** 18n;
14+
15+
const formatWei = (value: bigint, fractionDigits = 6): string => {
16+
const sign = value < 0n ? "-" : "";
17+
const abs = value < 0n ? -value : value;
18+
const integer = abs / WEI_PER_ETH;
19+
const remainder = abs % WEI_PER_ETH;
20+
if (fractionDigits === 0) {
21+
return `${sign}${integer.toString()}`;
22+
}
23+
const scale = 10n ** BigInt(fractionDigits);
24+
const fractional = (remainder * scale) / WEI_PER_ETH;
25+
return `${sign}${integer.toString()}.${fractional
26+
.toString()
27+
.padStart(fractionDigits, "0")}`;
28+
};
29+
30+
const formatPercent = (
31+
numerator: bigint,
32+
denominator: bigint,
33+
fractionDigits = 1,
34+
): string => {
35+
if (denominator === 0n || numerator === 0n) {
36+
return `0.${"0".repeat(fractionDigits)}`;
37+
}
38+
const scale = 10n ** BigInt(fractionDigits);
39+
const scaled = (numerator * 100n * scale) / denominator;
40+
const integer = scaled / scale;
41+
const remainder = scaled % scale;
42+
return `${integer.toString()}.${remainder
43+
.toString()
44+
.padStart(fractionDigits, "0")}`;
45+
};
46+
1247
const batchRequestSchema = Type.Object({
1348
fromAddress: Type.String({
1449
description: "The wallet address to send transactions from",
@@ -204,12 +239,20 @@ export async function estimateBatchTransactions(fastify: FastifyInstance) {
204239
);
205240
const totalCostWei = totalGasEstimate * gasPrice;
206241

207-
// Calculate savings vs individual transactions
208-
// Batching saves on base gas and reduces total gas by ~15%
209-
const individualCostWei =
210-
BigInt(transactions.length) * (21000n * gasPrice);
242+
// Calculate savings vs individual transactions with proper gas estimation
243+
// Apply expected savings percentages based on optimization strategy
244+
const savingsBpsByStrategy: Record<"speed" | "balanced" | "cost", bigint> = {
245+
speed: 0n, // No savings for speed mode
246+
balanced: 15n, // 15% savings for balanced
247+
cost: 25n, // 25% savings for cost mode
248+
};
249+
const savingsBps = savingsBpsByStrategy[optimization] ?? 15n;
250+
251+
// Individual gas estimate = batch gas * (100 + savings%) / 100
252+
const individualGasEstimate = (totalGasEstimate * (100n + savingsBps)) / 100n;
253+
const individualCostWei = individualGasEstimate * gasPrice;
254+
const savingsGas = individualGasEstimate - totalGasEstimate;
211255
const savingsWei = individualCostWei - totalCostWei;
212-
const savingsPercent = Number((savingsWei * 100n) / individualCostWei);
213256

214257
// Optimization strategy recommendations
215258
let estimatedTimeSeconds = 30;
@@ -266,12 +309,12 @@ export async function estimateBatchTransactions(fastify: FastifyInstance) {
266309
totalGasEstimate: totalGasEstimate.toString(),
267310
gasPrice: gasPrice.toString(),
268311
totalCostWei: totalCostWei.toString(),
269-
totalCostEth: (Number(totalCostWei) / 1e18).toFixed(6),
312+
totalCostEth: formatWei(totalCostWei, 6),
270313
perTransactionCostWei: (totalCostWei / BigInt(transactions.length)).toString(),
271314
},
272315
optimization: {
273316
strategy: optimization,
274-
savingsVsIndividual: `${savingsPercent.toFixed(1)}% (${(Number(savingsWei) / 1e18).toFixed(6)} ETH)`,
317+
savingsVsIndividual: `${formatPercent(savingsGas, individualGasEstimate, 1)}% (${formatWei(savingsWei, 6)} ETH)`,
275318
estimatedTimeSeconds,
276319
recommendation,
277320
},

0 commit comments

Comments
 (0)