@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.
- package/CHANGELOG.md +61 -0
- package/cjs/hardware-wallet-manager.js +3 -1
- package/cjs/hardware-wallet-manager.js.map +1 -1
- package/cjs/keyring-manager.js +3 -2
- package/cjs/keyring-manager.js.map +1 -1
- package/cjs/providers.js +273 -121
- package/cjs/providers.js.map +1 -1
- package/cjs/transactions/ethereum.js +200 -114
- package/cjs/transactions/ethereum.js.map +1 -1
- package/cjs/transactions/evm-local-signer.js +2 -1
- package/cjs/transactions/evm-local-signer.js.map +1 -1
- package/cjs/types.js.map +1 -1
- package/package.json +1 -1
- package/types/providers.d.ts +10 -1
- package/types/transactions/ethereum.d.ts +7 -4
- package/types/transactions/evm-local-signer.d.ts +1 -1
- package/types/types.d.ts +18 -2
|
@@ -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
|
|
436
|
-
const block = await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
1088
|
-
const maxPriorityFeePerGas = await Promise.resolve(tx.
|
|
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
|
-
//
|
|
1109
|
-
//
|
|
1110
|
-
if (isLegacy) {
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
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
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
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:
|
|
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
|
-
|
|
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 ?
|
|
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
|
-
|
|
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
|
-
|
|
1343
|
-
const
|
|
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);
|