@sidhujag/sysweb3-keyring 1.0.613 → 1.0.616

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,6 +7,7 @@ exports.EthereumTransactions = void 0;
7
7
  const eth_sig_util_1 = require("@metamask/eth-sig-util");
8
8
  const sysweb3_network_1 = require("@sidhujag/sysweb3-network");
9
9
  const sysweb3_utils_1 = require("@sidhujag/sysweb3-utils");
10
+ const transaction_1 = require("ethers/transaction");
10
11
  const omit_1 = __importDefault(require("lodash/omit"));
11
12
  const ethers_v6_1 = require("../ethers-v6");
12
13
  const providers_1 = require("../providers");
@@ -427,18 +428,18 @@ class EthereumTransactions {
427
428
  throw error;
428
429
  }
429
430
  };
430
- this.getFeeDataWithDynamicMaxPriorityFeePerGas = async () => {
431
+ this.getFeeDataWithDynamicMaxPriorityFeePerGas = async (provider = this.web3Provider) => {
431
432
  let maxFeePerGas = this.toBigNumber(0);
432
433
  let maxPriorityFeePerGas = this.toBigNumber(0);
433
434
  try {
434
435
  // First, try to get the current gas price as a baseline
435
- const currentGasPrice = await this.web3Provider.getGasPrice();
436
- const block = await this.web3Provider.getBlock('latest');
436
+ const currentGasPrice = await provider.getGasPrice();
437
+ const block = await provider.getBlock('latest');
437
438
  if (block && block.baseFeePerGas != null) {
438
439
  const baseFeePerGas = ethers_v6_1.BigNumber.from(block.baseFeePerGas);
439
440
  try {
440
441
  // Some networks don't support this RPC method
441
- const ethMaxPriorityFee = await this.web3Provider.send('eth_maxPriorityFeePerGas', []);
442
+ const ethMaxPriorityFee = await provider.send('eth_maxPriorityFeePerGas', []);
442
443
  maxPriorityFeePerGas = ethers_v6_1.BigNumber.from(ethMaxPriorityFee);
443
444
  // Apply minimum priority fee override if set
444
445
  if (this.gasOverrides.minPriorityFee &&
@@ -463,10 +464,14 @@ class EthereumTransactions {
463
464
  : baselineMaxFee;
464
465
  }
465
466
  catch (e) {
467
+ if (e?.code === 'SYSWEB3_RPC_RESPONSE_INVALID' ||
468
+ e?.name === 'AbortError' ||
469
+ this.abortController.signal.aborted)
470
+ throw e;
466
471
  // Use a more aggressive fallback strategy with higher priority fees
467
472
  // Check if we can get fee history for better estimation
468
473
  try {
469
- const feeHistory = await this.web3Provider.send('eth_feeHistory', [
474
+ const feeHistory = await provider.send('eth_feeHistory', [
470
475
  '0x5', // Last 5 blocks
471
476
  'latest',
472
477
  [25, 50, 75], // Percentiles for priority fees
@@ -496,10 +501,15 @@ class EthereumTransactions {
496
501
  }
497
502
  }
498
503
  catch (feeHistoryError) {
504
+ if (feeHistoryError?.code === 'SYSWEB3_RPC_RESPONSE_INVALID' ||
505
+ feeHistoryError?.name === 'AbortError' ||
506
+ this.abortController.signal.aborted) {
507
+ throw feeHistoryError;
508
+ }
499
509
  // If fee history fails, use current gas price as reference
500
510
  console.warn('Fee history not available, using gas price based estimation');
501
511
  // Use 10% of current gas price as priority fee
502
- const currentGasPrice = await this.web3Provider.getGasPrice();
512
+ const currentGasPrice = await provider.getGasPrice();
503
513
  maxPriorityFeePerGas = currentGasPrice.mul(10).div(100);
504
514
  // Ensure minimum of 1 gwei
505
515
  const minPriority = ethers_v6_1.BigNumber.from('1000000000');
@@ -508,7 +518,7 @@ class EthereumTransactions {
508
518
  }
509
519
  }
510
520
  // Calculate maxFeePerGas based on current network conditions
511
- const currentGasPrice = await this.web3Provider.getGasPrice();
521
+ const currentGasPrice = await provider.getGasPrice();
512
522
  const baselineMaxFee = currentGasPrice.mul(120).div(100);
513
523
  const calculatedMaxFee = baseFeePerGas
514
524
  .mul(250)
@@ -538,6 +548,10 @@ class EthereumTransactions {
538
548
  return { maxFeePerGas, maxPriorityFeePerGas };
539
549
  }
540
550
  catch (error) {
551
+ if (error?.code === 'SYSWEB3_RPC_RESPONSE_INVALID' ||
552
+ error?.name === 'AbortError' ||
553
+ this.abortController.signal.aborted)
554
+ throw error;
541
555
  console.error(error);
542
556
  return { maxFeePerGas, maxPriorityFeePerGas };
543
557
  }
@@ -1060,13 +1074,101 @@ class EthereumTransactions {
1060
1074
  return await sendEVMTransaction();
1061
1075
  }
1062
1076
  };
1063
- this.sendTransactionWithEditedFee = async (txHash, isLegacy) => {
1064
- let tx = (await this.web3Provider.getTransaction(txHash));
1077
+ this.sendTransactionWithEditedFee = async (txHash, isLegacy = false, options = {}) => {
1078
+ const { assertCurrentContext, validateOriginal, beforeBroadcast, approvedMaximumFee, } = options;
1079
+ const previewOnly = options.previewOnly === true;
1080
+ if (!previewOnly &&
1081
+ (typeof approvedMaximumFee !== 'string' ||
1082
+ !/^\d{1,78}$/.test(approvedMaximumFee))) {
1083
+ throw new Error('Replacement fee approval required');
1084
+ }
1085
+ const assertCurrent = () => assertCurrentContext?.();
1086
+ const assertBeforeBroadcast = () => {
1087
+ assertCurrent();
1088
+ beforeBroadcast?.();
1089
+ assertCurrent();
1090
+ };
1091
+ const assertSignedSender = (signedTx) => {
1092
+ if (transaction_1.Transaction.from(signedTx).from?.toLowerCase() !== expectedSender) {
1093
+ throw new Error('Replacement signature does not match account');
1094
+ }
1095
+ };
1096
+ assertCurrent();
1097
+ const state = this.getState();
1098
+ const activeAccountType = options.targetAccount?.type ?? state.activeAccountType;
1099
+ const activeAccountId = options.targetAccount?.id ?? state.activeAccountId;
1100
+ const targetAccount = options.targetAccount
1101
+ ? { type: activeAccountType, id: activeAccountId }
1102
+ : undefined;
1103
+ const activeAccount = {
1104
+ ...state.accounts?.[activeAccountType]?.[activeAccountId],
1105
+ };
1106
+ const activeNetwork = { ...state.activeNetwork };
1107
+ if (!activeAccount.address ||
1108
+ (targetAccount && activeAccountType === types_1.KeyringAccountType.SmartAccount)) {
1109
+ throw new Error('Replacement signing account is unavailable');
1110
+ }
1111
+ const expectedSender = activeAccount.address.toLowerCase();
1112
+ const requestedHash = txHash.toLowerCase();
1113
+ const provider = this.web3Provider;
1114
+ assertCurrent();
1115
+ const validateLookup = (transaction) => {
1116
+ assertCurrent();
1117
+ if (!transaction)
1118
+ return null;
1119
+ const fields = (0, ethers_v6_1.normalizeTransactionRequest)(transaction);
1120
+ if (fields.hash != null)
1121
+ fields.hash = fields.hash.toLowerCase();
1122
+ const type = fields.type ??
1123
+ (fields.maxFeePerGas != null ? 2 : fields.accessList != null ? 1 : 0);
1124
+ if (![0, 1, 2].includes(type)) {
1125
+ throw new Error('Unsupported replacement transaction type');
1126
+ }
1127
+ fields.signature = fields.signature ?? {
1128
+ r: fields.r,
1129
+ s: fields.s,
1130
+ v: fields.v ?? fields.yParity,
1131
+ };
1132
+ fields.type = type;
1133
+ fields.data = fields.data ?? fields.input ?? '0x';
1134
+ if (type === 2)
1135
+ delete fields.gasPrice; // Effective RPC gas price is not signed in type 2.
1136
+ const original = transaction_1.Transaction.from(fields);
1137
+ if (!original.signature ||
1138
+ original.hash !== requestedHash ||
1139
+ original.from?.toLowerCase() !== expectedSender ||
1140
+ original.chainId !== BigInt(activeNetwork.chainId)) {
1141
+ throw new Error('Original transaction is not authenticated');
1142
+ }
1143
+ if (type !== 1)
1144
+ isLegacy = type === 0;
1145
+ if (isLegacy &&
1146
+ type === 1 &&
1147
+ activeAccountType === types_1.KeyringAccountType.Trezor) {
1148
+ throw new Error('Trezor does not support type 1 replacement signing');
1149
+ }
1150
+ // Read only detached, authenticated signed fields after asynchronous work.
1151
+ const authenticated = (0, providers_1.wrapTransactionResponse)({
1152
+ ...original.toJSON(),
1153
+ hash: original.hash,
1154
+ from: original.from,
1155
+ gasPrice: original.gasPrice ?? 0,
1156
+ maxFeePerGas: original.maxFeePerGas ?? 0,
1157
+ maxPriorityFeePerGas: original.maxPriorityFeePerGas ?? 0,
1158
+ signature: original.signature,
1159
+ blockNumber: transaction.blockNumber,
1160
+ });
1161
+ validateOriginal?.(authenticated);
1162
+ assertCurrent();
1163
+ return authenticated;
1164
+ };
1165
+ let tx = validateLookup(await provider.getTransaction(txHash));
1065
1166
  if (!tx) {
1066
1167
  // Retry a couple of times in case the node hasn't indexed the pending tx yet
1067
1168
  for (let attempt = 0; attempt < 2 && !tx; attempt++) {
1068
1169
  await new Promise((resolve) => setTimeout(resolve, 500 * (attempt + 1)));
1069
- tx = (await this.web3Provider.getTransaction(txHash));
1170
+ assertCurrent();
1171
+ tx = validateLookup(await provider.getTransaction(txHash));
1070
1172
  }
1071
1173
  if (!tx) {
1072
1174
  return {
@@ -1077,17 +1179,17 @@ class EthereumTransactions {
1077
1179
  };
1078
1180
  }
1079
1181
  }
1080
- const { activeAccountType, activeAccountId, accounts, activeNetwork } = this.getState();
1081
- const activeAccount = accounts[activeAccountType][activeAccountId];
1082
1182
  // Check if this might be a max send transaction by comparing total cost to balance
1083
- const currentBalance = await this.web3Provider.getBalance(activeAccount.address);
1183
+ const currentBalance = await provider.getBalance(activeAccount.address);
1184
+ assertCurrent();
1084
1185
  // Ensure all transaction values are resolved from promises
1085
1186
  const gasLimit = await Promise.resolve(tx.gasLimit);
1086
1187
  const gasPrice = await Promise.resolve(tx.gasPrice || 0);
1087
- const maxFeePerGas = await Promise.resolve(tx.maxFeePerGas || 0);
1088
- const maxPriorityFeePerGas = await Promise.resolve(tx.maxPriorityFeePerGas || 0);
1188
+ const maxFeePerGas = await Promise.resolve(tx.type === 1 ? tx.gasPrice : tx.maxFeePerGas);
1189
+ const maxPriorityFeePerGas = await Promise.resolve(tx.type === 1 ? tx.gasPrice : tx.maxPriorityFeePerGas);
1089
1190
  const txValue = await Promise.resolve(tx.value);
1090
1191
  const txData = await Promise.resolve(tx.data || '0x');
1192
+ assertCurrent();
1091
1193
  // Check if this is a contract call (has data)
1092
1194
  const isContractCall = txData && txData !== '0x' && txData.length > 2;
1093
1195
  const originalGasCost = isLegacy
@@ -1097,113 +1199,73 @@ class EthereumTransactions {
1097
1199
  // If original transaction used >95% of balance, it's likely a max send
1098
1200
  const balanceThreshold = currentBalance.mul(95).div(100);
1099
1201
  const isLikelyMaxSend = originalTotalCost.gt(balanceThreshold);
1100
- let txWithEditedFee;
1101
- // If the original tx has 0 or very low gas price, fetch current network gas prices
1102
1202
  const oldTxsGasValues = {
1103
1203
  maxFeePerGas: maxFeePerGas,
1104
1204
  maxPriorityFeePerGas: maxPriorityFeePerGas,
1105
1205
  gasPrice: gasPrice,
1106
1206
  gasLimit: gasLimit,
1107
1207
  };
1108
- // Only fetch current gas prices if the original tx has exactly 0 or undefined gas
1109
- // This avoids unnecessary backend calls for legitimate low-gas networks (L2s, testnets)
1110
- if (isLegacy) {
1111
- if (!oldTxsGasValues.gasPrice || oldTxsGasValues.gasPrice.isZero()) {
1112
- // Fetch current network gas price for replacement
1113
- const currentGasPrice = await this.web3Provider.getGasPrice();
1114
- oldTxsGasValues.gasPrice = currentGasPrice;
1115
- }
1116
- }
1117
- else {
1118
- // For EIP-1559 transactions
1119
- if (!oldTxsGasValues.maxFeePerGas ||
1120
- oldTxsGasValues.maxFeePerGas.isZero()) {
1121
- // Fetch current network fee data for replacement
1122
- const feeData = await this.getFeeDataWithDynamicMaxPriorityFeePerGas();
1123
- oldTxsGasValues.maxFeePerGas = ethers_v6_1.BigNumber.from(feeData.maxFeePerGas || 0);
1124
- oldTxsGasValues.maxPriorityFeePerGas = ethers_v6_1.BigNumber.from(feeData.maxPriorityFeePerGas || 0);
1208
+ // A zero-fee original may need a current estimate; the preview shows it
1209
+ // and the final candidate must stay within the user's approved ceiling.
1210
+ if (isLegacy && oldTxsGasValues.gasPrice.isZero()) {
1211
+ oldTxsGasValues.gasPrice = await provider.getGasPrice();
1212
+ assertCurrent();
1213
+ }
1214
+ else if (!isLegacy && oldTxsGasValues.maxFeePerGas.isZero()) {
1215
+ const feeData = await this.getFeeDataWithDynamicMaxPriorityFeePerGas(provider);
1216
+ assertCurrent();
1217
+ oldTxsGasValues.maxFeePerGas = ethers_v6_1.BigNumber.from(feeData.maxFeePerGas || 0);
1218
+ oldTxsGasValues.maxPriorityFeePerGas = ethers_v6_1.BigNumber.from(feeData.maxPriorityFeePerGas || 0);
1219
+ }
1220
+ const newGasValues = this.calculateNewGasValues(oldTxsGasValues, false, isLegacy);
1221
+ if (!newGasValues.gasLimit.gt(0))
1222
+ throw new Error('Invalid replacement gas limit');
1223
+ if (!isLegacy &&
1224
+ newGasValues.maxPriorityFeePerGas.gt(newGasValues.maxFeePerGas))
1225
+ throw new Error('Invalid replacement fee');
1226
+ const maximumFee = newGasValues.gasLimit.mul(isLegacy ? newGasValues.gasPrice : newGasValues.maxFeePerGas);
1227
+ let adjustedValue = txValue;
1228
+ if (isLikelyMaxSend && txValue.add(maximumFee).gt(currentBalance)) {
1229
+ if (isContractCall) {
1230
+ console.error('[SpeedUp] Cannot adjust value for contract call - rejecting speedup');
1231
+ return {
1232
+ isSpeedUp: false,
1233
+ error: true,
1234
+ code: 'CONTRACT_CALL_MAX_SEND',
1235
+ message: 'Cannot speed up a likely max-send contract call; value cannot be adjusted to fit new gas',
1236
+ };
1125
1237
  }
1238
+ adjustedValue = currentBalance.gt(maximumFee)
1239
+ ? currentBalance.sub(maximumFee)
1240
+ : ethers_v6_1.Zero;
1126
1241
  }
1127
- if (!isLegacy) {
1128
- const newGasValues = this.calculateNewGasValues(oldTxsGasValues, false, false);
1129
- let adjustedValue = txValue;
1130
- // For likely max sends, check if we need to adjust value
1131
- if (isLikelyMaxSend &&
1132
- newGasValues.gasLimit &&
1133
- newGasValues.maxFeePerGas) {
1134
- const newGasCost = newGasValues.gasLimit.mul(newGasValues.maxFeePerGas);
1135
- const newTotalCost = txValue.add(newGasCost);
1136
- if (newTotalCost.gt(currentBalance)) {
1137
- // If this is a contract call, we cannot adjust the value
1138
- if (isContractCall) {
1139
- console.error('[SpeedUp] Cannot adjust value for contract call - rejecting speedup');
1140
- return {
1141
- isSpeedUp: false,
1142
- error: true,
1143
- code: 'CONTRACT_CALL_MAX_SEND',
1144
- message: 'Cannot speed up a likely max-send contract call; value cannot be adjusted to fit new gas',
1145
- };
1146
- }
1147
- // For non-contract calls, reduce value to fit within balance (clamp at zero)
1148
- adjustedValue = currentBalance.gt(newGasCost)
1149
- ? currentBalance.sub(newGasCost)
1150
- : ethers_v6_1.Zero;
1151
- }
1152
- }
1153
- txWithEditedFee = {
1154
- from: tx.from,
1155
- to: tx.to,
1156
- nonce: tx.nonce,
1157
- value: adjustedValue,
1158
- data: txData,
1159
- maxFeePerGas: newGasValues.maxFeePerGas,
1160
- maxPriorityFeePerGas: newGasValues.maxPriorityFeePerGas,
1161
- gasLimit: newGasValues.gasLimit,
1162
- // Don't set type - let ethers auto-detect for EIP-1559
1163
- };
1164
- }
1165
- else {
1166
- const newGasValues = this.calculateNewGasValues(oldTxsGasValues, false, true);
1167
- let adjustedValue = txValue;
1168
- // For likely max sends, check if we need to adjust value
1169
- if (isLikelyMaxSend && newGasValues.gasLimit && newGasValues.gasPrice) {
1170
- const newGasCost = newGasValues.gasLimit.mul(newGasValues.gasPrice);
1171
- const newTotalCost = txValue.add(newGasCost);
1172
- if (newTotalCost.gt(currentBalance)) {
1173
- // If this is a contract call, we cannot adjust the value
1174
- if (isContractCall) {
1175
- console.error('[SpeedUp] Cannot adjust value for contract call - rejecting speedup');
1176
- return {
1177
- isSpeedUp: false,
1178
- error: true,
1179
- code: 'CONTRACT_CALL_MAX_SEND',
1180
- message: 'Cannot speed up a likely max-send contract call; value cannot be adjusted to fit new gas',
1181
- };
1182
- }
1183
- // For non-contract calls, reduce value to fit within balance (clamp at zero)
1184
- adjustedValue = currentBalance.gt(newGasCost)
1185
- ? currentBalance.sub(newGasCost)
1186
- : ethers_v6_1.Zero;
1187
- }
1188
- }
1189
- txWithEditedFee = {
1190
- from: tx.from,
1191
- to: tx.to,
1192
- nonce: tx.nonce,
1193
- value: adjustedValue,
1194
- data: txData,
1195
- gasLimit: newGasValues.gasLimit,
1196
- gasPrice: newGasValues.gasPrice,
1197
- type: 0, // Force Type 0 for legacy speedup
1198
- };
1199
- }
1242
+ const txWithEditedFee = {
1243
+ chainId: activeNetwork.chainId,
1244
+ from: tx.from,
1245
+ to: tx.to,
1246
+ nonce: tx.nonce,
1247
+ value: adjustedValue,
1248
+ data: txData,
1249
+ gasLimit: newGasValues.gasLimit,
1250
+ ...(tx.accessList != null ? { accessList: tx.accessList } : {}),
1251
+ ...(isLegacy
1252
+ ? { gasPrice: newGasValues.gasPrice, type: tx.type === 1 ? 1 : 0 }
1253
+ : {
1254
+ maxFeePerGas: newGasValues.maxFeePerGas,
1255
+ maxPriorityFeePerGas: newGasValues.maxPriorityFeePerGas,
1256
+ }),
1257
+ };
1258
+ if (previewOnly)
1259
+ return { isSpeedUp: false, maximumFee: maximumFee.toString() };
1260
+ if (maximumFee.gt(approvedMaximumFee))
1261
+ throw new Error('Replacement fee exceeds approval');
1200
1262
  // Ledger speedup handler
1201
1263
  const speedUpWithLedger = async () => {
1202
1264
  try {
1203
1265
  const resolvedParams = await (0, ethers_v6_1.resolveProperties)((0, omit_1.default)(txWithEditedFee, 'from'));
1204
1266
  const formatParams = {
1205
1267
  ...resolvedParams,
1206
- nonce: resolvedParams.nonce
1268
+ nonce: resolvedParams.nonce != null
1207
1269
  ? Number(resolvedParams.nonce.toString())
1208
1270
  : undefined,
1209
1271
  };
@@ -1211,7 +1273,7 @@ class EthereumTransactions {
1211
1273
  ? {
1212
1274
  ...formatParams,
1213
1275
  chainId: activeNetwork.chainId,
1214
- type: 0, // Need explicit type for hardware wallet serialization
1276
+ type: txWithEditedFee.type,
1215
1277
  }
1216
1278
  : {
1217
1279
  ...formatParams,
@@ -1219,6 +1281,7 @@ class EthereumTransactions {
1219
1281
  type: 2, // Need explicit type for hardware wallet serialization
1220
1282
  };
1221
1283
  const rawTx = (0, ethers_v6_1.serializeTransaction)(txFormattedForEthers);
1284
+ assertCurrent();
1222
1285
  const signature = await this.ledgerSigner.evm.signEVMTransaction({
1223
1286
  rawTx: rawTx.replace('0x', ''),
1224
1287
  accountIndex: activeAccountId,
@@ -1230,7 +1293,9 @@ class EthereumTransactions {
1230
1293
  };
1231
1294
  if (signature) {
1232
1295
  const signedTx = (0, ethers_v6_1.serializeTransaction)(txFormattedForEthers, formattedSignature);
1233
- const transactionResponse = await this.web3Provider.sendTransaction(signedTx);
1296
+ assertSignedSender(signedTx);
1297
+ assertBeforeBroadcast();
1298
+ const transactionResponse = await provider.sendTransaction(signedTx);
1234
1299
  return {
1235
1300
  isSpeedUp: true,
1236
1301
  transaction: transactionResponse,
@@ -1276,7 +1341,7 @@ class EthereumTransactions {
1276
1341
  : this.toHex0x(formattedTx.value, 'value'),
1277
1342
  nonce: this.toHex0x(formattedTx.nonce, 'nonce'),
1278
1343
  chainId: activeNetwork.chainId,
1279
- type: isLegacy ? 0 : 2, // Need explicit type for hardware wallet serialization
1344
+ type: isLegacy ? txWithEditedFee.type : 2,
1280
1345
  };
1281
1346
  if (formattedTx.data && formattedTx.data !== '0x') {
1282
1347
  txFormattedForTrezor.data = formattedTx.data;
@@ -1297,6 +1362,7 @@ class EthereumTransactions {
1297
1362
  ? formattedTx.maxPriorityFeePerGas
1298
1363
  : this.toHex0x(formattedTx.maxPriorityFeePerGas, 'maxPriorityFeePerGas');
1299
1364
  }
1365
+ assertCurrent();
1300
1366
  const signature = await this.trezorSigner.signEthTransaction({
1301
1367
  coin: trezorCoin,
1302
1368
  tx: txFormattedForTrezor,
@@ -1305,7 +1371,9 @@ class EthereumTransactions {
1305
1371
  });
1306
1372
  if (signature.success) {
1307
1373
  const signedTx = (0, ethers_v6_1.serializeTransaction)(txFormattedForTrezor, signature.payload);
1308
- const transactionResponse = await this.web3Provider.sendTransaction(signedTx);
1374
+ assertSignedSender(signedTx);
1375
+ assertBeforeBroadcast();
1376
+ const transactionResponse = await provider.sendTransaction(signedTx);
1309
1377
  return {
1310
1378
  isSpeedUp: true,
1311
1379
  transaction: transactionResponse,
@@ -1339,8 +1407,13 @@ class EthereumTransactions {
1339
1407
  // Regular wallet speedup handler
1340
1408
  const speedUpWithPrivateKey = async () => {
1341
1409
  try {
1342
- const { decryptedPrivateKey } = this.getDecryptedPrivateKey();
1343
- const transactionResponse = await (0, evm_local_signer_1.sendLocalEvmTransaction)(this.web3Provider, decryptedPrivateKey, txWithEditedFee);
1410
+ assertCurrent();
1411
+ const { address, decryptedPrivateKey } = this.getDecryptedPrivateKey(targetAccount);
1412
+ assertCurrent();
1413
+ if (address.toLowerCase() !== expectedSender) {
1414
+ throw new Error('Replacement signing account changed');
1415
+ }
1416
+ const transactionResponse = await (0, evm_local_signer_1.sendLocalEvmTransaction)(provider, decryptedPrivateKey, txWithEditedFee, assertBeforeBroadcast, assertCurrent);
1344
1417
  if (transactionResponse) {
1345
1418
  return {
1346
1419
  isSpeedUp: true,
@@ -2059,6 +2132,10 @@ class EthereumTransactions {
2059
2132
  return withBuffer;
2060
2133
  }
2061
2134
  catch (error) {
2135
+ if (error?.code === 'SYSWEB3_RPC_RESPONSE_INVALID' ||
2136
+ error?.name === 'AbortError' ||
2137
+ this.abortController.signal.aborted)
2138
+ throw error;
2062
2139
  console.warn('Gas estimation failed:', error);
2063
2140
  if (hasCalldata) {
2064
2141
  const calldataFallback = ethers_v6_1.BigNumber.from('100000');
@@ -2083,6 +2160,11 @@ class EthereumTransactions {
2083
2160
  return withBuffer;
2084
2161
  }
2085
2162
  catch (secondError) {
2163
+ if (secondError?.code === 'SYSWEB3_RPC_RESPONSE_INVALID' ||
2164
+ secondError?.name === 'AbortError' ||
2165
+ this.abortController.signal.aborted) {
2166
+ throw secondError;
2167
+ }
2086
2168
  // Ultimate fallback
2087
2169
  console.warn('Simple estimation also failed, using default', secondError);
2088
2170
  return this.gasOverrides.minGasLimit || ethers_v6_1.BigNumber.from('100000');
@@ -2156,6 +2238,10 @@ class EthereumTransactions {
2156
2238
  };
2157
2239
  }
2158
2240
  catch (error) {
2241
+ if (error?.code === 'SYSWEB3_RPC_RESPONSE_INVALID' ||
2242
+ error?.name === 'AbortError' ||
2243
+ this.abortController.signal.aborted)
2244
+ throw error;
2159
2245
  console.warn('Failed to get EIP-1559 fees, using legacy:', error);
2160
2246
  const gasPrice = await this.web3Provider.getGasPrice();
2161
2247
  const bufferedGasPrice = gasPrice.mul(110).div(100);