feat: add undici dependency + dynamic gas multiplier for tx retry
- Install undici package for fetch proxy support - Add gas multiplier on retry to handle "replacement transaction underpriced" - Attempt 1: 1x gas - Attempt 2: 2x gas - Attempt 3: 4x gas Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
56f59eabdf
commit
d24c5f2e81
Generated
+10
@@ -14,6 +14,7 @@
|
||||
"dotenv": "^16.4.7",
|
||||
"ethers": "^5.8.0",
|
||||
"https-proxy-agent": "^7.0.6",
|
||||
"undici": "^7.22.0",
|
||||
"ws": "^8.19.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -1828,6 +1829,15 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "7.22.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-7.22.0.tgz",
|
||||
"integrity": "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20.18.1"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "5.26.5",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"dotenv": "^16.4.7",
|
||||
"ethers": "^5.8.0",
|
||||
"https-proxy-agent": "^7.0.6",
|
||||
"undici": "^7.22.0",
|
||||
"ws": "^8.19.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
+20
-4
@@ -118,6 +118,9 @@ async function _doExecSafeCall(to, data, description = '') {
|
||||
if (description) logger.info(`MM: exec safe tx — ${description}`);
|
||||
|
||||
let lastErr;
|
||||
// Track gas price multiplier for replacement transactions
|
||||
let gasMultiplier = 1;
|
||||
|
||||
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
||||
try {
|
||||
const provider = await getPolygonProvider();
|
||||
@@ -149,9 +152,15 @@ async function _doExecSafeCall(to, data, description = '') {
|
||||
// Polygon requires maxPriorityFeePerGas ≥ 25 Gwei.
|
||||
// Some RPC nodes (e.g. lava.build) return a stale low estimate, so we enforce a floor.
|
||||
const feeData = await provider.getFeeData();
|
||||
const MIN_TIP = ethers.utils.parseUnits('30', 'gwei');
|
||||
const gasTip = feeData.maxPriorityFeePerGas?.gt(MIN_TIP) ? feeData.maxPriorityFeePerGas : MIN_TIP;
|
||||
const gasFeeCap = feeData.maxFeePerGas ?? ethers.utils.parseUnits('500', 'gwei');
|
||||
|
||||
// Increase gas price on retry to replace pending transaction
|
||||
// Multiplier: 1x → 2x → 4x on subsequent attempts
|
||||
const MIN_TIP = ethers.utils.parseUnits('30', 'gwei').mul(Math.ceil(gasMultiplier));
|
||||
const gasTip = feeData.maxPriorityFeePerGas?.mul(Math.ceil(gasMultiplier)).gt(MIN_TIP)
|
||||
? feeData.maxPriorityFeePerGas.mul(Math.ceil(gasMultiplier))
|
||||
: MIN_TIP;
|
||||
const gasFeeCap = (feeData.maxFeePerGas ?? ethers.utils.parseUnits('500', 'gwei'))
|
||||
.mul(Math.ceil(gasMultiplier * 1.5));
|
||||
|
||||
const tx = await safe.execTransaction(
|
||||
to, 0, data, 0, 0, 0, 0,
|
||||
@@ -169,7 +178,14 @@ async function _doExecSafeCall(to, data, description = '') {
|
||||
const friendly = parseOnchainError(err);
|
||||
|
||||
if (attempt < MAX_RETRIES) {
|
||||
logger.warn(`MM: transaction failed (attempt ${attempt}/${MAX_RETRIES}): ${friendly} — retrying in ${RETRY_DELAY / 1000}s...`);
|
||||
// Increase gas multiplier for replacement transaction
|
||||
if (err?.message?.includes('replacement transaction underpriced') ||
|
||||
err?.message?.includes('Gas price too low to replace')) {
|
||||
gasMultiplier *= 2;
|
||||
logger.warn(`MM: transaction failed (attempt ${attempt}/${MAX_RETRIES}): ${friendly} — increasing gas ${gasMultiplier}x and retrying...`);
|
||||
} else {
|
||||
logger.warn(`MM: transaction failed (attempt ${attempt}/${MAX_RETRIES}): ${friendly} — retrying in ${RETRY_DELAY / 1000}s...`);
|
||||
}
|
||||
await sleep(RETRY_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user