@sidhujag/sysweb3-keyring 2.1.0 → 2.1.1

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.
Files changed (53) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +46 -108
  3. package/cjs/evm-fees.js +223 -0
  4. package/cjs/evm-fees.js.map +1 -0
  5. package/cjs/keyring-core.js +1 -227
  6. package/cjs/keyring-core.js.map +1 -1
  7. package/cjs/keyring-manager.js +4 -3
  8. package/cjs/keyring-manager.js.map +1 -1
  9. package/cjs/software.js +12 -11
  10. package/cjs/software.js.map +1 -1
  11. package/cjs/transactions/ethereum.js +194 -190
  12. package/cjs/transactions/ethereum.js.map +1 -1
  13. package/cjs/transactions/evm-local-signer.js +28 -7
  14. package/cjs/transactions/evm-local-signer.js.map +1 -1
  15. package/cjs/transactions/evm-submission.js +179 -0
  16. package/cjs/transactions/evm-submission.js.map +1 -0
  17. package/cjs/transactions/index.js +1 -0
  18. package/cjs/transactions/index.js.map +1 -1
  19. package/cjs/transactions/prepared-evm.js +2 -0
  20. package/cjs/transactions/prepared-evm.js.map +1 -1
  21. package/cjs/types.js.map +1 -1
  22. package/cjs/utils/derivation-paths.js +4 -3
  23. package/cjs/utils/derivation-paths.js.map +1 -1
  24. package/cjs/utils/watch-descriptor.js +3 -2
  25. package/cjs/utils/watch-descriptor.js.map +1 -1
  26. package/esm/types.js.map +1 -1
  27. package/esm/utils/derivation-paths.js +2 -1
  28. package/esm/utils/derivation-paths.js.map +1 -1
  29. package/package.json +24 -11
  30. package/types/evm-fees.d.ts +42 -0
  31. package/types/keyring-core.d.ts +3 -8
  32. package/types/keyring-manager.d.ts +8 -3
  33. package/types/software.d.ts +6 -11
  34. package/types/transactions/ethereum.d.ts +10 -8
  35. package/types/transactions/evm-local-signer.d.ts +4 -1
  36. package/types/transactions/evm-submission.d.ts +48 -0
  37. package/types/transactions/index.d.ts +1 -0
  38. package/types/types.d.ts +4 -0
  39. package/cjs/bip84-native.js +0 -8
  40. package/cjs/bip84-native.js.map +0 -1
  41. package/cjs/public-bip84.js +0 -157
  42. package/cjs/public-bip84.js.map +0 -1
  43. package/cjs/software-utxo-account.js +0 -12
  44. package/cjs/software-utxo-account.js.map +0 -1
  45. package/cjs/transactions/bip84-native-plan.js +0 -481
  46. package/cjs/transactions/bip84-native-plan.js.map +0 -1
  47. package/cjs/transactions/prepared-bip84.js +0 -214
  48. package/cjs/transactions/prepared-bip84.js.map +0 -1
  49. package/types/bip84-native.d.ts +0 -3
  50. package/types/public-bip84.d.ts +0 -39
  51. package/types/software-utxo-account.d.ts +0 -13
  52. package/types/transactions/bip84-native-plan.d.ts +0 -83
  53. package/types/transactions/prepared-bip84.d.ts +0 -27
@@ -10,9 +10,11 @@ const sysweb3_utils_1 = require("@sidhujag/sysweb3-utils");
10
10
  const transaction_1 = require("ethers/transaction");
11
11
  const omit_1 = __importDefault(require("lodash/omit"));
12
12
  const ethers_v6_1 = require("../ethers-v6");
13
+ const evm_fees_1 = require("../evm-fees");
13
14
  const providers_1 = require("../providers");
14
15
  const types_1 = require("../types");
15
16
  const evm_local_signer_1 = require("./evm-local-signer");
17
+ const evm_submission_1 = require("./evm-submission");
16
18
  const derivation_paths_1 = require("../utils/derivation-paths");
17
19
  const hardwareSignerUnavailable = (hardware) => Object.assign(new Error(`${hardware} signer is unavailable in this host`), {
18
20
  code: 'HARDWARE_SIGNER_UNAVAILABLE',
@@ -25,7 +27,11 @@ const assertHardwareTransactionSender = (signedTx, expectedAddress) => {
25
27
  });
26
28
  }
27
29
  };
28
- const stripHexPrefix = (value) => value.startsWith('0x') || value.startsWith('0X') ? value.slice(2) : value;
30
+ const stripHexPrefix = (value) => {
31
+ return value.startsWith('0x') || value.startsWith('0X')
32
+ ? value.slice(2)
33
+ : value;
34
+ };
29
35
  // Connect returns hexadecimal signature fields; normalize their encoding
30
36
  // without mutating the response or reparsing an already numeric recovery v.
31
37
  const trezorSignature = (signature) => {
@@ -47,55 +53,14 @@ const trezorSignature = (signature) => {
47
53
  };
48
54
  };
49
55
  const normalizeEthersOverrideValue = (value) => (0, ethers_v6_1.normalizeTxValue)(value);
