@sidhujag/sysweb3-keyring 1.0.615 → 1.0.617
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 +7 -0
- package/cjs/providers.js +282 -136
- package/cjs/providers.js.map +1 -1
- package/cjs/transactions/ethereum.js +195 -173
- package/cjs/transactions/ethereum.js.map +1 -1
- package/package.json +1 -1
- package/types/providers.d.ts +10 -1
- package/types/transactions/ethereum.d.ts +7 -0
|
@@ -16,6 +16,55 @@ const evm_local_signer_1 = require("./evm-local-signer");
|
|
|
16
16
|
const derivation_paths_1 = require("../utils/derivation-paths");
|
|
17
17
|
const stripHexPrefix = (value) => value.startsWith('0x') || value.startsWith('0X') ? value.slice(2) : value;
|
|
18
18
|
const normalizeEthersOverrideValue = (value) => (0, ethers_v6_1.normalizeTxValue)(value);
|
|
19
|
+
const nonNegativeFeeQuantity = (value, label) => {
|
|
20
|
+
// Match the exported BigNumberish object contract without trusting a
|
|
21
|
+
// private marker or BigNumber.from's null/empty-to-zero coercions. Normalize
|
|
22
|
+
// once so validation and conversion use the same integer representation.
|
|
23
|
+
if (value !== null && typeof value === 'object') {
|
|
24
|
+
const objectValue = value;
|
|
25
|
+
value =
|
|
26
|
+
objectValue._hex ??
|
|
27
|
+
objectValue.hex ??
|
|
28
|
+
(typeof objectValue.toString === 'function'
|
|
29
|
+
? objectValue.toString()
|
|
30
|
+
: undefined);
|
|
31
|
+
if (typeof value !== 'string')
|
|
32
|
+
throw new Error(`Invalid ${label}`);
|
|
33
|
+
}
|
|
34
|
+
if (!((typeof value === 'string' && /^(?:0x[\da-f]+|\d+)$/i.test(value)) ||
|
|
35
|
+
(typeof value === 'number' &&
|
|
36
|
+
Number.isSafeInteger(value) &&
|
|
37
|
+
value >= 0) ||
|
|
38
|
+
(typeof value === 'bigint' && value >= 0n)))
|
|
39
|
+
throw new Error(`Invalid ${label}`);
|
|
40
|
+
const quantity = ethers_v6_1.BigNumber.from(value);
|
|
41
|
+
if (quantity.isNegative())
|
|
42
|
+
throw new Error(`Invalid ${label}`);
|
|
43
|
+
return quantity;
|
|
44
|
+
};
|
|
45
|
+
const isUnsupportedPriorityFeeRpc = (error) => {
|
|
46
|
+
const unsupportedCode = (code) => code === -32601 || code === 4200;
|
|
47
|
+
if (unsupportedCode(error?.code))
|
|
48
|
+
return true;
|
|
49
|
+
// Ethers' HTTP normalizer retains the matched JSON-RPC error and request
|
|
50
|
+
// under one of these two shapes. A generic outer unsupported/network error
|
|
51
|
+
// is not evidence that this particular RPC method is unavailable.
|
|
52
|
+
if (!['UNKNOWN_ERROR', 'UNSUPPORTED_OPERATION'].includes(error?.code)) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
if (error?.operation != null &&
|
|
56
|
+
error.operation !== 'eth_maxPriorityFeePerGas') {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
return [
|
|
60
|
+
{ cause: error.error, payload: error.payload },
|
|
61
|
+
{ cause: error.info?.error, payload: error.info?.payload },
|
|
62
|
+
].some(({ cause, payload }) => cause?.name !== 'AbortError' &&
|
|
63
|
+
unsupportedCode(cause?.code) &&
|
|
64
|
+
payload?.method === 'eth_maxPriorityFeePerGas' &&
|
|
65
|
+
Array.isArray(payload.params) &&
|
|
66
|
+
payload.params.length === 0);
|
|
67
|
+
};
|
|
19
68
|
const normalizeEthersOverrides = (overrides) => {
|
|
20
69
|
const normalized = { ...overrides };
|
|
21
70
|
for (const field of [
|
|
@@ -429,119 +478,68 @@ class EthereumTransactions {
|
|
|
429
478
|
}
|
|
430
479
|
};
|
|
431
480
|
this.getFeeDataWithDynamicMaxPriorityFeePerGas = async (provider = this.web3Provider) => {
|
|
432
|
-
|
|
433
|
-
|
|
481
|
+
// Keep the actual legacy-price recommendation separate from the buffered
|
|
482
|
+
// EIP-1559 maximum. A failed read is not evidence of a zero/legacy fee.
|
|
483
|
+
const gasPrice = nonNegativeFeeQuantity(await provider.getGasPrice(), 'gas price');
|
|
484
|
+
const block = await provider.getBlock('latest');
|
|
485
|
+
if (!block)
|
|
486
|
+
throw new Error('Block not found');
|
|
487
|
+
if (block.baseFeePerGas == null) {
|
|
488
|
+
return {
|
|
489
|
+
gasPrice,
|
|
490
|
+
maxFeePerGas: ethers_v6_1.BigNumber.from(0),
|
|
491
|
+
maxPriorityFeePerGas: ethers_v6_1.BigNumber.from(0),
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
const baseFeePerGas = nonNegativeFeeQuantity(block.baseFeePerGas, 'base fee');
|
|
495
|
+
let maxPriorityFeePerGas;
|
|
434
496
|
try {
|
|
435
|
-
|
|
436
|
-
const currentGasPrice = await provider.getGasPrice();
|
|
437
|
-
const block = await provider.getBlock('latest');
|
|
438
|
-
if (block && block.baseFeePerGas != null) {
|
|
439
|
-
const baseFeePerGas = ethers_v6_1.BigNumber.from(block.baseFeePerGas);
|
|
440
|
-
try {
|
|
441
|
-
// Some networks don't support this RPC method
|
|
442
|
-
const ethMaxPriorityFee = await provider.send('eth_maxPriorityFeePerGas', []);
|
|
443
|
-
maxPriorityFeePerGas = ethers_v6_1.BigNumber.from(ethMaxPriorityFee);
|
|
444
|
-
// Apply minimum priority fee override if set
|
|
445
|
-
if (this.gasOverrides.minPriorityFee &&
|
|
446
|
-
maxPriorityFeePerGas.lt(this.gasOverrides.minPriorityFee)) {
|
|
447
|
-
maxPriorityFeePerGas = this.gasOverrides.minPriorityFee;
|
|
448
|
-
}
|
|
449
|
-
if (maxPriorityFeePerGas.isZero()) {
|
|
450
|
-
throw new Error('Max priority fee is zero');
|
|
451
|
-
}
|
|
452
|
-
// For networks with validation issues, use current gas price as baseline
|
|
453
|
-
// This ensures we're not setting fees that the network will reject
|
|
454
|
-
const baselineMaxFee = currentGasPrice.mul(120).div(100); // 20% above current gas price
|
|
455
|
-
// Calculate standard maxFeePerGas
|
|
456
|
-
const multiplier = this.gasOverrides.feeMultiplier || 250;
|
|
457
|
-
const calculatedMaxFee = baseFeePerGas
|
|
458
|
-
.mul(multiplier)
|
|
459
|
-
.div(100)
|
|
460
|
-
.add(maxPriorityFeePerGas);
|
|
461
|
-
// Use the higher of the two to ensure transaction goes through
|
|
462
|
-
maxFeePerGas = calculatedMaxFee.gt(baselineMaxFee)
|
|
463
|
-
? calculatedMaxFee
|
|
464
|
-
: baselineMaxFee;
|
|
465
|
-
}
|
|
466
|
-
catch (e) {
|
|
467
|
-
// Use a more aggressive fallback strategy with higher priority fees
|
|
468
|
-
// Check if we can get fee history for better estimation
|
|
469
|
-
try {
|
|
470
|
-
const feeHistory = await provider.send('eth_feeHistory', [
|
|
471
|
-
'0x5', // Last 5 blocks
|
|
472
|
-
'latest',
|
|
473
|
-
[25, 50, 75], // Percentiles for priority fees
|
|
474
|
-
]);
|
|
475
|
-
if (feeHistory &&
|
|
476
|
-
feeHistory.reward &&
|
|
477
|
-
feeHistory.reward.length > 0) {
|
|
478
|
-
// Use median of the 50th percentile from recent blocks
|
|
479
|
-
const recentFees = feeHistory.reward
|
|
480
|
-
.map((r) => r[1]) // Get 50th percentile
|
|
481
|
-
.filter((f) => f && f !== '0x0')
|
|
482
|
-
.map((f) => ethers_v6_1.BigNumber.from(f));
|
|
483
|
-
if (recentFees.length > 0) {
|
|
484
|
-
// Use the median value with a 50% buffer for better reliability
|
|
485
|
-
const sortedFees = recentFees.sort((a, b) => a.sub(b).isNegative() ? -1 : 1);
|
|
486
|
-
const medianFee = sortedFees[Math.floor(sortedFees.length / 2)];
|
|
487
|
-
maxPriorityFeePerGas = medianFee.mul(150).div(100); // Add 50% buffer
|
|
488
|
-
}
|
|
489
|
-
else {
|
|
490
|
-
// Fallback with higher default (5 gwei for better validation)
|
|
491
|
-
maxPriorityFeePerGas = ethers_v6_1.BigNumber.from('5000000000');
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
else {
|
|
495
|
-
// Fallback with higher default (5 gwei for better validation)
|
|
496
|
-
maxPriorityFeePerGas = ethers_v6_1.BigNumber.from('5000000000');
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
catch (feeHistoryError) {
|
|
500
|
-
// If fee history fails, use current gas price as reference
|
|
501
|
-
console.warn('Fee history not available, using gas price based estimation');
|
|
502
|
-
// Use 10% of current gas price as priority fee
|
|
503
|
-
const currentGasPrice = await provider.getGasPrice();
|
|
504
|
-
maxPriorityFeePerGas = currentGasPrice.mul(10).div(100);
|
|
505
|
-
// Ensure minimum of 1 gwei
|
|
506
|
-
const minPriority = ethers_v6_1.BigNumber.from('1000000000');
|
|
507
|
-
if (maxPriorityFeePerGas.lt(minPriority)) {
|
|
508
|
-
maxPriorityFeePerGas = minPriority;
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
// Calculate maxFeePerGas based on current network conditions
|
|
512
|
-
const currentGasPrice = await provider.getGasPrice();
|
|
513
|
-
const baselineMaxFee = currentGasPrice.mul(120).div(100);
|
|
514
|
-
const calculatedMaxFee = baseFeePerGas
|
|
515
|
-
.mul(250)
|
|
516
|
-
.div(100)
|
|
517
|
-
.add(maxPriorityFeePerGas);
|
|
518
|
-
maxFeePerGas = calculatedMaxFee.gt(baselineMaxFee)
|
|
519
|
-
? calculatedMaxFee
|
|
520
|
-
: baselineMaxFee;
|
|
521
|
-
}
|
|
522
|
-
// Ensure maxFeePerGas is at least 20% higher than maxPriorityFeePerGas
|
|
523
|
-
const minMaxFee = maxPriorityFeePerGas.mul(120).div(100);
|
|
524
|
-
if (maxFeePerGas.lt(minMaxFee)) {
|
|
525
|
-
maxFeePerGas = minMaxFee;
|
|
526
|
-
}
|
|
527
|
-
return { maxFeePerGas, maxPriorityFeePerGas };
|
|
528
|
-
}
|
|
529
|
-
else if (block && block.baseFeePerGas == null) {
|
|
530
|
-
// For non-EIP1559 chains, return zeros to indicate legacy transaction should be used
|
|
531
|
-
console.log('Chain does not support EIP1559, use legacy transactions');
|
|
532
|
-
return {
|
|
533
|
-
maxFeePerGas: ethers_v6_1.BigNumber.from(0),
|
|
534
|
-
maxPriorityFeePerGas: ethers_v6_1.BigNumber.from(0),
|
|
535
|
-
};
|
|
536
|
-
}
|
|
537
|
-
else if (!block)
|
|
538
|
-
throw new Error('Block not found');
|
|
539
|
-
return { maxFeePerGas, maxPriorityFeePerGas };
|
|
497
|
+
maxPriorityFeePerGas = nonNegativeFeeQuantity(await provider.send('eth_maxPriorityFeePerGas', []), 'priority fee');
|
|
540
498
|
}
|
|
541
499
|
catch (error) {
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
500
|
+
if (error?.code === 'SYSWEB3_RPC_RESPONSE_INVALID' ||
|
|
501
|
+
error?.name === 'AbortError' ||
|
|
502
|
+
this.abortController.signal.aborted ||
|
|
503
|
+
provider.signal?.aborted ||
|
|
504
|
+
!isUnsupportedPriorityFeeRpc(error))
|
|
505
|
+
throw error;
|
|
506
|
+
// An explicitly unsupported method may use actual observed history,
|
|
507
|
+
// including zero rewards. Missing/corrupt history never invents a tip.
|
|
508
|
+
const history = await provider.send('eth_feeHistory', [
|
|
509
|
+
'0x5',
|
|
510
|
+
'latest',
|
|
511
|
+
[25, 50, 75],
|
|
512
|
+
]);
|
|
513
|
+
if (!Array.isArray(history?.reward) || history.reward.length === 0)
|
|
514
|
+
throw new Error('Priority fee history unavailable');
|
|
515
|
+
const recentFees = history.reward
|
|
516
|
+
.map((reward) => {
|
|
517
|
+
if (!Array.isArray(reward) || reward.length !== 3)
|
|
518
|
+
throw new Error('Invalid priority fee history');
|
|
519
|
+
return nonNegativeFeeQuantity(reward[1], 'priority fee history');
|
|
520
|
+
})
|
|
521
|
+
.sort((a, b) => (a.lt(b) ? -1 : a.gt(b) ? 1 : 0));
|
|
522
|
+
maxPriorityFeePerGas = recentFees[Math.floor(recentFees.length / 2)]
|
|
523
|
+
.mul(150)
|
|
524
|
+
.div(100);
|
|
525
|
+
}
|
|
526
|
+
if (this.gasOverrides.minPriorityFee &&
|
|
527
|
+
maxPriorityFeePerGas.lt(this.gasOverrides.minPriorityFee))
|
|
528
|
+
maxPriorityFeePerGas = this.gasOverrides.minPriorityFee;
|
|
529
|
+
const baselineMaxFee = gasPrice.mul(120).div(100);
|
|
530
|
+
const calculatedMaxFee = baseFeePerGas
|
|
531
|
+
.mul(this.gasOverrides.feeMultiplier || 250)
|
|
532
|
+
.div(100)
|
|
533
|
+
.add(maxPriorityFeePerGas);
|
|
534
|
+
let maxFeePerGas = calculatedMaxFee.gt(baselineMaxFee)
|
|
535
|
+
? calculatedMaxFee
|
|
536
|
+
: baselineMaxFee;
|
|
537
|
+
const minMaxFee = maxPriorityFeePerGas.mul(120).div(100);
|
|
538
|
+
if (maxFeePerGas.lt(minMaxFee))
|
|
539
|
+
maxFeePerGas = minMaxFee;
|
|
540
|
+
if (maxFeePerGas.isZero())
|
|
541
|
+
throw new Error('Invalid EIP-1559 maximum fee');
|
|
542
|
+
return { gasPrice, baseFeePerGas, maxFeePerGas, maxPriorityFeePerGas };
|
|
545
543
|
};
|
|
546
544
|
this.calculateNewGasValues = (oldTxsParams, isForCancel, isLegacy) => {
|
|
547
545
|
const newGasValues = {
|
|
@@ -584,13 +582,22 @@ class EthereumTransactions {
|
|
|
584
582
|
this.cancelSentTransaction = async (txHash, isLegacy, fallbackNonce) => {
|
|
585
583
|
const { activeAccountType, activeAccountId, accounts, activeNetwork } = this.getState();
|
|
586
584
|
const activeAccount = accounts[activeAccountType][activeAccountId];
|
|
585
|
+
const currentEip1559Fees = async () => {
|
|
586
|
+
const feeData = await this.getFeeDataWithDynamicMaxPriorityFeePerGas();
|
|
587
|
+
const maxFeePerGas = nonNegativeFeeQuantity(feeData.maxFeePerGas, 'cancellation maximum fee');
|
|
588
|
+
const maxPriorityFeePerGas = nonNegativeFeeQuantity(feeData.maxPriorityFeePerGas, 'cancellation priority fee');
|
|
589
|
+
if (maxFeePerGas.isZero() || maxPriorityFeePerGas.gt(maxFeePerGas)) {
|
|
590
|
+
throw new Error('Invalid cancellation EIP-1559 fees');
|
|
591
|
+
}
|
|
592
|
+
return { maxFeePerGas, maxPriorityFeePerGas };
|
|
593
|
+
};
|
|
587
594
|
let tx = (await this.web3Provider.getTransaction(txHash));
|
|
588
595
|
// If transaction not found, create a minimal tx object with current gas prices
|
|
589
596
|
// This handles cases where tx with 0 gas never made it to the mempool
|
|
590
597
|
if (!tx && fallbackNonce !== undefined) {
|
|
591
598
|
// Fetch current network gas prices for the cancellation
|
|
592
599
|
if (isLegacy) {
|
|
593
|
-
const currentGasPrice = await this.web3Provider.getGasPrice();
|
|
600
|
+
const currentGasPrice = nonNegativeFeeQuantity(await this.web3Provider.getGasPrice(), 'cancellation gas price');
|
|
594
601
|
tx = {
|
|
595
602
|
from: activeAccount.address,
|
|
596
603
|
to: activeAccount.address,
|
|
@@ -602,14 +609,14 @@ class EthereumTransactions {
|
|
|
602
609
|
};
|
|
603
610
|
}
|
|
604
611
|
else {
|
|
605
|
-
const feeData = await
|
|
612
|
+
const feeData = await currentEip1559Fees();
|
|
606
613
|
tx = {
|
|
607
614
|
from: activeAccount.address,
|
|
608
615
|
to: activeAccount.address,
|
|
609
616
|
value: ethers_v6_1.Zero,
|
|
610
617
|
nonce: fallbackNonce,
|
|
611
|
-
maxFeePerGas:
|
|
612
|
-
maxPriorityFeePerGas:
|
|
618
|
+
maxFeePerGas: feeData.maxFeePerGas,
|
|
619
|
+
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas,
|
|
613
620
|
gasLimit: ethers_v6_1.BigNumber.from(42000),
|
|
614
621
|
data: '0x',
|
|
615
622
|
};
|
|
@@ -630,7 +637,10 @@ class EthereumTransactions {
|
|
|
630
637
|
gasLimit: tx.gasLimit,
|
|
631
638
|
};
|
|
632
639
|
if (isLegacy) {
|
|
633
|
-
const currentGasPrice = await this.web3Provider.getGasPrice();
|
|
640
|
+
const currentGasPrice = nonNegativeFeeQuantity(await this.web3Provider.getGasPrice(), 'cancellation gas price');
|
|
641
|
+
if (oldTxsGasValues.gasPrice != null) {
|
|
642
|
+
oldTxsGasValues.gasPrice = nonNegativeFeeQuantity(oldTxsGasValues.gasPrice, 'original gas price');
|
|
643
|
+
}
|
|
634
644
|
if (!oldTxsGasValues.gasPrice ||
|
|
635
645
|
oldTxsGasValues.gasPrice.isZero() ||
|
|
636
646
|
oldTxsGasValues.gasPrice.lt(currentGasPrice)) {
|
|
@@ -638,13 +648,13 @@ class EthereumTransactions {
|
|
|
638
648
|
}
|
|
639
649
|
}
|
|
640
650
|
else {
|
|
641
|
-
const
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
651
|
+
const { maxFeePerGas: currentMaxFeePerGas, maxPriorityFeePerGas: currentMaxPriorityFeePerGas, } = await currentEip1559Fees();
|
|
652
|
+
if (oldTxsGasValues.maxFeePerGas != null) {
|
|
653
|
+
oldTxsGasValues.maxFeePerGas = nonNegativeFeeQuantity(oldTxsGasValues.maxFeePerGas, 'original maximum fee');
|
|
654
|
+
}
|
|
655
|
+
if (oldTxsGasValues.maxPriorityFeePerGas != null) {
|
|
656
|
+
oldTxsGasValues.maxPriorityFeePerGas = nonNegativeFeeQuantity(oldTxsGasValues.maxPriorityFeePerGas, 'original priority fee');
|
|
657
|
+
}
|
|
648
658
|
if (!oldTxsGasValues.maxFeePerGas ||
|
|
649
659
|
oldTxsGasValues.maxFeePerGas.isZero() ||
|
|
650
660
|
oldTxsGasValues.maxFeePerGas.lt(currentMaxFeePerGas)) {
|
|
@@ -658,7 +668,24 @@ class EthereumTransactions {
|
|
|
658
668
|
oldTxsGasValues.maxFeePerGas = oldTxsGasValues.maxPriorityFeePerGas;
|
|
659
669
|
}
|
|
660
670
|
}
|
|
661
|
-
|
|
671
|
+
// Preserve the existing 20% replacement policy with exact integer math.
|
|
672
|
+
// Nodes also require a strictly larger cap and tip than an old pending
|
|
673
|
+
// transaction; zero/small fees therefore need at least a one-wei bump.
|
|
674
|
+
// This is a replacement bump, not a fabricated network priority floor.
|
|
675
|
+
const bumpCancelFee = (fee) => {
|
|
676
|
+
const percentageBump = fee.mul(120).div(100);
|
|
677
|
+
const strictBump = fee.add(1);
|
|
678
|
+
return percentageBump.gt(strictBump) ? percentageBump : strictBump;
|
|
679
|
+
};
|
|
680
|
+
const newGasValues = {
|
|
681
|
+
gasLimit: ethers_v6_1.BigNumber.from(42000),
|
|
682
|
+
...(isLegacy
|
|
683
|
+
? { gasPrice: bumpCancelFee(oldTxsGasValues.gasPrice) }
|
|
684
|
+
: {
|
|
685
|
+
maxFeePerGas: bumpCancelFee(oldTxsGasValues.maxFeePerGas),
|
|
686
|
+
maxPriorityFeePerGas: bumpCancelFee(oldTxsGasValues.maxPriorityFeePerGas),
|
|
687
|
+
}),
|
|
688
|
+
};
|
|
662
689
|
// Base cancel transaction parameters (same for all wallet types)
|
|
663
690
|
const baseCancelTx = {
|
|
664
691
|
nonce: tx.nonce,
|
|
@@ -847,24 +874,24 @@ class EthereumTransactions {
|
|
|
847
874
|
this.sendFormattedTransactionInternal = async (params, isLegacy, beforeBroadcast) => {
|
|
848
875
|
const { activeAccountType, activeAccountId, accounts, activeNetwork } = this.getState();
|
|
849
876
|
const activeAccount = accounts[activeAccountType][activeAccountId];
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
877
|
+
const explicitEip1559 = params.type != null && ethers_v6_1.BigNumber.from(params.type).eq(2);
|
|
878
|
+
const requestedEip1559 = explicitEip1559 ||
|
|
879
|
+
params.maxFeePerGas != null ||
|
|
880
|
+
params.maxPriorityFeePerGas != null;
|
|
881
|
+
if (isLegacy && requestedEip1559)
|
|
882
|
+
throw new Error('EIP-1559 fees conflict with legacy pricing');
|
|
883
|
+
const validateEip1559Fees = () => {
|
|
884
|
+
const maximum = nonNegativeFeeQuantity(params.maxFeePerGas, 'maximum fee');
|
|
885
|
+
const priority = nonNegativeFeeQuantity(params.maxPriorityFeePerGas, 'priority fee');
|
|
886
|
+
if (maximum.isZero() || priority.gt(maximum))
|
|
887
|
+
throw new Error('Invalid EIP-1559 fees');
|
|
888
|
+
params.maxFeePerGas = maximum;
|
|
889
|
+
params.maxPriorityFeePerGas = priority;
|
|
890
|
+
};
|
|
891
|
+
if (!isLegacy &&
|
|
892
|
+
params.maxFeePerGas != null &&
|
|
893
|
+
params.maxPriorityFeePerGas != null)
|
|
894
|
+
validateEip1559Fees();
|
|
868
895
|
// Only apply a minimum gas limit when a network-specific override is set.
|
|
869
896
|
// Otherwise keep the user/dapp-approved gas limit unchanged.
|
|
870
897
|
if (params.gasLimit && this.gasOverrides.minGasLimit) {
|
|
@@ -891,20 +918,24 @@ class EthereumTransactions {
|
|
|
891
918
|
params.gasPrice = await this.web3Provider.getGasPrice();
|
|
892
919
|
}
|
|
893
920
|
}
|
|
894
|
-
else if (
|
|
921
|
+
else if (params.maxFeePerGas == null ||
|
|
922
|
+
params.maxPriorityFeePerGas == null) {
|
|
895
923
|
const feeData = await this.web3Provider.getFeeData();
|
|
896
|
-
params.maxFeePerGas =
|
|
897
|
-
params.maxFeePerGas || feeData.maxFeePerGas || feeData.gasPrice;
|
|
924
|
+
params.maxFeePerGas = params.maxFeePerGas ?? feeData.maxFeePerGas;
|
|
898
925
|
params.maxPriorityFeePerGas =
|
|
899
926
|
params.maxPriorityFeePerGas ?? feeData.maxPriorityFeePerGas;
|
|
900
|
-
if (
|
|
927
|
+
if (params.maxFeePerGas == null || params.maxPriorityFeePerGas == null) {
|
|
928
|
+
if (requestedEip1559)
|
|
929
|
+
throw new Error('EIP-1559 fee data unavailable');
|
|
901
930
|
isLegacy = true;
|
|
902
931
|
params.gasPrice =
|
|
903
|
-
feeData.gasPrice
|
|
932
|
+
feeData.gasPrice ?? (await this.web3Provider.getGasPrice());
|
|
904
933
|
delete params.maxFeePerGas;
|
|
905
934
|
delete params.maxPriorityFeePerGas;
|
|
906
935
|
}
|
|
907
936
|
}
|
|
937
|
+
if (!isLegacy)
|
|
938
|
+
validateEip1559Fees();
|
|
908
939
|
const sendEVMLedgerTransaction = async () => {
|
|
909
940
|
const transactionNonce = await this.getRecommendedNonce(activeAccount.address);
|
|
910
941
|
const formatParams = isLegacy
|
|
@@ -2119,6 +2150,10 @@ class EthereumTransactions {
|
|
|
2119
2150
|
return withBuffer;
|
|
2120
2151
|
}
|
|
2121
2152
|
catch (error) {
|
|
2153
|
+
if (error?.code === 'SYSWEB3_RPC_RESPONSE_INVALID' ||
|
|
2154
|
+
error?.name === 'AbortError' ||
|
|
2155
|
+
this.abortController.signal.aborted)
|
|
2156
|
+
throw error;
|
|
2122
2157
|
console.warn('Gas estimation failed:', error);
|
|
2123
2158
|
if (hasCalldata) {
|
|
2124
2159
|
const calldataFallback = ethers_v6_1.BigNumber.from('100000');
|
|
@@ -2143,6 +2178,11 @@ class EthereumTransactions {
|
|
|
2143
2178
|
return withBuffer;
|
|
2144
2179
|
}
|
|
2145
2180
|
catch (secondError) {
|
|
2181
|
+
if (secondError?.code === 'SYSWEB3_RPC_RESPONSE_INVALID' ||
|
|
2182
|
+
secondError?.name === 'AbortError' ||
|
|
2183
|
+
this.abortController.signal.aborted) {
|
|
2184
|
+
throw secondError;
|
|
2185
|
+
}
|
|
2146
2186
|
// Ultimate fallback
|
|
2147
2187
|
console.warn('Simple estimation also failed, using default', secondError);
|
|
2148
2188
|
return this.gasOverrides.minGasLimit || ethers_v6_1.BigNumber.from('100000');
|
|
@@ -2192,39 +2232,21 @@ class EthereumTransactions {
|
|
|
2192
2232
|
maxPriorityFeePerGas: undefined,
|
|
2193
2233
|
};
|
|
2194
2234
|
}
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
if (!feeData.maxFeePerGas ||
|
|
2200
|
-
feeData.maxFeePerGas.isZero() ||
|
|
2201
|
-
!feeData.maxPriorityFeePerGas ||
|
|
2202
|
-
feeData.maxPriorityFeePerGas.isZero()) {
|
|
2203
|
-
console.log('EIP-1559 fees invalid, falling back to legacy gas price');
|
|
2204
|
-
const gasPrice = await this.web3Provider.getGasPrice();
|
|
2205
|
-
const bufferedGasPrice = gasPrice.mul(110).div(100);
|
|
2206
|
-
return {
|
|
2207
|
-
gasPrice: bufferedGasPrice,
|
|
2208
|
-
maxFeePerGas: undefined,
|
|
2209
|
-
maxPriorityFeePerGas: undefined,
|
|
2210
|
-
};
|
|
2211
|
-
}
|
|
2212
|
-
return {
|
|
2213
|
-
gasPrice: undefined,
|
|
2214
|
-
maxFeePerGas: feeData.maxFeePerGas,
|
|
2215
|
-
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas,
|
|
2216
|
-
};
|
|
2217
|
-
}
|
|
2218
|
-
catch (error) {
|
|
2219
|
-
console.warn('Failed to get EIP-1559 fees, using legacy:', error);
|
|
2220
|
-
const gasPrice = await this.web3Provider.getGasPrice();
|
|
2221
|
-
const bufferedGasPrice = gasPrice.mul(110).div(100);
|
|
2235
|
+
const feeData = await this.getFeeDataWithDynamicMaxPriorityFeePerGas();
|
|
2236
|
+
if (feeData.baseFeePerGas === undefined) {
|
|
2237
|
+
// Only an actual pre-London header authorizes legacy selection.
|
|
2238
|
+
const bufferedGasPrice = feeData.gasPrice.mul(110).div(100);
|
|
2222
2239
|
return {
|
|
2223
2240
|
gasPrice: bufferedGasPrice,
|
|
2224
2241
|
maxFeePerGas: undefined,
|
|
2225
2242
|
maxPriorityFeePerGas: undefined,
|
|
2226
2243
|
};
|
|
2227
2244
|
}
|
|
2245
|
+
return {
|
|
2246
|
+
gasPrice: undefined,
|
|
2247
|
+
maxFeePerGas: feeData.maxFeePerGas,
|
|
2248
|
+
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas,
|
|
2249
|
+
};
|
|
2228
2250
|
};
|
|
2229
2251
|
this.getNetwork = getNetwork;
|
|
2230
2252
|
this.getDecryptedPrivateKey = getDecryptedPrivateKey;
|