@sidhujag/sysweb3-keyring 2.1.7 → 2.1.8
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/hardware-transaction-authority.js +15 -11
- package/cjs/hardware-transaction-authority.js.map +1 -1
- package/cjs/hardware-wallet-manager.js +74 -119
- package/cjs/hardware-wallet-manager.js.map +1 -1
- package/cjs/hardware-wallet-types.js +16 -0
- package/cjs/hardware-wallet-types.js.map +1 -0
- package/cjs/host-keyring.js +32 -0
- package/cjs/host-keyring.js.map +1 -0
- package/cjs/index.js +1 -0
- package/cjs/index.js.map +1 -1
- package/cjs/keyring-core.js +96 -61
- package/cjs/keyring-core.js.map +1 -1
- package/cjs/keyring-manager.js +15 -16
- package/cjs/keyring-manager.js.map +1 -1
- package/cjs/ledger/host-types.js.map +1 -1
- package/cjs/ledger/index.js +24 -4
- package/cjs/ledger/index.js.map +1 -1
- package/cjs/ledger-hardware.js +15 -0
- package/cjs/ledger-hardware.js.map +1 -0
- package/cjs/software.js +6 -25
- package/cjs/software.js.map +1 -1
- package/cjs/transactions/ethereum.js +317 -268
- package/cjs/transactions/ethereum.js.map +1 -1
- package/cjs/transactions/evm-submission.js +32 -4
- package/cjs/transactions/evm-submission.js.map +1 -1
- package/cjs/transactions/ledger-signing.js +44 -0
- package/cjs/transactions/ledger-signing.js.map +1 -0
- package/cjs/transactions/prepared-evm.js +24 -1
- package/cjs/transactions/prepared-evm.js.map +1 -1
- package/cjs/trezor/connect.js +238 -243
- package/cjs/trezor/connect.js.map +1 -1
- package/cjs/trezor/host-types.js +19 -0
- package/cjs/trezor/host-types.js.map +1 -0
- package/cjs/trezor/index.js +75 -23
- package/cjs/trezor/index.js.map +1 -1
- package/cjs/trezor/webextension-host.js +118 -0
- package/cjs/trezor/webextension-host.js.map +1 -0
- package/cjs/trezor-hardware.js +16 -0
- package/cjs/trezor-hardware.js.map +1 -0
- package/cjs/types.js.map +1 -1
- package/esm/hardware-transaction-authority.js +14 -11
- package/esm/hardware-transaction-authority.js.map +1 -1
- package/esm/hardware-wallet-manager.js +20 -32
- package/esm/hardware-wallet-manager.js.map +1 -1
- package/esm/hardware-wallet-types.js +13 -0
- package/esm/hardware-wallet-types.js.map +1 -0
- package/esm/keyring-core.js +98 -63
- package/esm/keyring-core.js.map +1 -1
- package/esm/ledger/dmk-host.js +2 -0
- package/esm/ledger/dmk-host.js.map +1 -1
- package/esm/ledger/dmk-session.js +73 -8
- package/esm/ledger/dmk-session.js.map +1 -1
- package/esm/ledger/host-types.js.map +1 -1
- package/esm/ledger/index.js +22 -2
- package/esm/ledger/index.js.map +1 -1
- package/esm/transactions/ethereum.js +318 -269
- package/esm/transactions/ethereum.js.map +1 -1
- package/esm/transactions/evm-submission.js +32 -4
- package/esm/transactions/evm-submission.js.map +1 -1
- package/esm/transactions/ledger-signing.js +41 -0
- package/esm/transactions/ledger-signing.js.map +1 -0
- package/esm/transactions/prepared-evm.js +23 -1
- package/esm/transactions/prepared-evm.js.map +1 -1
- package/esm/trezor/connect.js +236 -209
- package/esm/trezor/connect.js.map +1 -1
- package/esm/trezor/host-types.js +16 -0
- package/esm/trezor/host-types.js.map +1 -0
- package/esm/trezor/index.js +74 -22
- package/esm/trezor/index.js.map +1 -1
- package/esm/types.js.map +1 -1
- package/package.json +55 -1
- package/types/hardware-transaction-authority.d.ts +1 -0
- package/types/hardware-wallet-manager.d.ts +6 -22
- package/types/hardware-wallet-types.d.ts +21 -0
- package/types/host-keyring.d.ts +16 -0
- package/types/index.d.ts +1 -0
- package/types/keyring-core.d.ts +12 -4
- package/types/keyring-manager.d.ts +5 -3
- package/types/ledger/dmk-host.d.ts +2 -0
- package/types/ledger/dmk-session.d.ts +9 -0
- package/types/ledger/host-types.d.ts +2 -0
- package/types/ledger/index.d.ts +3 -1
- package/types/ledger-hardware.d.ts +5 -0
- package/types/software.d.ts +2 -16
- package/types/transactions/ethereum.d.ts +11 -1
- package/types/transactions/evm-submission.d.ts +7 -2
- package/types/transactions/ledger-signing.d.ts +13 -0
- package/types/transactions/prepared-evm.d.ts +12 -0
- package/types/trezor/connect.d.ts +11 -9
- package/types/trezor/host-types.d.ts +25 -0
- package/types/trezor/index.d.ts +7 -7
- package/types/trezor/webextension-host.d.ts +7 -0
- package/types/trezor-hardware.d.ts +6 -0
- package/types/types.d.ts +16 -4
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { decrypt, signTypedData as signTypedDataUtil, getEncryptionPublicKey, recoverPersonalSignature, recoverTypedSignature, } from '@metamask/eth-sig-util';
|
|
2
2
|
import { INetworkType } from '@sidhujag/sysweb3-network';
|
|
3
3
|
import { createContractUsingAbi, getErc20Abi, getErc21Abi, getErc55Abi, } from '@sidhujag/sysweb3-utils';
|
|
4
|
+
import { getAddress } from 'ethers/address';
|
|
4
5
|
import { accessListify, Transaction } from 'ethers/transaction';
|
|
5
6
|
import { hexlify } from 'ethers/utils';
|
|
6
7
|
import omit from 'lodash/omit';
|
|
@@ -9,9 +10,11 @@ import { assertEvmGasLimit, bufferedEvmGasLimit, EvmGasLimitError, getEvmFeeData
|
|
|
9
10
|
import { CustomJsonRpcProvider, wrapTransactionResponse } from '../providers';
|
|
10
11
|
import { KeyringAccountType, } from '../types';
|
|
11
12
|
import { assertEvmCancellationEnvelope, authenticateRetainedCancellation, } from './evm-cancellation';
|
|
13
|
+
import { readEvmOwnData, serializePreparedEvmTransaction, snapshotPreparedEvmTransaction, } from './evm-envelope';
|
|
12
14
|
import { deriveEvmAccountFromMnemonic, parsePersonalMessage as parseLocalPersonalMessage, privateKeyToAccount, normalizeGasLimitedTransaction, sendLocalEvmTransaction, signDigestHex, signPersonalMessage as signLocalPersonalMessage, } from './evm-local-signer';
|
|
13
15
|
import { captureEvmSubmission, submitSignedEvmTransaction, submitSignedHardwareEvmTransaction, evmSubmissionFailure, copyEvmSubmissionFailure, } from './evm-submission';
|
|
14
|
-
import {
|
|
16
|
+
import { signLedgerEvmTransaction } from './ledger-signing';
|
|
17
|
+
import { signPreparedEvmEnvelope, snapshotPreparedEvmRequest, snapshotEvmSigningIdentity, preparedEvmGuard, } from './prepared-evm';
|
|
15
18
|
import { getAddressDerivationPath } from '../utils/derivation-paths';
|
|
16
19
|
const hardwareSignerUnavailable = (hardware) => Object.assign(new Error(`${hardware} signer is unavailable in this host`), {
|
|
17
20
|
code: 'HARDWARE_SIGNER_UNAVAILABLE',
|
|
@@ -154,7 +157,7 @@ export class EthereumTransactions {
|
|
|
154
157
|
}
|
|
155
158
|
return () => assertSynchronousOriginalValidation(guard(), 'Signing context guard must be synchronous');
|
|
156
159
|
};
|
|
157
|
-
this.
|
|
160
|
+
this.captureEvmAccountContext = (target) => {
|
|
158
161
|
this.assertHardwareSignerAvailable(target);
|
|
159
162
|
const state = this.getState();
|
|
160
163
|
const selectedType = state.activeAccountType;
|
|
@@ -168,7 +171,6 @@ export class EthereumTransactions {
|
|
|
168
171
|
const account = { address: selectedAccount.address };
|
|
169
172
|
const network = { ...state.activeNetwork };
|
|
170
173
|
const assertSession = this.captureSessionGuard();
|
|
171
|
-
const provider = this.web3Provider;
|
|
172
174
|
const assertCurrent = () => {
|
|
173
175
|
assertSession();
|
|
174
176
|
const current = this.getState();
|
|
@@ -182,8 +184,7 @@ export class EthereumTransactions {
|
|
|
182
184
|
[current.activeNetwork, this.getNetwork()].some((selected) => selected.kind !== network.kind ||
|
|
183
185
|
selected.chainId !== network.chainId ||
|
|
184
186
|
selected.slip44 !== network.slip44 ||
|
|
185
|
-
selected.url !== network.url)
|
|
186
|
-
this.web3Provider !== provider)
|
|
187
|
+
selected.url !== network.url))
|
|
187
188
|
throw new Error('Wallet signing context changed');
|
|
188
189
|
};
|
|
189
190
|
assertCurrent();
|
|
@@ -192,10 +193,20 @@ export class EthereumTransactions {
|
|
|
192
193
|
activeAccountType,
|
|
193
194
|
activeAccountId,
|
|
194
195
|
activeNetwork: network,
|
|
195
|
-
provider,
|
|
196
196
|
assertCurrent,
|
|
197
197
|
};
|
|
198
198
|
};
|
|
199
|
+
this.captureEvmSigningContext = (target) => {
|
|
200
|
+
const context = this.captureEvmAccountContext(target);
|
|
201
|
+
const provider = this.web3Provider;
|
|
202
|
+
const assertCurrent = () => {
|
|
203
|
+
context.assertCurrent();
|
|
204
|
+
if (this.web3Provider !== provider)
|
|
205
|
+
throw new Error('Wallet signing context changed');
|
|
206
|
+
};
|
|
207
|
+
assertCurrent();
|
|
208
|
+
return { ...context, provider, assertCurrent };
|
|
209
|
+
};
|
|
199
210
|
this.captureSubmission = (operation, account, type, id, network, assertCurrent, details = {}) => {
|
|
200
211
|
if (!this.captureSubmissionLifecycle)
|
|
201
212
|
throw new TypeError('A durable EVM submission lifecycle is required');
|
|
@@ -296,10 +307,51 @@ export class EthereumTransactions {
|
|
|
296
307
|
throw error;
|
|
297
308
|
}
|
|
298
309
|
};
|
|
299
|
-
this.ethSign = async (params) => {
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
|
|
310
|
+
this.ethSign = async (params, input) => {
|
|
311
|
+
const selected = this.captureEvmAccountContext();
|
|
312
|
+
if (!Array.isArray(params) || params.length !== 2)
|
|
313
|
+
throw new Error('Invalid digest request');
|
|
314
|
+
const first = Object.getOwnPropertyDescriptor(params, '0');
|
|
315
|
+
const second = Object.getOwnPropertyDescriptor(params, '1');
|
|
316
|
+
if (!first ||
|
|
317
|
+
!second ||
|
|
318
|
+
!('value' in first) ||
|
|
319
|
+
!('value' in second) ||
|
|
320
|
+
typeof first.value !== 'string' ||
|
|
321
|
+
typeof second.value !== 'string')
|
|
322
|
+
throw new Error('Invalid digest request');
|
|
323
|
+
params = [first.value, second.value];
|
|
324
|
+
const options = readEvmOwnData(input, [
|
|
325
|
+
'selectedAccount',
|
|
326
|
+
'targetAccount',
|
|
327
|
+
'assertCurrent',
|
|
328
|
+
]);
|
|
329
|
+
const selectedAccount = snapshotEvmSigningIdentity(options.selectedAccount);
|
|
330
|
+
const targetAccount = snapshotEvmSigningIdentity(options.targetAccount);
|
|
331
|
+
const hostGuard = preparedEvmGuard({
|
|
332
|
+
assertCurrent: options.assertCurrent,
|
|
333
|
+
});
|
|
334
|
+
const target = {
|
|
335
|
+
id: targetAccount.id,
|
|
336
|
+
type: targetAccount.type,
|
|
337
|
+
};
|
|
338
|
+
const context = this.captureEvmAccountContext(target);
|
|
339
|
+
const assertCurrent = () => {
|
|
340
|
+
hostGuard();
|
|
341
|
+
selected.assertCurrent();
|
|
342
|
+
context.assertCurrent();
|
|
343
|
+
if (selected.activeAccountId !== selectedAccount.id ||
|
|
344
|
+
selected.activeAccountType !== selectedAccount.type ||
|
|
345
|
+
getAddress(selected.activeAccount.address) !==
|
|
346
|
+
selectedAccount.expectedAddress ||
|
|
347
|
+
getAddress(context.activeAccount.address) !==
|
|
348
|
+
targetAccount.expectedAddress ||
|
|
349
|
+
targetAccount.type === 'SmartAccount')
|
|
350
|
+
throw new Error('EVM digest signing context changed');
|
|
351
|
+
};
|
|
352
|
+
assertCurrent();
|
|
353
|
+
const { address, decryptedPrivateKey } = this.getDecryptedPrivateKey(target);
|
|
354
|
+
const { activeAccountId, activeAccountType, activeNetwork, activeAccount } = context;
|
|
303
355
|
// Validate that the derived address matches the active account to prevent race conditions
|
|
304
356
|
if (address.toLowerCase() !== activeAccount.address.toLowerCase()) {
|
|
305
357
|
throw {
|
|
@@ -368,14 +420,19 @@ export class EthereumTransactions {
|
|
|
368
420
|
throw error;
|
|
369
421
|
}
|
|
370
422
|
};
|
|
423
|
+
let signature;
|
|
371
424
|
switch (activeAccountType) {
|
|
372
425
|
case KeyringAccountType.Trezor:
|
|
373
|
-
|
|
426
|
+
signature = await signWithTrezor();
|
|
427
|
+
break;
|
|
374
428
|
case KeyringAccountType.Ledger:
|
|
375
|
-
|
|
429
|
+
signature = await signWithLedger();
|
|
430
|
+
break;
|
|
376
431
|
default:
|
|
377
|
-
|
|
432
|
+
signature = sign();
|
|
378
433
|
}
|
|
434
|
+
assertCurrent();
|
|
435
|
+
return signature;
|
|
379
436
|
};
|
|
380
437
|
this.signPersonalMessage = async (params) => {
|
|
381
438
|
const { address, decryptedPrivateKey } = this.getDecryptedPrivateKey();
|
|
@@ -945,33 +1002,18 @@ export class EthereumTransactions {
|
|
|
945
1002
|
const rawTx = serializeTransaction(txFormattedForEthers);
|
|
946
1003
|
if (retained)
|
|
947
1004
|
assertCancellationEnvelope(rawTx);
|
|
948
|
-
const
|
|
949
|
-
|
|
1005
|
+
const { rawTransaction: signedTx } = await signLedgerEvmTransaction(this.requireLedgerSigner(), {
|
|
1006
|
+
unsignedTransaction: rawTx,
|
|
950
1007
|
accountIndex: activeAccountId,
|
|
951
1008
|
expectedAddress: activeAccount.address,
|
|
952
1009
|
}, await this.authorizeHardwareTransaction(provider, assertCurrent));
|
|
953
1010
|
assertCurrent();
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
1011
|
+
assertHardwareTransactionSender(signedTx, activeAccount.address);
|
|
1012
|
+
const transactionResponse = await this.submitSignedHardwareTransaction(provider, signedTx, ({ rawTransaction, admit }) => admit(() => provider.sendTransaction(rawTransaction)), submission, undefined, rawTx);
|
|
1013
|
+
return {
|
|
1014
|
+
isCanceled: true,
|
|
1015
|
+
transaction: transactionResponse,
|
|
958
1016
|
};
|
|
959
|
-
if (signature) {
|
|
960
|
-
const signedTx = serializeTransaction(txFormattedForEthers, formattedSignature);
|
|
961
|
-
assertCurrent();
|
|
962
|
-
assertHardwareTransactionSender(signedTx, activeAccount.address);
|
|
963
|
-
const transactionResponse = await this.submitSignedHardwareTransaction(provider, signedTx, ({ rawTransaction, admit }) => admit(() => provider.sendTransaction(rawTransaction)), submission, undefined, rawTx);
|
|
964
|
-
return {
|
|
965
|
-
isCanceled: true,
|
|
966
|
-
transaction: transactionResponse,
|
|
967
|
-
};
|
|
968
|
-
}
|
|
969
|
-
else {
|
|
970
|
-
return {
|
|
971
|
-
isCanceled: false,
|
|
972
|
-
error: true,
|
|
973
|
-
};
|
|
974
|
-
}
|
|
975
1017
|
}
|
|
976
1018
|
catch (error) {
|
|
977
1019
|
return {
|
|
@@ -1102,12 +1144,21 @@ export class EthereumTransactions {
|
|
|
1102
1144
|
typeof options !== 'object' ||
|
|
1103
1145
|
Array.isArray(options) ||
|
|
1104
1146
|
(options.mode !== 'reviewed-native' &&
|
|
1147
|
+
options.mode !== 'reviewed-generic' &&
|
|
1105
1148
|
options.mode !== 'specialized') ||
|
|
1106
1149
|
typeof options.assertCurrent !== 'function')
|
|
1107
1150
|
throw new Error('Explicit transaction approval is required');
|
|
1108
1151
|
// Capture once; later caller mutations cannot replace this invocation's
|
|
1109
1152
|
// admission decision while chain checks or device approval are pending.
|
|
1110
1153
|
const { assertCurrent: assertHostCurrent, mode, walletNonce } = options;
|
|
1154
|
+
const reviewId = mode === 'reviewed-generic' ? options.reviewId : undefined;
|
|
1155
|
+
if (mode === 'reviewed-generic' &&
|
|
1156
|
+
(typeof reviewId !== 'string' ||
|
|
1157
|
+
!/^[A-Za-z0-9_-]{1,128}$/.test(reviewId) ||
|
|
1158
|
+
walletNonce === undefined))
|
|
1159
|
+
throw new Error('An issued fee review and approved nonce are required');
|
|
1160
|
+
const selectedAccount = snapshotEvmSigningIdentity(options.selectedAccount);
|
|
1161
|
+
const targetAccount = snapshotEvmSigningIdentity(options.targetAccount);
|
|
1111
1162
|
const nonceApproval = walletNonce === undefined ? undefined : { ...walletNonce };
|
|
1112
1163
|
const assertApproved = () => {
|
|
1113
1164
|
const result = assertHostCurrent();
|
|
@@ -1119,14 +1170,14 @@ export class EthereumTransactions {
|
|
|
1119
1170
|
assertApproved();
|
|
1120
1171
|
return await this.sendFormattedTransactionInternal(params, isLegacy, () => {
|
|
1121
1172
|
broadcastAttempted = true;
|
|
1122
|
-
}, assertApproved, mode, nonceApproval);
|
|
1173
|
+
}, assertApproved, mode, nonceApproval, selectedAccount, targetAccount, reviewId);
|
|
1123
1174
|
}
|
|
1124
1175
|
catch (error) {
|
|
1125
1176
|
// A broadcast transport error may mean the node accepted the signed
|
|
1126
1177
|
// transaction. Derive this marker from this invocation, never RPC data.
|
|
1127
1178
|
const submissionError = Object.assign(new Error(error instanceof Error ? error.message : String(error)), error, {
|
|
1128
1179
|
transactionNotBroadcast: !broadcastAttempted ||
|
|
1129
|
-
evmSubmissionFailure(error)
|
|
1180
|
+
evmSubmissionFailure(error)?.transactionNotBroadcast === true,
|
|
1130
1181
|
});
|
|
1131
1182
|
if (error instanceof Error) {
|
|
1132
1183
|
submissionError.name = error.name;
|
|
@@ -1136,19 +1187,60 @@ export class EthereumTransactions {
|
|
|
1136
1187
|
throw submissionError;
|
|
1137
1188
|
}
|
|
1138
1189
|
};
|
|
1139
|
-
this.sendFormattedTransactionInternal = async (input, isLegacy, beforeBroadcast, assertHostCurrent, mode, walletNonce) => {
|
|
1190
|
+
this.sendFormattedTransactionInternal = async (input, isLegacy, beforeBroadcast, assertHostCurrent, mode, walletNonce, selectedIdentity, targetIdentity, reviewId) => {
|
|
1140
1191
|
// Hardware approval may outlive the selected account, network or unlocked
|
|
1141
1192
|
// session. Keep immutable identities and one provider for this invocation.
|
|
1142
|
-
const
|
|
1193
|
+
const selectedContext = this.captureEvmAccountContext();
|
|
1194
|
+
const targetAccount = {
|
|
1195
|
+
id: targetIdentity.id,
|
|
1196
|
+
type: targetIdentity.type,
|
|
1197
|
+
};
|
|
1198
|
+
if (targetAccount.type === KeyringAccountType.SmartAccount)
|
|
1199
|
+
throw new Error('Invalid EVM payer');
|
|
1200
|
+
const { activeAccount, activeAccountType, activeAccountId, activeNetwork, provider, assertCurrent: assertKeyringCurrent, } = this.captureEvmSigningContext(targetAccount);
|
|
1143
1201
|
const assertSigningCurrent = () => {
|
|
1144
1202
|
assertHostCurrent();
|
|
1203
|
+
selectedContext.assertCurrent();
|
|
1145
1204
|
assertKeyringCurrent();
|
|
1205
|
+
if (selectedContext.activeAccountId !== selectedIdentity.id ||
|
|
1206
|
+
selectedContext.activeAccountType !== selectedIdentity.type ||
|
|
1207
|
+
getAddress(selectedContext.activeAccount.address) !==
|
|
1208
|
+
selectedIdentity.expectedAddress ||
|
|
1209
|
+
getAddress(activeAccount.address) !== targetIdentity.expectedAddress)
|
|
1210
|
+
throw new Error('EVM payer approval context changed');
|
|
1146
1211
|
};
|
|
1212
|
+
assertSigningCurrent();
|
|
1213
|
+
// Generic reviewed calls keep the exact prepared envelope, including
|
|
1214
|
+
// contract creation, calldata and access lists, through existing signers.
|
|
1215
|
+
const reviewed = mode === 'reviewed-generic'
|
|
1216
|
+
? snapshotPreparedEvmTransaction(input)
|
|
1217
|
+
: undefined;
|
|
1218
|
+
if (reviewed) {
|
|
1219
|
+
if (reviewed.from !== targetIdentity.expectedAddress ||
|
|
1220
|
+
reviewed.chainId !== String(activeNetwork.chainId) ||
|
|
1221
|
+
reviewed.nonce !== String(walletNonce?.nonce) ||
|
|
1222
|
+
(isLegacy !== undefined && isLegacy !== (reviewed.type === 0)) ||
|
|
1223
|
+
(this.gasOverrides.minGasLimit &&
|
|
1224
|
+
BigInt(reviewed.gasLimit) < this.gasOverrides.minGasLimit.toBigInt()))
|
|
1225
|
+
throw new Error('Reviewed EVM transaction does not match its approval');
|
|
1226
|
+
input = reviewed;
|
|
1227
|
+
isLegacy = reviewed.type === 0;
|
|
1228
|
+
}
|
|
1147
1229
|
const assertBeforeBroadcast = () => {
|
|
1148
1230
|
assertCurrent();
|
|
1149
1231
|
beforeBroadcast();
|
|
1150
1232
|
};
|
|
1151
|
-
const submission = this.captureSubmission('send', activeAccount, activeAccountType, activeAccountId, activeNetwork, assertSigningCurrent, {
|
|
1233
|
+
const submission = this.captureSubmission('send', activeAccount, activeAccountType, activeAccountId, activeNetwork, assertSigningCurrent, {
|
|
1234
|
+
walletNonce,
|
|
1235
|
+
...(reviewed
|
|
1236
|
+
? {
|
|
1237
|
+
review: {
|
|
1238
|
+
id: reviewId,
|
|
1239
|
+
unsignedHash: Transaction.from(serializePreparedEvmTransaction(reviewed)).unsignedHash,
|
|
1240
|
+
},
|
|
1241
|
+
}
|
|
1242
|
+
: {}),
|
|
1243
|
+
});
|
|
1152
1244
|
const assertCurrent = submission.assertCurrent;
|
|
1153
1245
|
const approvedNonce = submission.context.walletNonce?.nonce;
|
|
1154
1246
|
const assertApprovedNonce = (nonce) => {
|
|
@@ -1157,6 +1249,7 @@ export class EthereumTransactions {
|
|
|
1157
1249
|
};
|
|
1158
1250
|
if (mode === 'reviewed-native') {
|
|
1159
1251
|
const { transaction } = snapshotPreparedEvmRequest({
|
|
1252
|
+
selectedAccount: selectedIdentity,
|
|
1160
1253
|
account: {
|
|
1161
1254
|
id: activeAccountId,
|
|
1162
1255
|
type: activeAccountType,
|
|
@@ -1187,7 +1280,7 @@ export class EthereumTransactions {
|
|
|
1187
1280
|
assertCurrent();
|
|
1188
1281
|
if (this.gasOverrides.minGasLimit !== minimum)
|
|
1189
1282
|
throw new Error('Reviewed native gas policy changed');
|
|
1190
|
-
const { address, decryptedPrivateKey } = this.getDecryptedPrivateKey();
|
|
1283
|
+
const { address, decryptedPrivateKey } = this.getDecryptedPrivateKey(targetAccount);
|
|
1191
1284
|
if (address.toLowerCase() !== transaction.from.toLowerCase())
|
|
1192
1285
|
throw new Error('Reviewed native signer does not match the approved account');
|
|
1193
1286
|
assertCurrent();
|
|
@@ -1200,6 +1293,9 @@ export class EthereumTransactions {
|
|
|
1200
1293
|
if (!params || typeof params !== 'object' || Array.isArray(params))
|
|
1201
1294
|
throw new TypeError('Invalid EVM transaction request');
|
|
1202
1295
|
const supplied = { ...params };
|
|
1296
|
+
if (supplied.from !== undefined &&
|
|
1297
|
+
getAddress(supplied.from) !== targetIdentity.expectedAddress)
|
|
1298
|
+
throw new Error('EVM transaction sender does not match the approved payer');
|
|
1203
1299
|
// Validate original scalar spellings before conversion. Normalization's
|
|
1204
1300
|
// permissive zero coercion must not bypass the bounded wire parser.
|
|
1205
1301
|
for (const field of [
|
|
@@ -1252,7 +1348,8 @@ export class EthereumTransactions {
|
|
|
1252
1348
|
const validateEip1559Fees = () => {
|
|
1253
1349
|
const maximum = nonNegativeFeeQuantity(params.maxFeePerGas, 'maximum fee');
|
|
1254
1350
|
const priority = nonNegativeFeeQuantity(params.maxPriorityFeePerGas, 'priority fee');
|
|
1255
|
-
if (maximum.isZero() ||
|
|
1351
|
+
if ((mode !== 'reviewed-generic' && maximum.isZero()) ||
|
|
1352
|
+
priority.gt(maximum))
|
|
1256
1353
|
throw new Error('Invalid EIP-1559 fees');
|
|
1257
1354
|
params.maxFeePerGas = maximum;
|
|
1258
1355
|
params.maxPriorityFeePerGas = priority;
|
|
@@ -1263,7 +1360,9 @@ export class EthereumTransactions {
|
|
|
1263
1360
|
validateEip1559Fees();
|
|
1264
1361
|
// Only apply a minimum gas limit when a network-specific override is set.
|
|
1265
1362
|
// Otherwise keep the user/dapp-approved gas limit unchanged.
|
|
1266
|
-
if (
|
|
1363
|
+
if (mode !== 'reviewed-generic' &&
|
|
1364
|
+
params.gasLimit != null &&
|
|
1365
|
+
this.gasOverrides.minGasLimit) {
|
|
1267
1366
|
const minGasLimit = this.gasOverrides.minGasLimit;
|
|
1268
1367
|
const currentGasLimit = BigNumber.from(params.gasLimit);
|
|
1269
1368
|
if (currentGasLimit.lt(minGasLimit)) {
|
|
@@ -1334,31 +1433,15 @@ export class EthereumTransactions {
|
|
|
1334
1433
|
type: 2, // Need explicit type for hardware wallet serialization
|
|
1335
1434
|
};
|
|
1336
1435
|
const rawTx = serializeTransaction(txFormattedForEthers);
|
|
1337
|
-
const
|
|
1338
|
-
|
|
1436
|
+
const { rawTransaction: signedTx } = await signLedgerEvmTransaction(this.requireLedgerSigner(), {
|
|
1437
|
+
unsignedTransaction: rawTx,
|
|
1339
1438
|
accountIndex: activeAccountId,
|
|
1340
1439
|
expectedAddress: activeAccount.address,
|
|
1341
1440
|
}, await this.authorizeHardwareTransaction(provider, assertCurrent));
|
|
1342
1441
|
assertCurrent();
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
v: parseInt(signature.v, 16),
|
|
1347
|
-
};
|
|
1348
|
-
if (signature) {
|
|
1349
|
-
try {
|
|
1350
|
-
const signedTx = serializeTransaction(txFormattedForEthers, formattedSignature);
|
|
1351
|
-
assertHardwareTransactionSender(signedTx, activeAccount.address);
|
|
1352
|
-
const finalTx = await this.submitSignedHardwareTransaction(provider, signedTx, ({ rawTransaction, admit }) => admit(() => provider.sendTransaction(rawTransaction)), submission, assertBeforeBroadcast, rawTx);
|
|
1353
|
-
return finalTx;
|
|
1354
|
-
}
|
|
1355
|
-
catch (error) {
|
|
1356
|
-
throw error;
|
|
1357
|
-
}
|
|
1358
|
-
}
|
|
1359
|
-
else {
|
|
1360
|
-
throw new Error(`Transaction Signature Failed. Error: ${signature}`);
|
|
1361
|
-
}
|
|
1442
|
+
assertHardwareTransactionSender(signedTx, activeAccount.address);
|
|
1443
|
+
const finalTx = await this.submitSignedHardwareTransaction(provider, signedTx, ({ rawTransaction, admit }) => admit(() => provider.sendTransaction(rawTransaction)), submission, assertBeforeBroadcast, rawTx);
|
|
1444
|
+
return finalTx;
|
|
1362
1445
|
};
|
|
1363
1446
|
const sendEVMTrezorTransaction = async () => {
|
|
1364
1447
|
assertCurrent();
|
|
@@ -1452,7 +1535,7 @@ export class EthereumTransactions {
|
|
|
1452
1535
|
};
|
|
1453
1536
|
const sendEVMTransaction = async () => {
|
|
1454
1537
|
assertCurrent();
|
|
1455
|
-
const { address, decryptedPrivateKey } = this.getDecryptedPrivateKey();
|
|
1538
|
+
const { address, decryptedPrivateKey } = this.getDecryptedPrivateKey(targetAccount);
|
|
1456
1539
|
// Validate that we have the correct private key for the active account to prevent race conditions
|
|
1457
1540
|
// This is critical for transaction security during account switches
|
|
1458
1541
|
if (address.toLowerCase() !== activeAccount.address.toLowerCase()) {
|
|
@@ -1709,32 +1792,18 @@ export class EthereumTransactions {
|
|
|
1709
1792
|
};
|
|
1710
1793
|
const rawTx = serializeTransaction(txFormattedForEthers);
|
|
1711
1794
|
assertCurrent();
|
|
1712
|
-
const
|
|
1713
|
-
|
|
1795
|
+
const { rawTransaction: signedTx } = await signLedgerEvmTransaction(this.requireLedgerSigner(), {
|
|
1796
|
+
unsignedTransaction: rawTx,
|
|
1714
1797
|
accountIndex: activeAccountId,
|
|
1715
1798
|
expectedAddress: activeAccount.address,
|
|
1716
1799
|
}, await this.authorizeHardwareTransaction(provider, assertCurrent));
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1800
|
+
assertSignedSender(signedTx);
|
|
1801
|
+
assertHardwareTransactionSender(signedTx, activeAccount.address);
|
|
1802
|
+
const transactionResponse = await this.submitSignedHardwareTransaction(provider, signedTx, ({ rawTransaction, admit }) => admit(() => provider.sendTransaction(rawTransaction)), submission, assertBeforeBroadcast, rawTx);
|
|
1803
|
+
return {
|
|
1804
|
+
isSpeedUp: true,
|
|
1805
|
+
transaction: transactionResponse,
|
|
1721
1806
|
};
|
|
1722
|
-
if (signature) {
|
|
1723
|
-
const signedTx = serializeTransaction(txFormattedForEthers, formattedSignature);
|
|
1724
|
-
assertSignedSender(signedTx);
|
|
1725
|
-
assertHardwareTransactionSender(signedTx, activeAccount.address);
|
|
1726
|
-
const transactionResponse = await this.submitSignedHardwareTransaction(provider, signedTx, ({ rawTransaction, admit }) => admit(() => provider.sendTransaction(rawTransaction)), submission, assertBeforeBroadcast, rawTx);
|
|
1727
|
-
return {
|
|
1728
|
-
isSpeedUp: true,
|
|
1729
|
-
transaction: transactionResponse,
|
|
1730
|
-
};
|
|
1731
|
-
}
|
|
1732
|
-
else {
|
|
1733
|
-
return {
|
|
1734
|
-
isSpeedUp: false,
|
|
1735
|
-
error: true,
|
|
1736
|
-
};
|
|
1737
|
-
}
|
|
1738
1807
|
}
|
|
1739
1808
|
catch (error) {
|
|
1740
1809
|
console.error('[SpeedUp] Failed to send replacement transaction with Ledger:', error);
|
|
@@ -1947,73 +2016,51 @@ export class EthereumTransactions {
|
|
|
1947
2016
|
};
|
|
1948
2017
|
const sendERC20TokenOnLedger = async () => {
|
|
1949
2018
|
assertCurrent();
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
type: 0,
|
|
1967
|
-
};
|
|
1968
|
-
}
|
|
1969
|
-
else {
|
|
1970
|
-
txFormattedForEthers = {
|
|
1971
|
-
to: tokenAddress,
|
|
1972
|
-
value: '0x0',
|
|
1973
|
-
gasLimit: gasLimit,
|
|
1974
|
-
maxFeePerGas,
|
|
1975
|
-
maxPriorityFeePerGas,
|
|
1976
|
-
data: txData,
|
|
1977
|
-
nonce,
|
|
1978
|
-
chainId: activeNetwork.chainId,
|
|
1979
|
-
type: 2, // Need explicit type for hardware wallet serialization
|
|
1980
|
-
};
|
|
1981
|
-
}
|
|
1982
|
-
const rawTx = serializeTransaction(txFormattedForEthers);
|
|
1983
|
-
const signature = await this.requireLedgerSigner().evm.signEVMTransaction({
|
|
1984
|
-
rawTx: rawTx.replace('0x', ''),
|
|
1985
|
-
accountIndex: activeAccountId,
|
|
1986
|
-
expectedAddress: activeAccount.address,
|
|
1987
|
-
}, await this.authorizeHardwareTransaction(provider, assertCurrent, signal));
|
|
1988
|
-
assertCurrent();
|
|
1989
|
-
const formattedSignature = {
|
|
1990
|
-
r: `0x${signature.r}`,
|
|
1991
|
-
s: `0x${signature.s}`,
|
|
1992
|
-
v: parseInt(signature.v, 16),
|
|
2019
|
+
const _contract = new Contract(tokenAddress, getErc20Abi(), provider);
|
|
2020
|
+
const txData = _contract.interface.encodeFunctionData('transfer', [
|
|
2021
|
+
receiver,
|
|
2022
|
+
exactTokenAmount,
|
|
2023
|
+
]);
|
|
2024
|
+
let txFormattedForEthers;
|
|
2025
|
+
if (isLegacy) {
|
|
2026
|
+
txFormattedForEthers = {
|
|
2027
|
+
to: tokenAddress,
|
|
2028
|
+
value: '0x0',
|
|
2029
|
+
gasLimit: gasLimit,
|
|
2030
|
+
gasPrice,
|
|
2031
|
+
data: txData,
|
|
2032
|
+
nonce,
|
|
2033
|
+
chainId: activeNetwork.chainId,
|
|
2034
|
+
type: 0,
|
|
1993
2035
|
};
|
|
1994
|
-
if (signature) {
|
|
1995
|
-
try {
|
|
1996
|
-
const signedTx = serializeTransaction(txFormattedForEthers, formattedSignature);
|
|
1997
|
-
assertCurrent();
|
|
1998
|
-
assertHardwareTransactionSender(signedTx, activeAccount.address);
|
|
1999
|
-
const finalTx = await this.submitSignedHardwareTransaction(provider, signedTx, async ({ rawTransaction, admit }) => {
|
|
2000
|
-
const response = await admit(() => provider.sendTransaction(rawTransaction));
|
|
2001
|
-
saveTrezorTx && saveTrezorTx(response);
|
|
2002
|
-
return response;
|
|
2003
|
-
}, submission, undefined, rawTx);
|
|
2004
|
-
return finalTx;
|
|
2005
|
-
}
|
|
2006
|
-
catch (error) {
|
|
2007
|
-
throw error;
|
|
2008
|
-
}
|
|
2009
|
-
}
|
|
2010
|
-
else {
|
|
2011
|
-
throw new Error(`Transaction Signature Failed. Error: ${signature}`);
|
|
2012
|
-
}
|
|
2013
2036
|
}
|
|
2014
|
-
|
|
2015
|
-
|
|
2037
|
+
else {
|
|
2038
|
+
txFormattedForEthers = {
|
|
2039
|
+
to: tokenAddress,
|
|
2040
|
+
value: '0x0',
|
|
2041
|
+
gasLimit: gasLimit,
|
|
2042
|
+
maxFeePerGas,
|
|
2043
|
+
maxPriorityFeePerGas,
|
|
2044
|
+
data: txData,
|
|
2045
|
+
nonce,
|
|
2046
|
+
chainId: activeNetwork.chainId,
|
|
2047
|
+
type: 2, // Need explicit type for hardware wallet serialization
|
|
2048
|
+
};
|
|
2016
2049
|
}
|
|
2050
|
+
const rawTx = serializeTransaction(txFormattedForEthers);
|
|
2051
|
+
const { rawTransaction: signedTx } = await signLedgerEvmTransaction(this.requireLedgerSigner(), {
|
|
2052
|
+
unsignedTransaction: rawTx,
|
|
2053
|
+
accountIndex: activeAccountId,
|
|
2054
|
+
expectedAddress: activeAccount.address,
|
|
2055
|
+
}, await this.authorizeHardwareTransaction(provider, assertCurrent, signal));
|
|
2056
|
+
assertCurrent();
|
|
2057
|
+
assertHardwareTransactionSender(signedTx, activeAccount.address);
|
|
2058
|
+
const finalTx = await this.submitSignedHardwareTransaction(provider, signedTx, async ({ rawTransaction, admit }) => {
|
|
2059
|
+
const response = await admit(() => provider.sendTransaction(rawTransaction));
|
|
2060
|
+
saveTrezorTx && saveTrezorTx(response);
|
|
2061
|
+
return response;
|
|
2062
|
+
}, submission, undefined, rawTx);
|
|
2063
|
+
return finalTx;
|
|
2017
2064
|
};
|
|
2018
2065
|
const sendERC20TokenOnTrezor = async () => {
|
|
2019
2066
|
assertCurrent();
|
|
@@ -2174,70 +2221,48 @@ export class EthereumTransactions {
|
|
|
2174
2221
|
};
|
|
2175
2222
|
const sendERC721TokenOnLedger = async () => {
|
|
2176
2223
|
assertCurrent();
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
type: 0,
|
|
2195
|
-
};
|
|
2196
|
-
}
|
|
2197
|
-
else {
|
|
2198
|
-
txFormattedForEthers = {
|
|
2199
|
-
to: tokenAddress,
|
|
2200
|
-
value: '0x0',
|
|
2201
|
-
gasLimit,
|
|
2202
|
-
maxFeePerGas,
|
|
2203
|
-
maxPriorityFeePerGas,
|
|
2204
|
-
data: txData,
|
|
2205
|
-
nonce,
|
|
2206
|
-
chainId: activeNetwork.chainId,
|
|
2207
|
-
type: 2, // Need explicit type for hardware wallet serialization
|
|
2208
|
-
};
|
|
2209
|
-
}
|
|
2210
|
-
const rawTx = serializeTransaction(txFormattedForEthers);
|
|
2211
|
-
const signature = await this.requireLedgerSigner().evm.signEVMTransaction({
|
|
2212
|
-
rawTx: rawTx.replace('0x', ''),
|
|
2213
|
-
accountIndex: activeAccountId,
|
|
2214
|
-
expectedAddress: activeAccount.address,
|
|
2215
|
-
}, await this.authorizeHardwareTransaction(provider, assertCurrent, signal));
|
|
2216
|
-
assertCurrent();
|
|
2217
|
-
const formattedSignature = {
|
|
2218
|
-
r: `0x${signature.r}`,
|
|
2219
|
-
s: `0x${signature.s}`,
|
|
2220
|
-
v: parseInt(signature.v, 16),
|
|
2224
|
+
const _contract = new Contract(tokenAddress, getErc21Abi(), provider);
|
|
2225
|
+
const txData = _contract.interface.encodeFunctionData('transferFrom', [
|
|
2226
|
+
activeAccountAddress,
|
|
2227
|
+
receiver,
|
|
2228
|
+
normalizedTokenId,
|
|
2229
|
+
]);
|
|
2230
|
+
let txFormattedForEthers;
|
|
2231
|
+
if (isLegacy) {
|
|
2232
|
+
txFormattedForEthers = {
|
|
2233
|
+
to: tokenAddress,
|
|
2234
|
+
value: '0x0',
|
|
2235
|
+
gasLimit,
|
|
2236
|
+
gasPrice,
|
|
2237
|
+
data: txData,
|
|
2238
|
+
nonce,
|
|
2239
|
+
chainId: activeNetwork.chainId,
|
|
2240
|
+
type: 0,
|
|
2221
2241
|
};
|
|
2222
|
-
if (signature) {
|
|
2223
|
-
try {
|
|
2224
|
-
const signedTx = serializeTransaction(txFormattedForEthers, formattedSignature);
|
|
2225
|
-
assertCurrent();
|
|
2226
|
-
assertHardwareTransactionSender(signedTx, activeAccount.address);
|
|
2227
|
-
const finalTx = await this.submitSignedHardwareTransaction(provider, signedTx, ({ rawTransaction, admit }) => admit(() => provider.sendTransaction(rawTransaction)), submission, undefined, rawTx);
|
|
2228
|
-
return finalTx;
|
|
2229
|
-
}
|
|
2230
|
-
catch (error) {
|
|
2231
|
-
throw error;
|
|
2232
|
-
}
|
|
2233
|
-
}
|
|
2234
|
-
else {
|
|
2235
|
-
throw new Error(`Transaction Signature Failed. Error: ${signature}`);
|
|
2236
|
-
}
|
|
2237
2242
|
}
|
|
2238
|
-
|
|
2239
|
-
|
|
2243
|
+
else {
|
|
2244
|
+
txFormattedForEthers = {
|
|
2245
|
+
to: tokenAddress,
|
|
2246
|
+
value: '0x0',
|
|
2247
|
+
gasLimit,
|
|
2248
|
+
maxFeePerGas,
|
|
2249
|
+
maxPriorityFeePerGas,
|
|
2250
|
+
data: txData,
|
|
2251
|
+
nonce,
|
|
2252
|
+
chainId: activeNetwork.chainId,
|
|
2253
|
+
type: 2, // Need explicit type for hardware wallet serialization
|
|
2254
|
+
};
|
|
2240
2255
|
}
|
|
2256
|
+
const rawTx = serializeTransaction(txFormattedForEthers);
|
|
2257
|
+
const { rawTransaction: signedTx } = await signLedgerEvmTransaction(this.requireLedgerSigner(), {
|
|
2258
|
+
unsignedTransaction: rawTx,
|
|
2259
|
+
accountIndex: activeAccountId,
|
|
2260
|
+
expectedAddress: activeAccount.address,
|
|
2261
|
+
}, await this.authorizeHardwareTransaction(provider, assertCurrent, signal));
|
|
2262
|
+
assertCurrent();
|
|
2263
|
+
assertHardwareTransactionSender(signedTx, activeAccount.address);
|
|
2264
|
+
const finalTx = await this.submitSignedHardwareTransaction(provider, signedTx, ({ rawTransaction, admit }) => admit(() => provider.sendTransaction(rawTransaction)), submission, undefined, rawTx);
|
|
2265
|
+
return finalTx;
|
|
2241
2266
|
};
|
|
2242
2267
|
const sendERC721TokenOnTrezor = async () => {
|
|
2243
2268
|
assertCurrent();
|
|
@@ -2404,66 +2429,44 @@ export class EthereumTransactions {
|
|
|
2404
2429
|
};
|
|
2405
2430
|
const sendERC1155TokenOnLedger = async () => {
|
|
2406
2431
|
assertCurrent();
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
type: 0,
|
|
2421
|
-
};
|
|
2422
|
-
}
|
|
2423
|
-
else {
|
|
2424
|
-
txFormattedForEthers = {
|
|
2425
|
-
to: tokenAddress,
|
|
2426
|
-
value: '0x0',
|
|
2427
|
-
gasLimit,
|
|
2428
|
-
maxFeePerGas,
|
|
2429
|
-
maxPriorityFeePerGas,
|
|
2430
|
-
data: txData,
|
|
2431
|
-
nonce,
|
|
2432
|
-
chainId: activeNetwork.chainId,
|
|
2433
|
-
type: 2, // Need explicit type for hardware wallet serialization
|
|
2434
|
-
};
|
|
2435
|
-
}
|
|
2436
|
-
const rawTx = serializeTransaction(txFormattedForEthers);
|
|
2437
|
-
const signature = await this.requireLedgerSigner().evm.signEVMTransaction({
|
|
2438
|
-
rawTx: rawTx.replace('0x', ''),
|
|
2439
|
-
accountIndex: activeAccountId,
|
|
2440
|
-
expectedAddress: activeAccount.address,
|
|
2441
|
-
}, await this.authorizeHardwareTransaction(provider, assertCurrent, signal));
|
|
2442
|
-
assertCurrent();
|
|
2443
|
-
const formattedSignature = {
|
|
2444
|
-
r: `0x${signature.r}`,
|
|
2445
|
-
s: `0x${signature.s}`,
|
|
2446
|
-
v: parseInt(signature.v, 16),
|
|
2432
|
+
const _contract = new Contract(tokenAddress, getErc55Abi(), provider);
|
|
2433
|
+
const txData = _contract.interface.encodeFunctionData('safeTransferFrom', [activeAccountAddress, receiver, normalizedTokenId, amount, '0x']);
|
|
2434
|
+
let txFormattedForEthers;
|
|
2435
|
+
if (isLegacy) {
|
|
2436
|
+
txFormattedForEthers = {
|
|
2437
|
+
to: tokenAddress,
|
|
2438
|
+
value: '0x0',
|
|
2439
|
+
gasLimit,
|
|
2440
|
+
gasPrice,
|
|
2441
|
+
data: txData,
|
|
2442
|
+
nonce,
|
|
2443
|
+
chainId: activeNetwork.chainId,
|
|
2444
|
+
type: 0,
|
|
2447
2445
|
};
|
|
2448
|
-
if (signature) {
|
|
2449
|
-
try {
|
|
2450
|
-
const signedTx = serializeTransaction(txFormattedForEthers, formattedSignature);
|
|
2451
|
-
assertCurrent();
|
|
2452
|
-
assertHardwareTransactionSender(signedTx, activeAccount.address);
|
|
2453
|
-
const finalTx = await this.submitSignedHardwareTransaction(provider, signedTx, ({ rawTransaction, admit }) => admit(() => provider.sendTransaction(rawTransaction)), submission, undefined, rawTx);
|
|
2454
|
-
return finalTx;
|
|
2455
|
-
}
|
|
2456
|
-
catch (error) {
|
|
2457
|
-
throw error;
|
|
2458
|
-
}
|
|
2459
|
-
}
|
|
2460
|
-
else {
|
|
2461
|
-
throw new Error(`Transaction Signature Failed. Error: ${signature}`);
|
|
2462
|
-
}
|
|
2463
2446
|
}
|
|
2464
|
-
|
|
2465
|
-
|
|
2447
|
+
else {
|
|
2448
|
+
txFormattedForEthers = {
|
|
2449
|
+
to: tokenAddress,
|
|
2450
|
+
value: '0x0',
|
|
2451
|
+
gasLimit,
|
|
2452
|
+
maxFeePerGas,
|
|
2453
|
+
maxPriorityFeePerGas,
|
|
2454
|
+
data: txData,
|
|
2455
|
+
nonce,
|
|
2456
|
+
chainId: activeNetwork.chainId,
|
|
2457
|
+
type: 2, // Need explicit type for hardware wallet serialization
|
|
2458
|
+
};
|
|
2466
2459
|
}
|
|
2460
|
+
const rawTx = serializeTransaction(txFormattedForEthers);
|
|
2461
|
+
const { rawTransaction: signedTx } = await signLedgerEvmTransaction(this.requireLedgerSigner(), {
|
|
2462
|
+
unsignedTransaction: rawTx,
|
|
2463
|
+
accountIndex: activeAccountId,
|
|
2464
|
+
expectedAddress: activeAccount.address,
|
|
2465
|
+
}, await this.authorizeHardwareTransaction(provider, assertCurrent, signal));
|
|
2466
|
+
assertCurrent();
|
|
2467
|
+
assertHardwareTransactionSender(signedTx, activeAccount.address);
|
|
2468
|
+
const finalTx = await this.submitSignedHardwareTransaction(provider, signedTx, ({ rawTransaction, admit }) => admit(() => provider.sendTransaction(rawTransaction)), submission, undefined, rawTx);
|
|
2469
|
+
return finalTx;
|
|
2467
2470
|
};
|
|
2468
2471
|
const sendERC1155TokenOnTrezor = async () => {
|
|
2469
2472
|
assertCurrent();
|
|
@@ -2785,6 +2788,52 @@ export class EthereumTransactions {
|
|
|
2785
2788
|
console.log('[EthereumTransactions] Deferring provider initialization:', error?.message || error);
|
|
2786
2789
|
}
|
|
2787
2790
|
}
|
|
2791
|
+
signPreparedEvmTransaction(input, options) {
|
|
2792
|
+
const selected = this.captureEvmAccountContext();
|
|
2793
|
+
const request = snapshotPreparedEvmRequest(input);
|
|
2794
|
+
const target = {
|
|
2795
|
+
id: request.account.id,
|
|
2796
|
+
type: request.account.type,
|
|
2797
|
+
};
|
|
2798
|
+
const context = this.captureEvmAccountContext(target);
|
|
2799
|
+
const hostGuard = preparedEvmGuard(options);
|
|
2800
|
+
const targetRecord = this.getState().accounts[target.type]?.[target.id];
|
|
2801
|
+
const encryptedKey = targetRecord?.xprv;
|
|
2802
|
+
const assertCurrent = () => {
|
|
2803
|
+
selected.assertCurrent();
|
|
2804
|
+
context.assertCurrent();
|
|
2805
|
+
const live = this.getState().accounts[target.type]?.[target.id];
|
|
2806
|
+
if (!live ||
|
|
2807
|
+
live.id !== target.id ||
|
|
2808
|
+
live.isSmartAccount ||
|
|
2809
|
+
live.isLedgerWallet ||
|
|
2810
|
+
live.isTrezorWallet ||
|
|
2811
|
+
typeof encryptedKey !== 'string' ||
|
|
2812
|
+
!encryptedKey ||
|
|
2813
|
+
live.xprv !== encryptedKey ||
|
|
2814
|
+
selected.activeAccountId !== request.selectedAccount.id ||
|
|
2815
|
+
selected.activeAccountType !== request.selectedAccount.type ||
|
|
2816
|
+
getAddress(selected.activeAccount.address) !==
|
|
2817
|
+
request.selectedAccount.expectedAddress ||
|
|
2818
|
+
getAddress(context.activeAccount.address) !==
|
|
2819
|
+
request.account.expectedAddress ||
|
|
2820
|
+
context.activeNetwork.kind !== 'ethereum' ||
|
|
2821
|
+
context.activeNetwork.slip44 !== 60 ||
|
|
2822
|
+
String(context.activeNetwork.chainId) !== request.network.chainId ||
|
|
2823
|
+
context.activeNetwork.url !== request.network.url)
|
|
2824
|
+
throw new Error('Prepared EVM signing context changed or is unsupported');
|
|
2825
|
+
};
|
|
2826
|
+
assertCurrent();
|
|
2827
|
+
hostGuard();
|
|
2828
|
+
assertCurrent();
|
|
2829
|
+
const key = this.getDecryptedPrivateKey(target);
|
|
2830
|
+
if (getAddress(key.address) !== request.account.expectedAddress)
|
|
2831
|
+
throw new Error('Prepared EVM signer address mismatch');
|
|
2832
|
+
const { signed } = signPreparedEvmEnvelope(request.transaction, key.decryptedPrivateKey);
|
|
2833
|
+
hostGuard();
|
|
2834
|
+
assertCurrent();
|
|
2835
|
+
return signed;
|
|
2836
|
+
}
|
|
2788
2837
|
setWeb3Provider(network) {
|
|
2789
2838
|
this.abortController.abort();
|
|
2790
2839
|
this.abortController = new AbortController();
|