50
- const nonNegativeFeeQuantity = (value, label) => {
51
- // Match the exported BigNumberish object contract without trusting a
52
- // private marker or BigNumber.from's null/empty-to-zero coercions. Normalize
53
- // once so validation and conversion use the same integer representation.
54
- if (value !== null && typeof value === 'object') {
55
- const objectValue = value;
56
- value =
57
- objectValue._hex ??
58
- objectValue.hex ??
59
- (typeof objectValue.toString === 'function'
60
- ? objectValue.toString()
61
- : undefined);
62
- if (typeof value !== 'string')
63
- throw new Error(`Invalid ${label}`);
64
- }
65
- if (!((typeof value === 'string' && /^(?:0x[\da-f]+|\d+)$/i.test(value)) ||
66
- (typeof value === 'number' &&
67
- Number.isSafeInteger(value) &&
68
- value >= 0) ||
69
- (typeof value === 'bigint' && value >= 0n)))
70
- throw new Error(`Invalid ${label}`);
71
- const quantity = ethers_v6_1.BigNumber.from(value);
72
- if (quantity.isNegative())
73
- throw new Error(`Invalid ${label}`);
74
- return quantity;
75
- };
76
- const isUnsupportedPriorityFeeRpc = (error) => {
77
- const unsupportedCode = (code) => code === -32601 || code === 4200;
78
- if (unsupportedCode(error?.code))
79
- return true;
80
- // Ethers' HTTP normalizer retains the matched JSON-RPC error and request
81
- // under one of these two shapes. A generic outer unsupported/network error
82
- // is not evidence that this particular RPC method is unavailable.
83
- if (!['UNKNOWN_ERROR', 'UNSUPPORTED_OPERATION'].includes(error?.code)) {
84
- return false;
85
- }
86
- if (error?.operation != null &&
87
- error.operation !== 'eth_maxPriorityFeePerGas') {
88
- return false;
89
- }
90
- return [
91
- { cause: error.error, payload: error.payload },
92
- { cause: error.info?.error, payload: error.info?.payload },
93
- ].some(({ cause, payload }) => cause?.name !== 'AbortError' &&
94
- unsupportedCode(cause?.code) &&
95
- payload?.method === 'eth_maxPriorityFeePerGas' &&
96
- Array.isArray(payload.params) &&
97
- payload.params.length === 0);
56
+ // The same protocol bound applies to software and external-device envelopes.
57
+ // Keep this at serialization as well as before handing a request to a device.
58
+ const assertTransactionGasLimit = (transaction) => (0, evm_fees_1.assertEvmGasLimit)((0, evm_fees_1.parseEvmFeeQuantity)(transaction.gasLimit, 'gas limit'), (0, evm_fees_1.parseEvmFeeQuantity)(transaction.chainId ?? 0, 'chain ID'));
59
+ const serializeTransaction = (...args) => {
60
+ const snapshot = (0, evm_local_signer_1.normalizeGasLimitedTransaction)(args[0]);
61
+ return (0, ethers_v6_1.serializeTransaction)(snapshot, args[1]);
98
62
  };
63
+ const nonNegativeFeeQuantity = (value, label) => ethers_v6_1.BigNumber.from((0, evm_fees_1.parseEvmFeeQuantity)(value, label));
99
64
  const normalizeEthersOverrides = (overrides) => {
100
65
  const normalized = { ...overrides };
101
66
  for (const field of [
@@ -159,7 +124,7 @@ const assertSynchronousOriginalValidation = (result, message = 'Original transac
159
124
  }
160
125
  };
161
126
  class EthereumTransactions {
162
- constructor(getNetwork, getDecryptedPrivateKey, getState, ledgerSigner, trezorSigner, captureSigningContext) {
127
+ constructor(getNetwork, getDecryptedPrivateKey, getState, ledgerSigner, trezorSigner, captureSigningContext, captureSubmissionLifecycle) {
163
128
  // Allow manual override of gas parameters when automatic estimation fails
164
129
  this.gasOverrides = {};
165
130
  this.requireLedgerSigner = () => {
@@ -182,22 +147,29 @@ class EthereumTransactions {
182
147
  if (type === types_1.KeyringAccountType.Trezor || account?.isTrezorWallet)
183
148
  this.requireTrezorSigner();
184
149
  };
185
- this.captureEvmSigningContext = () => {
186
- this.assertHardwareSignerAvailable();
187
- const { activeAccountType, activeAccountId, accounts, activeNetwork } = this.getState();
188
- const selectedAccount = accounts[activeAccountType]?.[activeAccountId];
189
- if (!selectedAccount)
150
+ this.captureEvmSigningContext = (target) => {
151
+ this.assertHardwareSignerAvailable(target);
152
+ const state = this.getState();
153
+ const selectedType = state.activeAccountType;
154
+ const selectedId = state.activeAccountId;
155
+ const selectedAddress = state.accounts[selectedType]?.[selectedId]?.address;
156
+ const activeAccountType = target?.type ?? selectedType;
157
+ const activeAccountId = target?.id ?? selectedId;
158
+ const selectedAccount = state.accounts[activeAccountType]?.[activeAccountId];
159
+ if (!selectedAccount || !selectedAddress)
190
160
  throw new Error('Active account not found');
191
161
  const account = { address: selectedAccount.address };
192
- const network = { ...activeNetwork };
162
+ const network = { ...state.activeNetwork };
193
163
  const assertSession = this.captureSigningContext?.();
194
164
  const provider = this.web3Provider;
195
165
  const assertCurrent = () => {
196
166
  assertSession?.();
197
167
  const current = this.getState();
198
- const currentAccount = current.accounts[current.activeAccountType]?.[current.activeAccountId];
199
- if (current.activeAccountId !== activeAccountId ||
200
- current.activeAccountType !== activeAccountType ||
168
+ const currentAccount = current.accounts[activeAccountType]?.[activeAccountId];
169
+ if (current.activeAccountId !== selectedId ||
170
+ current.activeAccountType !== selectedType ||
171
+ current.accounts[selectedType]?.[selectedId]?.address?.toLowerCase() !==
172
+ selectedAddress.toLowerCase() ||
201
173
  currentAccount?.address?.toLowerCase() !==
202
174
  account.address.toLowerCase() ||
203
175
  [current.activeNetwork, this.getNetwork()].some((selected) => selected.kind !== network.kind ||
@@ -217,6 +189,17 @@ class EthereumTransactions {
217
189
  assertCurrent,
218
190
  };
219
191
  };
192
+ this.captureSubmission = (operation, account, type, id, network, assertCurrent, originalHash) => (0, evm_submission_1.captureEvmSubmission)({
193
+ operation,
194
+ account: { address: account.address, type, id },
195
+ network: {
196
+ kind: network.kind,
197
+ chainId: network.chainId,
198
+ slip44: network.slip44,
199
+ url: network.url,
200
+ },
201
+ ...(originalHash !== undefined ? { originalHash } : {}),
202
+ }, assertCurrent, this.captureSubmissionLifecycle);
220
203
  this.signTypedData = async (addr, typedData, version) => {
221
204
  const { address, decryptedPrivateKey } = this.getDecryptedPrivateKey();
222
205
  const { activeAccountType, accounts, activeAccountId } = this.getState();
@@ -615,68 +598,21 @@ class EthereumTransactions {
615
598
  }
616
599
  };
617
600
  this.getFeeDataWithDynamicMaxPriorityFeePerGas = async (provider = this.web3Provider) => {
618
- // Keep the actual legacy-price recommendation separate from the buffered
619
- // EIP-1559 maximum. A failed read is not evidence of a zero/legacy fee.
620
- const gasPrice = nonNegativeFeeQuantity(await provider.getGasPrice(), 'gas price');
621
- const block = await provider.getBlock('latest');
622
- if (!block)
623
- throw new Error('Block not found');
624
- if (block.baseFeePerGas == null) {
625
- return {
626
- gasPrice,
627
- maxFeePerGas: ethers_v6_1.BigNumber.from(0),
628
- maxPriorityFeePerGas: ethers_v6_1.BigNumber.from(0),
629
- };
630
- }
631
- const baseFeePerGas = nonNegativeFeeQuantity(block.baseFeePerGas, 'base fee');
632
- let maxPriorityFeePerGas;
633
- try {
634
- maxPriorityFeePerGas = nonNegativeFeeQuantity(await provider.send('eth_maxPriorityFeePerGas', []), 'priority fee');
635
- }
636
- catch (error) {
637
- if (error?.code === 'SYSWEB3_RPC_RESPONSE_INVALID' ||
638
- error?.name === 'AbortError' ||
639
- this.abortController.signal.aborted ||
640
- provider.signal?.aborted ||
641
- !isUnsupportedPriorityFeeRpc(error))
642
- throw error;
643
- // An explicitly unsupported method may use actual observed history,
644
- // including zero rewards. Missing/corrupt history never invents a tip.
645
- const history = await provider.send('eth_feeHistory', [
646
- '0x5',
647
- 'latest',
648
- [25, 50, 75],
649
- ]);
650
- if (!Array.isArray(history?.reward) || history.reward.length === 0)
651
- throw new Error('Priority fee history unavailable');
652
- const recentFees = history.reward
653
- .map((reward) => {
654
- if (!Array.isArray(reward) || reward.length !== 3)
655
- throw new Error('Invalid priority fee history');
656
- return nonNegativeFeeQuantity(reward[1], 'priority fee history');
657
- })
658
- .sort((a, b) => (a.lt(b) ? -1 : a.gt(b) ? 1 : 0));
659
- maxPriorityFeePerGas = recentFees[Math.floor(recentFees.length / 2)]
660
- .mul(150)
661
- .div(100);
662
- }
663
- if (this.gasOverrides.minPriorityFee &&
664
- maxPriorityFeePerGas.lt(this.gasOverrides.minPriorityFee))
665
- maxPriorityFeePerGas = this.gasOverrides.minPriorityFee;
666
- const baselineMaxFee = gasPrice.mul(120).div(100);
667
- const calculatedMaxFee = baseFeePerGas
668
- .mul(this.gasOverrides.feeMultiplier || 250)
669
- .div(100)
670
- .add(maxPriorityFeePerGas);
671
- let maxFeePerGas = calculatedMaxFee.gt(baselineMaxFee)
672
- ? calculatedMaxFee
673
- : baselineMaxFee;
674
- const minMaxFee = maxPriorityFeePerGas.mul(120).div(100);
675
- if (maxFeePerGas.lt(minMaxFee))
676
- maxFeePerGas = minMaxFee;
677
- if (maxFeePerGas.isZero())
678
- throw new Error('Invalid EIP-1559 maximum fee');
679
- return { gasPrice, baseFeePerGas, maxFeePerGas, maxPriorityFeePerGas };
601
+ const fee = await (0, evm_fees_1.getEvmFeeData)(provider, {
602
+ signal: this.abortController.signal,
603
+ minPriorityFee: this.gasOverrides.minPriorityFee,
604
+ feeMultiplier: this.gasOverrides.feeMultiplier,
605
+ });
606
+ return {
607
+ gasPrice: ethers_v6_1.BigNumber.from(fee.gasPrice),
608
+ ...(fee.baseFeePerGas === undefined
609
+ ? {}
610
+ : {
611
+ baseFeePerGas: ethers_v6_1.BigNumber.from(fee.baseFeePerGas),
612
+ }),
613
+ maxFeePerGas: ethers_v6_1.BigNumber.from(fee.maxFeePerGas),
614
+ maxPriorityFeePerGas: ethers_v6_1.BigNumber.from(fee.maxPriorityFeePerGas),
615
+ };
680
616
  };
681
617
  this.calculateNewGasValues = (oldTxsParams, isForCancel, isLegacy) => {
682
618
  const newGasValues = {
@@ -780,6 +716,9 @@ class EthereumTransactions {
780
716
  assertKeyringContext();
781
717
  };
782
718
  assertCurrent();
719
+ const submission = previewOnly
720
+ ? undefined
721
+ : this.captureSubmission('cancel', activeAccount, activeAccountType, activeAccountId, activeNetwork, assertCurrent, txHash);
783
722
  const currentEip1559Fees = async () => {
784
723
  const feeData = await this.getFeeDataWithDynamicMaxPriorityFeePerGas(provider);
785
724
  assertCurrent();
@@ -956,7 +895,7 @@ class EthereumTransactions {
956
895
  type: 2, // Need explicit type for hardware wallet serialization
957
896
  };
958
897
  assertApprovedEnvelope(txFormattedForEthers);
959
- const rawTx = (0, ethers_v6_1.serializeTransaction)(txFormattedForEthers);
898
+ const rawTx = serializeTransaction(txFormattedForEthers);
960
899
  const signature = await this.requireLedgerSigner().evm.signEVMTransaction({
961
900
  rawTx: rawTx.replace('0x', ''),
962
901
  accountIndex: activeAccountId,
@@ -969,10 +908,10 @@ class EthereumTransactions {
969
908
  v: parseInt(signature.v, 16),
970
909
  };
971
910
  if (signature) {
972
- const signedTx = (0, ethers_v6_1.serializeTransaction)(txFormattedForEthers, formattedSignature);
911
+ const signedTx = serializeTransaction(txFormattedForEthers, formattedSignature);
973
912
  assertCurrent();
974
913
  assertHardwareTransactionSender(signedTx, activeAccount.address);
975
- const transactionResponse = await provider.sendTransaction(signedTx);
914
+ const transactionResponse = await (0, evm_submission_1.submitSignedEvmTransaction)(signedTx, (raw) => provider.sendTransaction(raw), submission);
976
915
  return {
977
916
  isCanceled: true,
978
917
  transaction: transactionResponse,
@@ -987,6 +926,7 @@ class EthereumTransactions {
987
926
  }
988
927
  catch (error) {
989
928
  return {
929
+ ...(0, evm_submission_1.evmSubmissionFailure)(error),
990
930
  isCanceled: false,
991
931
  error: true,
992
932
  };
@@ -1026,6 +966,7 @@ class EthereumTransactions {
1026
966
  : this.toHex0x(formattedTx.maxPriorityFeePerGas, 'maxPriorityFeePerGas');
1027
967
  }
1028
968
  assertApprovedEnvelope(txFormattedForTrezor);
969
+ assertTransactionGasLimit(txFormattedForTrezor);
1029
970
  const signature = await this.requireTrezorSigner().signEthTransaction({
1030
971
  coin: trezorCoin,
1031
972
  tx: txFormattedForTrezor,
@@ -1034,10 +975,10 @@ class EthereumTransactions {
1034
975
  });
1035
976
  assertCurrent();
1036
977
  if (signature.success) {
1037
- const signedTx = (0, ethers_v6_1.serializeTransaction)(txFormattedForTrezor, trezorSignature(signature.payload));
978
+ const signedTx = serializeTransaction(txFormattedForTrezor, trezorSignature(signature.payload));
1038
979
  assertCurrent();
1039
980
  assertHardwareTransactionSender(signedTx, activeAccount.address);
1040
- const transactionResponse = await provider.sendTransaction(signedTx);
981
+ const transactionResponse = await (0, evm_submission_1.submitSignedEvmTransaction)(signedTx, (raw) => provider.sendTransaction(raw), submission);
1041
982
  return {
1042
983
  isCanceled: true,
1043
984
  transaction: transactionResponse,
@@ -1052,6 +993,7 @@ class EthereumTransactions {
1052
993
  }
1053
994
  catch (error) {
1054
995
  return {
996
+ ...(0, evm_submission_1.evmSubmissionFailure)(error),
1055
997
  isCanceled: false,
1056
998
  error: true,
1057
999
  };
@@ -1065,7 +1007,7 @@ class EthereumTransactions {
1065
1007
  if (address?.toLowerCase() !== activeAccount.address.toLowerCase()) {
1066
1008
  throw new Error('Wallet signing context changed');
1067
1009
  }
1068
- const transactionResponse = await (0, evm_local_signer_1.sendLocalEvmTransaction)(provider, decryptedPrivateKey, changedTxToCancel, assertCurrent, assertCurrent, assertApprovedEnvelope);
1010
+ const transactionResponse = await (0, evm_local_signer_1.sendLocalEvmTransaction)(provider, decryptedPrivateKey, changedTxToCancel, assertCurrent, assertCurrent, assertApprovedEnvelope, submission);
1069
1011
  if (transactionResponse) {
1070
1012
  return {
1071
1013
  isCanceled: true,
@@ -1080,6 +1022,7 @@ class EthereumTransactions {
1080
1022
  }
1081
1023
  catch (error) {
1082
1024
  return {
1025
+ ...(0, evm_submission_1.evmSubmissionFailure)(error),
1083
1026
  isCanceled: false,
1084
1027
  error: true,
1085
1028
  };
@@ -1115,11 +1058,15 @@ class EthereumTransactions {
1115
1058
  catch (error) {
1116
1059
  // A broadcast transport error may mean the node accepted the signed
1117
1060
  // transaction. Derive this marker from this invocation, never RPC data.
1118
- const submissionError = Object.assign(new Error(error instanceof Error ? error.message : String(error)), error, { transactionNotBroadcast: !broadcastAttempted });
1061
+ const submissionError = Object.assign(new Error(error instanceof Error ? error.message : String(error)), error, {
1062
+ transactionNotBroadcast: !broadcastAttempted ||
1063
+ (0, evm_submission_1.evmSubmissionFailure)(error).transactionNotBroadcast,
1064
+ });
1119
1065
  if (error instanceof Error) {
1120
1066
  submissionError.name = error.name;
1121
1067
  submissionError.stack = error.stack;
1122
1068
  }
1069
+ (0, evm_submission_1.copyEvmSubmissionFailure)(error, submissionError);
1123
1070
  throw submissionError;
1124
1071
  }
1125
1072
  };
@@ -1140,6 +1087,7 @@ class EthereumTransactions {
1140
1087
  beforeBroadcast();
1141
1088
  };
1142
1089
  assertCurrent();
1090
+ const submission = this.captureSubmission('send', activeAccount, activeAccountType, activeAccountId, activeNetwork, assertCurrent);
1143
1091
  const explicitEip1559 = params.type != null && ethers_v6_1.BigNumber.from(params.type).eq(2);
1144
1092
  const requestedEip1559 = explicitEip1559 ||
1145
1093
  params.maxFeePerGas != null ||
@@ -1180,6 +1128,10 @@ class EthereumTransactions {
1180
1128
  params.gasLimit = await provider.estimateGas(txForEstimation);
1181
1129
  assertCurrent();
1182
1130
  }
1131
+ assertTransactionGasLimit({
1132
+ gasLimit: params.gasLimit,
1133
+ chainId: activeNetwork.chainId,
1134
+ });
1183
1135
  if (isLegacy) {
1184
1136
  if (!params.gasPrice) {
1185
1137
  params.gasPrice = await provider.getGasPrice();
@@ -1225,7 +1177,7 @@ class EthereumTransactions {
1225
1177
  chainId: activeNetwork.chainId,
1226
1178
  type: 2, // Need explicit type for hardware wallet serialization
1227
1179
  };
1228
- const rawTx = (0, ethers_v6_1.serializeTransaction)(txFormattedForEthers);
1180
+ const rawTx = serializeTransaction(txFormattedForEthers);
1229
1181
  const signature = await this.requireLedgerSigner().evm.signEVMTransaction({
1230
1182
  rawTx: rawTx.replace('0x', ''),
1231
1183
  accountIndex: activeAccountId,
@@ -1239,10 +1191,9 @@ class EthereumTransactions {
1239
1191
  };
1240
1192
  if (signature) {
1241
1193
  try {
1242
- const signedTx = (0, ethers_v6_1.serializeTransaction)(txFormattedForEthers, formattedSignature);
1194
+ const signedTx = serializeTransaction(txFormattedForEthers, formattedSignature);
1243
1195
  assertHardwareTransactionSender(signedTx, activeAccount.address);
1244
- assertBeforeBroadcast();
1245
- const finalTx = await provider.sendTransaction(signedTx);
1196
+ const finalTx = await (0, evm_submission_1.submitSignedEvmTransaction)(signedTx, (raw) => provider.sendTransaction(raw), submission, assertBeforeBroadcast);
1246
1197
  return finalTx;
1247
1198
  }
1248
1199
  catch (error) {
@@ -1301,6 +1252,7 @@ class EthereumTransactions {
1301
1252
  };
1302
1253
  break;
1303
1254
  }
1255
+ assertTransactionGasLimit(txFormattedForTrezor);
1304
1256
  const signature = await this.requireTrezorSigner().signEthTransaction({
1305
1257
  index: `${activeAccountId}`,
1306
1258
  tx: txFormattedForTrezor,
@@ -1323,10 +1275,9 @@ class EthereumTransactions {
1323
1275
  chainId: activeNetwork.chainId,
1324
1276
  type: 2, // Need explicit type for hardware wallet serialization
1325
1277
  };
1326
- const signedTx = (0, ethers_v6_1.serializeTransaction)(txFormattedForEthers, trezorSignature(signature.payload));
1278
+ const signedTx = serializeTransaction(txFormattedForEthers, trezorSignature(signature.payload));
1327
1279
  assertHardwareTransactionSender(signedTx, activeAccount.address);
1328
- assertBeforeBroadcast();
1329
- const finalTx = await provider.sendTransaction(signedTx);
1280
+ const finalTx = await (0, evm_submission_1.submitSignedEvmTransaction)(signedTx, (raw) => provider.sendTransaction(raw), submission, assertBeforeBroadcast);
1330
1281
  return finalTx;
1331
1282
  }
1332
1283
  catch (error) {
@@ -1351,7 +1302,7 @@ class EthereumTransactions {
1351
1302
  ? { ...params, type: 0 } // Force Type 0 for legacy transactions
1352
1303
  : (0, omit_1.default)(params, ['gasPrice']); // Strip gasPrice for EIP-1559
1353
1304
  try {
1354
- const transaction = await (0, evm_local_signer_1.sendLocalEvmTransaction)(provider, decryptedPrivateKey, tx, assertBeforeBroadcast, assertCurrent);
1305
+ const transaction = await (0, evm_local_signer_1.sendLocalEvmTransaction)(provider, decryptedPrivateKey, tx, assertBeforeBroadcast, assertCurrent, undefined, submission);
1355
1306
  // The broadcast response already has the hash. Pending transactions
1356
1307
  // have no block number, so a block lookup can fail after a successful
1357
1308
  // broadcast and incorrectly report the send as failed.
@@ -1380,7 +1331,14 @@ class EthereumTransactions {
1380
1331
  !/^\d{1,78}$/.test(approvedMaximumFee))) {
1381
1332
  throw new Error('Replacement fee approval required');
1382
1333
  }
1383
- const assertCurrent = () => assertCurrentContext?.();
1334
+ let signing;
1335
+ const assertCurrent = () => {
1336
+ const result = assertCurrentContext?.();
1337
+ assertSynchronousOriginalValidation(result, 'Transaction context guard must be synchronous');
1338
+ if (result !== undefined)
1339
+ throw new Error('Transaction context guard must return undefined');
1340
+ signing?.assertCurrent();
1341
+ };
1384
1342
  const assertBeforeBroadcast = () => {
1385
1343
  assertCurrent();
1386
1344
  beforeBroadcast?.();
@@ -1406,10 +1364,15 @@ class EthereumTransactions {
1406
1364
  (targetAccount && activeAccountType === types_1.KeyringAccountType.SmartAccount)) {
1407
1365
  throw new Error('Replacement signing account is unavailable');
1408
1366
  }
1367
+ if (!previewOnly)
1368
+ signing = this.captureEvmSigningContext(targetAccount);
1409
1369
  const expectedSender = activeAccount.address.toLowerCase();
1410
1370
  const requestedHash = txHash.toLowerCase();
1411
1371
  const provider = this.web3Provider;
1412
1372
  assertCurrent();
1373
+ const submission = previewOnly
1374
+ ? undefined
1375
+ : this.captureSubmission('speed-up', activeAccount, activeAccountType, activeAccountId, activeNetwork, assertCurrent, txHash);
1413
1376
  const validateLookup = (transaction) => {
1414
1377
  assertCurrent();
1415
1378
  if (!transaction)
@@ -1547,7 +1510,7 @@ class EthereumTransactions {
1547
1510
  chainId: activeNetwork.chainId,
1548
1511
  type: 2, // Need explicit type for hardware wallet serialization
1549
1512
  };
1550
- const rawTx = (0, ethers_v6_1.serializeTransaction)(txFormattedForEthers);
1513
+ const rawTx = serializeTransaction(txFormattedForEthers);
1551
1514
  assertCurrent();
1552
1515
  const signature = await this.requireLedgerSigner().evm.signEVMTransaction({
1553
1516
  rawTx: rawTx.replace('0x', ''),
@@ -1560,11 +1523,10 @@ class EthereumTransactions {
1560
1523
  v: parseInt(signature.v, 16),
1561
1524
  };
1562
1525
  if (signature) {
1563
- const signedTx = (0, ethers_v6_1.serializeTransaction)(txFormattedForEthers, formattedSignature);
1526
+ const signedTx = serializeTransaction(txFormattedForEthers, formattedSignature);
1564
1527
  assertSignedSender(signedTx);
1565
1528
  assertHardwareTransactionSender(signedTx, activeAccount.address);
1566
- assertBeforeBroadcast();
1567
- const transactionResponse = await provider.sendTransaction(signedTx);
1529
+ const transactionResponse = await (0, evm_submission_1.submitSignedEvmTransaction)(signedTx, (raw) => provider.sendTransaction(raw), submission, assertBeforeBroadcast);
1568
1530
  return {
1569
1531
  isSpeedUp: true,
1570
1532
  transaction: transactionResponse,
@@ -1588,6 +1550,7 @@ class EthereumTransactions {
1588
1550
  ? 'REPLACEMENT_ALREADY_KNOWN'
1589
1551
  : 'REPLACEMENT_SEND_FAILED';
1590
1552
  return {
1553
+ ...(0, evm_submission_1.evmSubmissionFailure)(error),
1591
1554
  isSpeedUp: false,
1592
1555
  error: true,
1593
1556
  code,
@@ -1632,6 +1595,7 @@ class EthereumTransactions {
1632
1595
  : this.toHex0x(formattedTx.maxPriorityFeePerGas, 'maxPriorityFeePerGas');
1633
1596
  }
1634
1597
  assertCurrent();
1598
+ assertTransactionGasLimit(txFormattedForTrezor);
1635
1599
  const signature = await this.requireTrezorSigner().signEthTransaction({
1636
1600
  coin: trezorCoin,
1637
1601
  tx: txFormattedForTrezor,
@@ -1639,11 +1603,10 @@ class EthereumTransactions {
1639
1603
  slip44: activeNetwork.slip44,
1640
1604
  });
1641
1605
  if (signature.success) {
1642
- const signedTx = (0, ethers_v6_1.serializeTransaction)(txFormattedForTrezor, trezorSignature(signature.payload));
1606
+ const signedTx = serializeTransaction(txFormattedForTrezor, trezorSignature(signature.payload));
1643
1607
  assertSignedSender(signedTx);
1644
1608
  assertHardwareTransactionSender(signedTx, activeAccount.address);
1645
- assertBeforeBroadcast();
1646
- const transactionResponse = await provider.sendTransaction(signedTx);
1609
+ const transactionResponse = await (0, evm_submission_1.submitSignedEvmTransaction)(signedTx, (raw) => provider.sendTransaction(raw), submission, assertBeforeBroadcast);
1647
1610
  return {
1648
1611
  isSpeedUp: true,
1649
1612
  transaction: transactionResponse,
@@ -1667,6 +1630,7 @@ class EthereumTransactions {
1667
1630
  ? 'REPLACEMENT_ALREADY_KNOWN'
1668
1631
  : 'REPLACEMENT_SEND_FAILED';
1669
1632
  return {
1633
+ ...(0, evm_submission_1.evmSubmissionFailure)(error),
1670
1634
  isSpeedUp: false,
1671
1635
  error: true,
1672
1636
  code,
@@ -1683,7 +1647,7 @@ class EthereumTransactions {
1683
1647
  if (address.toLowerCase() !== expectedSender) {
1684
1648
  throw new Error('Replacement signing account changed');
1685
1649
  }
1686
- const transactionResponse = await (0, evm_local_signer_1.sendLocalEvmTransaction)(provider, decryptedPrivateKey, txWithEditedFee, assertBeforeBroadcast, assertCurrent);
1650
+ const transactionResponse = await (0, evm_local_signer_1.sendLocalEvmTransaction)(provider, decryptedPrivateKey, txWithEditedFee, assertBeforeBroadcast, assertCurrent, undefined, submission);
1687
1651
  if (transactionResponse) {
1688
1652
  return {
1689
1653
  isSpeedUp: true,
@@ -1709,6 +1673,7 @@ class EthereumTransactions {
1709
1673
  ? 'INSUFFICIENT_FUNDS_REPLACEMENT'
1710
1674
  : 'REPLACEMENT_SEND_FAILED';
1711
1675
  return {
1676
+ ...(0, evm_submission_1.evmSubmissionFailure)(error),
1712
1677
  isSpeedUp: false,
1713
1678
  error: true,
1714
1679
  code,
@@ -1729,6 +1694,7 @@ class EthereumTransactions {
1729
1694
  this.sendSignedErc20Transaction = async ({ receiver, tokenAddress, tokenAmount, isLegacy = false, maxPriorityFeePerGas, maxFeePerGas, gasPrice, decimals, gasLimit, saveTrezorTx, }) => {
1730
1695
  const { activeAccount, activeAccountType, activeAccountId, activeNetwork, provider, assertCurrent, } = this.captureEvmSigningContext();
1731
1696
  const activeAccountAddress = activeAccount.address;
1697
+ const submission = this.captureSubmission('erc20', activeAccount, activeAccountType, activeAccountId, activeNetwork, assertCurrent);
1732
1698
  ({ gasPrice, isLegacy, maxFeePerGas, maxPriorityFeePerGas } =
1733
1699
  await this.resolveEvmFeeParams({
1734
1700
  gasPrice,
@@ -1769,7 +1735,7 @@ class EthereumTransactions {
1769
1735
  chainId: activeNetwork.chainId,
1770
1736
  from: activeAccountAddress,
1771
1737
  value: '0x0',
1772
- }, assertCurrent, assertCurrent);
1738
+ }, assertCurrent, assertCurrent, undefined, submission);
1773
1739
  }
1774
1740
  catch (error) {
1775
1741
  throw error;
@@ -1815,7 +1781,7 @@ class EthereumTransactions {
1815
1781
  type: 2, // Need explicit type for hardware wallet serialization
1816
1782
  };
1817
1783
  }
1818
- const rawTx = (0, ethers_v6_1.serializeTransaction)(txFormattedForEthers);
1784
+ const rawTx = serializeTransaction(txFormattedForEthers);
1819
1785
  const signature = await this.requireLedgerSigner().evm.signEVMTransaction({
1820
1786
  rawTx: rawTx.replace('0x', ''),
1821
1787
  accountIndex: activeAccountId,
@@ -1829,11 +1795,14 @@ class EthereumTransactions {
1829
1795
  };
1830
1796
  if (signature) {
1831
1797
  try {
1832
- const signedTx = (0, ethers_v6_1.serializeTransaction)(txFormattedForEthers, formattedSignature);
1798
+ const signedTx = serializeTransaction(txFormattedForEthers, formattedSignature);
1833
1799
  assertCurrent();
1834
1800
  assertHardwareTransactionSender(signedTx, activeAccount.address);
1835
- const finalTx = await provider.sendTransaction(signedTx);
1836
- saveTrezorTx && saveTrezorTx(finalTx);
1801
+ const finalTx = await (0, evm_submission_1.submitSignedEvmTransaction)(signedTx, async (raw) => {
1802
+ const response = await provider.sendTransaction(raw);
1803
+ saveTrezorTx && saveTrezorTx(response);
1804
+ return response;
1805
+ }, submission);
1837
1806
  return finalTx;
1838
1807
  }
1839
1808
  catch (error) {
@@ -1886,6 +1855,7 @@ class EthereumTransactions {
1886
1855
  data: txData,
1887
1856
  };
1888
1857
  }
1858
+ assertTransactionGasLimit(txToBeSignedByTrezor);
1889
1859
  const signature = await this.requireTrezorSigner().signEthTransaction({
1890
1860
  index: `${activeAccountId}`,
1891
1861
  tx: txToBeSignedByTrezor,
@@ -1921,11 +1891,14 @@ class EthereumTransactions {
1921
1891
  type: 2, // Need explicit type for hardware wallet serialization
1922
1892
  };
1923
1893
  }
1924
- const signedTx = (0, ethers_v6_1.serializeTransaction)(txFormattedForEthers, trezorSignature(signature.payload));
1894
+ const signedTx = serializeTransaction(txFormattedForEthers, trezorSignature(signature.payload));
1925
1895
  assertCurrent();
1926
1896
  assertHardwareTransactionSender(signedTx, activeAccount.address);
1927
- const finalTx = await provider.sendTransaction(signedTx);
1928
- saveTrezorTx && saveTrezorTx(finalTx);
1897
+ const finalTx = await (0, evm_submission_1.submitSignedEvmTransaction)(signedTx, async (raw) => {
1898
+ const response = await provider.sendTransaction(raw);
1899
+ saveTrezorTx && saveTrezorTx(response);
1900
+ return response;
1901
+ }, submission);
1929
1902
  return finalTx;
1930
1903
  }
1931
1904
  catch (error) {
@@ -1952,6 +1925,7 @@ class EthereumTransactions {
1952
1925
  this.sendSignedErc721Transaction = async ({ receiver, tokenAddress, tokenId, isLegacy, maxPriorityFeePerGas, maxFeePerGas, gasPrice, gasLimit, }) => {
1953
1926
  const { activeAccount, activeAccountType, activeAccountId, activeNetwork, provider, assertCurrent, } = this.captureEvmSigningContext();
1954
1927
  const activeAccountAddress = activeAccount.address;
1928
+ const submission = this.captureSubmission('erc721', activeAccount, activeAccountType, activeAccountId, activeNetwork, assertCurrent);
1955
1929
  if (tokenId === undefined || tokenId === null) {
1956
1930
  throw new Error('ERC721 tokenId is required');
1957
1931
  }
@@ -1993,7 +1967,7 @@ class EthereumTransactions {
1993
1967
  chainId: activeNetwork.chainId,
1994
1968
  from: activeAccountAddress,
1995
1969
  value: '0x0',
1996
- }, assertCurrent, assertCurrent);
1970
+ }, assertCurrent, assertCurrent, undefined, submission);
1997
1971
  }
1998
1972
  catch (error) {
1999
1973
  throw error;
@@ -2038,7 +2012,7 @@ class EthereumTransactions {
2038
2012
  type: 2, // Need explicit type for hardware wallet serialization
2039
2013
  };
2040
2014
  }
2041
- const rawTx = (0, ethers_v6_1.serializeTransaction)(txFormattedForEthers);
2015
+ const rawTx = serializeTransaction(txFormattedForEthers);
2042
2016
  const signature = await this.requireLedgerSigner().evm.signEVMTransaction({
2043
2017
  rawTx: rawTx.replace('0x', ''),
2044
2018
  accountIndex: activeAccountId,
@@ -2052,10 +2026,10 @@ class EthereumTransactions {
2052
2026
  };
2053
2027
  if (signature) {
2054
2028
  try {
2055
- const signedTx = (0, ethers_v6_1.serializeTransaction)(txFormattedForEthers, formattedSignature);
2029
+ const signedTx = serializeTransaction(txFormattedForEthers, formattedSignature);
2056
2030
  assertCurrent();
2057
2031
  assertHardwareTransactionSender(signedTx, activeAccount.address);
2058
- const finalTx = await provider.sendTransaction(signedTx);
2032
+ const finalTx = await (0, evm_submission_1.submitSignedEvmTransaction)(signedTx, (raw) => provider.sendTransaction(raw), submission);
2059
2033
  return finalTx;
2060
2034
  }
2061
2035
  catch (error) {
@@ -2110,6 +2084,7 @@ class EthereumTransactions {
2110
2084
  }
2111
2085
  // For EVM networks, Trezor expects 'eth' regardless of the network's currency
2112
2086
  const trezorCoin = activeNetwork.slip44 === 60 ? 'eth' : activeNetwork.currency;
2087
+ assertTransactionGasLimit(txToBeSignedByTrezor);
2113
2088
  const signature = await this.requireTrezorSigner().signEthTransaction({
2114
2089
  index: `${activeAccountId}`,
2115
2090
  tx: txToBeSignedByTrezor,
@@ -2145,10 +2120,10 @@ class EthereumTransactions {
2145
2120
  type: 2, // Need explicit type for hardware wallet serialization
2146
2121
  };
2147
2122
  }
2148
- const signedTx = (0, ethers_v6_1.serializeTransaction)(txFormattedForEthers, trezorSignature(signature.payload));
2123
+ const signedTx = serializeTransaction(txFormattedForEthers, trezorSignature(signature.payload));
2149
2124
  assertCurrent();
2150
2125
  assertHardwareTransactionSender(signedTx, activeAccount.address);
2151
- const finalTx = await provider.sendTransaction(signedTx);
2126
+ const finalTx = await (0, evm_submission_1.submitSignedEvmTransaction)(signedTx, (raw) => provider.sendTransaction(raw), submission);
2152
2127
  return finalTx;
2153
2128
  }
2154
2129
  catch (error) {
@@ -2177,6 +2152,7 @@ class EthereumTransactions {
2177
2152
  this.sendSignedErc1155Transaction = async ({ receiver, tokenAddress, tokenId, tokenAmount, isLegacy, maxPriorityFeePerGas, maxFeePerGas, gasPrice, gasLimit, }) => {
2178
2153
  const { activeAccount, activeAccountType, activeAccountId, activeNetwork, provider, assertCurrent, } = this.captureEvmSigningContext();
2179
2154
  const activeAccountAddress = activeAccount.address;
2155
+ const submission = this.captureSubmission('erc1155', activeAccount, activeAccountType, activeAccountId, activeNetwork, assertCurrent);
2180
2156
  ({ gasPrice, isLegacy, maxFeePerGas, maxPriorityFeePerGas } =
2181
2157
  await this.resolveEvmFeeParams({
2182
2158
  gasPrice,
@@ -2216,7 +2192,7 @@ class EthereumTransactions {
2216
2192
  chainId: activeNetwork.chainId,
2217
2193
  from: activeAccountAddress,
2218
2194
  value: '0x0',
2219
- }, assertCurrent, assertCurrent);
2195
+ }, assertCurrent, assertCurrent, undefined, submission);
2220
2196
  }
2221
2197
  catch (error) {
2222
2198
  throw error;
@@ -2258,7 +2234,7 @@ class EthereumTransactions {
2258
2234
  type: 2, // Need explicit type for hardware wallet serialization
2259
2235
  };
2260
2236
  }
2261
- const rawTx = (0, ethers_v6_1.serializeTransaction)(txFormattedForEthers);
2237
+ const rawTx = serializeTransaction(txFormattedForEthers);
2262
2238
  const signature = await this.requireLedgerSigner().evm.signEVMTransaction({
2263
2239
  rawTx: rawTx.replace('0x', ''),
2264
2240
  accountIndex: activeAccountId,
@@ -2272,10 +2248,10 @@ class EthereumTransactions {
2272
2248
  };
2273
2249
  if (signature) {
2274
2250
  try {
2275
- const signedTx = (0, ethers_v6_1.serializeTransaction)(txFormattedForEthers, formattedSignature);
2251
+ const signedTx = serializeTransaction(txFormattedForEthers, formattedSignature);
2276
2252
  assertCurrent();
2277
2253
  assertHardwareTransactionSender(signedTx, activeAccount.address);
2278
- const finalTx = await provider.sendTransaction(signedTx);
2254
+ const finalTx = await (0, evm_submission_1.submitSignedEvmTransaction)(signedTx, (raw) => provider.sendTransaction(raw), submission);
2279
2255
  return finalTx;
2280
2256
  }
2281
2257
  catch (error) {
@@ -2324,6 +2300,7 @@ class EthereumTransactions {
2324
2300
  data: txData,
2325
2301
  };
2326
2302
  }
2303
+ assertTransactionGasLimit(txToBeSignedByTrezor);
2327
2304
  const signature = await this.requireTrezorSigner().signEthTransaction({
2328
2305
  index: `${activeAccountId}`,
2329
2306
  tx: txToBeSignedByTrezor,
@@ -2359,10 +2336,10 @@ class EthereumTransactions {
2359
2336
  type: 2, // Need explicit type for hardware wallet serialization
2360
2337
  };
2361
2338
  }
2362
- const signedTx = (0, ethers_v6_1.serializeTransaction)(txFormattedForEthers, trezorSignature(signature.payload));
2339
+ const signedTx = serializeTransaction(txFormattedForEthers, trezorSignature(signature.payload));
2363
2340
  assertCurrent();
2364
2341
  assertHardwareTransactionSender(signedTx, activeAccount.address);
2365
- const finalTx = await provider.sendTransaction(signedTx);
2342
+ const finalTx = await (0, evm_submission_1.submitSignedEvmTransaction)(signedTx, (raw) => provider.sendTransaction(raw), submission);
2366
2343
  return finalTx;
2367
2344
  }
2368
2345
  catch (error) {
@@ -2424,56 +2401,82 @@ class EthereumTransactions {
2424
2401
  }
2425
2402
  };
2426
2403
  this.getTxGasLimit = async (tx) => {
2427
- const hasCalldata = Boolean(tx.data && tx.data !== '0x' && tx.data !== '0x0');
2404
+ const request = { ...tx };
2405
+ const network = this.getNetwork();
2406
+ const identity = {
2407
+ kind: network.kind,
2408
+ chainId: network.chainId,
2409
+ slip44: network.slip44,
2410
+ url: network.url,
2411
+ };
2412
+ const chainId = (0, evm_fees_1.parseEvmFeeQuantity)(identity.chainId, 'chain ID');
2413
+ const provider = this.web3Provider;
2414
+ const controller = this.abortController;
2415
+ const minimum = this.gasOverrides.minGasLimit;
2416
+ const assertCurrent = () => {
2417
+ const current = this.getNetwork();
2418
+ if (controller.signal.aborted ||
2419
+ controller !== this.abortController ||
2420
+ current.kind !== identity.kind ||
2421
+ current.chainId !== identity.chainId ||
2422
+ current.slip44 !== identity.slip44 ||
2423
+ current.url !== identity.url ||
2424
+ this.web3Provider !== provider) {
2425
+ const error = new Error('EVM gas estimate context changed');
2426
+ error.name = 'AbortError';
2427
+ throw error;
2428
+ }
2429
+ };
2430
+ const checked = (limit) => {
2431
+ assertCurrent();
2432
+ return ethers_v6_1.BigNumber.from((0, evm_fees_1.assertEvmGasLimit)(typeof limit === 'bigint' ? limit : limit.toBigInt(), chainId));
2433
+ };
2434
+ const hasCalldata = Boolean(request.data && request.data !== '0x' && request.data !== '0x0');
2428
2435
  try {
2429
2436
  // First attempt: standard estimation
2430
- const estimated = await this.web3Provider.estimateGas(tx);
2431
- // Add 20% buffer to the estimated gas
2432
- const withBuffer = estimated.mul(120).div(100);
2433
- // Apply an explicit per-network floor only when configured.
2434
- if (this.gasOverrides.minGasLimit &&
2435
- withBuffer.lt(this.gasOverrides.minGasLimit)) {
2436
- return this.gasOverrides.minGasLimit;
2437
- }
2438
- return withBuffer;
2437
+ assertCurrent();
2438
+ const estimated = await provider.estimateGas(request);
2439
+ return checked((0, evm_fees_1.bufferedEvmGasLimit)(estimated.toBigInt(), minimum?.toBigInt()));
2439
2440
  }
2440
2441
  catch (error) {
2441
- if (error?.code === 'SYSWEB3_RPC_RESPONSE_INVALID' ||
2442
+ assertCurrent();
2443
+ if (error instanceof evm_fees_1.EvmGasLimitError ||
2444
+ error?.code === 'SYSWEB3_RPC_RESPONSE_INVALID' ||
2442
2445
  error?.name === 'AbortError' ||
2443
2446
  this.abortController.signal.aborted)
2444
2447
  throw error;
2445
2448
  console.warn('Gas estimation failed:', error);
2446
2449
  if (hasCalldata) {
2447
2450
  const calldataFallback = ethers_v6_1.BigNumber.from('100000');
2448
- if (this.gasOverrides.minGasLimit &&
2449
- calldataFallback.lt(this.gasOverrides.minGasLimit)) {
2450
- return this.gasOverrides.minGasLimit;
2451
+ if (minimum && calldataFallback.lt(minimum)) {
2452
+ return checked(minimum);
2451
2453
  }
2452
- return calldataFallback;
2454
+ return checked(calldataFallback);
2453
2455
  }
2454
2456
  // Try a simpler estimation for basic transfers
2455
2457
  try {
2456
- const simpleEstimate = await this.web3Provider.estimateGas({
2457
- to: tx.to,
2458
- from: tx.from,
2459
- value: tx.value || '0x0',
2458
+ const simpleEstimate = await provider.estimateGas({
2459
+ to: request.to,
2460
+ from: request.from,
2461
+ value: request.value || '0x0',
2460
2462
  });
2461
2463
  const withBuffer = simpleEstimate.mul(150).div(100); // 50% buffer for failed estimations
2462
- if (this.gasOverrides.minGasLimit &&
2463
- withBuffer.lt(this.gasOverrides.minGasLimit)) {
2464
- return this.gasOverrides.minGasLimit;
2464
+ if (minimum && withBuffer.lt(minimum)) {
2465
+ return checked(minimum);
2465
2466
  }
2466
- return withBuffer;
2467
+ return checked(withBuffer);
2467
2468
  }
2468
2469
  catch (secondError) {
2469
- if (secondError?.code === 'SYSWEB3_RPC_RESPONSE_INVALID' ||
2470
+ assertCurrent();
2471
+ if (secondError instanceof evm_fees_1.EvmGasLimitError ||
2472
+ secondError?.code === 'SYSWEB3_RPC_RESPONSE_INVALID' ||
2470
2473
  secondError?.name === 'AbortError' ||
2471
2474
  this.abortController.signal.aborted) {
2472
2475
  throw secondError;
2473
2476
  }
2474
2477
  // Ultimate fallback
2475
2478
  console.warn('Simple estimation also failed, using default', secondError);
2476
- return this.gasOverrides.minGasLimit || ethers_v6_1.BigNumber.from('100000');
2479
+ return checked(minimum || ethers_v6_1.BigNumber.from('100000'));
2477
2480
  }
2478
2481
  }
2479
2482
  };
@@ -2548,6 +2551,7 @@ class EthereumTransactions {
2548
2551
  this.trezorSigner = trezorSigner;
2549
2552
  this.ledgerSigner = ledgerSigner;
2550
2553
  this.captureSigningContext = captureSigningContext;
2554
+ this.captureSubmissionLifecycle = captureSubmissionLifecycle;
2551
2555
  }
2552
2556
  // Getter that automatically ensures providers are initialized when accessed
2553
2557
  get web3Provider() {