@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.
- package/CHANGELOG.md +11 -0
- package/README.md +46 -108
- package/cjs/evm-fees.js +223 -0
- package/cjs/evm-fees.js.map +1 -0
- package/cjs/keyring-core.js +1 -227
- package/cjs/keyring-core.js.map +1 -1
- package/cjs/keyring-manager.js +4 -3
- package/cjs/keyring-manager.js.map +1 -1
- package/cjs/software.js +12 -11
- package/cjs/software.js.map +1 -1
- package/cjs/transactions/ethereum.js +194 -190
- package/cjs/transactions/ethereum.js.map +1 -1
- package/cjs/transactions/evm-local-signer.js +28 -7
- package/cjs/transactions/evm-local-signer.js.map +1 -1
- package/cjs/transactions/evm-submission.js +179 -0
- package/cjs/transactions/evm-submission.js.map +1 -0
- package/cjs/transactions/index.js +1 -0
- package/cjs/transactions/index.js.map +1 -1
- package/cjs/transactions/prepared-evm.js +2 -0
- package/cjs/transactions/prepared-evm.js.map +1 -1
- package/cjs/types.js.map +1 -1
- package/cjs/utils/derivation-paths.js +4 -3
- package/cjs/utils/derivation-paths.js.map +1 -1
- package/cjs/utils/watch-descriptor.js +3 -2
- package/cjs/utils/watch-descriptor.js.map +1 -1
- package/esm/types.js.map +1 -1
- package/esm/utils/derivation-paths.js +2 -1
- package/esm/utils/derivation-paths.js.map +1 -1
- package/package.json +24 -11
- package/types/evm-fees.d.ts +42 -0
- package/types/keyring-core.d.ts +3 -8
- package/types/keyring-manager.d.ts +8 -3
- package/types/software.d.ts +6 -11
- package/types/transactions/ethereum.d.ts +10 -8
- package/types/transactions/evm-local-signer.d.ts +4 -1
- package/types/transactions/evm-submission.d.ts +48 -0
- package/types/transactions/index.d.ts +1 -0
- package/types/types.d.ts +4 -0
- package/cjs/bip84-native.js +0 -8
- package/cjs/bip84-native.js.map +0 -1
- package/cjs/public-bip84.js +0 -157
- package/cjs/public-bip84.js.map +0 -1
- package/cjs/software-utxo-account.js +0 -12
- package/cjs/software-utxo-account.js.map +0 -1
- package/cjs/transactions/bip84-native-plan.js +0 -481
- package/cjs/transactions/bip84-native-plan.js.map +0 -1
- package/cjs/transactions/prepared-bip84.js +0 -214
- package/cjs/transactions/prepared-bip84.js.map +0 -1
- package/types/bip84-native.d.ts +0 -3
- package/types/public-bip84.d.ts +0 -39
- package/types/software-utxo-account.d.ts +0 -13
- package/types/transactions/bip84-native-plan.d.ts +0 -83
- 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) =>
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
|
188
|
-
const
|
|
189
|
-
|
|
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[
|
|
199
|
-
if (current.activeAccountId !==
|
|
200
|
-
current.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
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
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 =
|
|
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 =
|
|
911
|
+
const signedTx = serializeTransaction(txFormattedForEthers, formattedSignature);
|
|
973
912
|
assertCurrent();
|
|
974
913
|
assertHardwareTransactionSender(signedTx, activeAccount.address);
|
|
975
|
-
const transactionResponse = await provider.sendTransaction(
|
|
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 =
|
|
978
|
+
const signedTx = serializeTransaction(txFormattedForTrezor, trezorSignature(signature.payload));
|
|
1038
979
|
assertCurrent();
|
|
1039
980
|
assertHardwareTransactionSender(signedTx, activeAccount.address);
|
|
1040
|
-
const transactionResponse = await provider.sendTransaction(
|
|
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, {
|
|
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 =
|
|
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 =
|
|
1194
|
+
const signedTx = serializeTransaction(txFormattedForEthers, formattedSignature);
|
|
1243
1195
|
assertHardwareTransactionSender(signedTx, activeAccount.address);
|
|
1244
|
-
|
|
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 =
|
|
1278
|
+
const signedTx = serializeTransaction(txFormattedForEthers, trezorSignature(signature.payload));
|
|
1327
1279
|
assertHardwareTransactionSender(signedTx, activeAccount.address);
|
|
1328
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
1526
|
+
const signedTx = serializeTransaction(txFormattedForEthers, formattedSignature);
|
|
1564
1527
|
assertSignedSender(signedTx);
|
|
1565
1528
|
assertHardwareTransactionSender(signedTx, activeAccount.address);
|
|
1566
|
-
|
|
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 =
|
|
1606
|
+
const signedTx = serializeTransaction(txFormattedForTrezor, trezorSignature(signature.payload));
|
|
1643
1607
|
assertSignedSender(signedTx);
|
|
1644
1608
|
assertHardwareTransactionSender(signedTx, activeAccount.address);
|
|
1645
|
-
|
|
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 =
|
|
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 =
|
|
1798
|
+
const signedTx = serializeTransaction(txFormattedForEthers, formattedSignature);
|
|
1833
1799
|
assertCurrent();
|
|
1834
1800
|
assertHardwareTransactionSender(signedTx, activeAccount.address);
|
|
1835
|
-
const finalTx = await
|
|
1836
|
-
|
|
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 =
|
|
1894
|
+
const signedTx = serializeTransaction(txFormattedForEthers, trezorSignature(signature.payload));
|
|
1925
1895
|
assertCurrent();
|
|
1926
1896
|
assertHardwareTransactionSender(signedTx, activeAccount.address);
|
|
1927
|
-
const finalTx = await
|
|
1928
|
-
|
|
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 =
|
|
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 =
|
|
2029
|
+
const signedTx = serializeTransaction(txFormattedForEthers, formattedSignature);
|
|
2056
2030
|
assertCurrent();
|
|
2057
2031
|
assertHardwareTransactionSender(signedTx, activeAccount.address);
|
|
2058
|
-
const finalTx = await provider.sendTransaction(
|
|
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 =
|
|
2123
|
+
const signedTx = serializeTransaction(txFormattedForEthers, trezorSignature(signature.payload));
|
|
2149
2124
|
assertCurrent();
|
|
2150
2125
|
assertHardwareTransactionSender(signedTx, activeAccount.address);
|
|
2151
|
-
const finalTx = await provider.sendTransaction(
|
|
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 =
|
|
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 =
|
|
2251
|
+
const signedTx = serializeTransaction(txFormattedForEthers, formattedSignature);
|
|
2276
2252
|
assertCurrent();
|
|
2277
2253
|
assertHardwareTransactionSender(signedTx, activeAccount.address);
|
|
2278
|
-
const finalTx = await provider.sendTransaction(
|
|
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 =
|
|
2339
|
+
const signedTx = serializeTransaction(txFormattedForEthers, trezorSignature(signature.payload));
|
|
2363
2340
|
assertCurrent();
|
|
2364
2341
|
assertHardwareTransactionSender(signedTx, activeAccount.address);
|
|
2365
|
-
const finalTx = await provider.sendTransaction(
|
|
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
|
|
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
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
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
|
-
|
|
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 (
|
|
2449
|
-
|
|
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
|
|
2457
|
-
to:
|
|
2458
|
-
from:
|
|
2459
|
-
value:
|
|
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 (
|
|
2463
|
-
|
|
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
|
-
|
|
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
|
|
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() {
|