@toruslabs/ethereum-controllers 8.16.0 → 8.17.0
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/dist/ethereumControllers.umd.min.js +1 -1
- package/dist/lib.cjs/Eip5792/walletGetCallsStatus.js +75 -0
- package/dist/lib.cjs/Eip5792/walletGetCapabilities.js +64 -0
- package/dist/lib.cjs/Eip5792/walletSendCalls.js +163 -0
- package/dist/lib.cjs/Eip7702/eip7702Utils.js +82 -0
- package/dist/lib.cjs/Eip7702/walletGetUpgradeStatus.js +35 -0
- package/dist/lib.cjs/Eip7702/walletUpgradeAccount.js +58 -0
- package/dist/lib.cjs/Keyring/KeyringController.js +50 -14
- package/dist/lib.cjs/Network/createEthereumMiddleware.js +114 -1
- package/dist/lib.cjs/Transaction/TransactionController.js +117 -6
- package/dist/lib.cjs/Transaction/TransactionGasUtil.js +13 -0
- package/dist/lib.cjs/Transaction/TransactionStateManager.js +9 -0
- package/dist/lib.cjs/Transaction/TransactionUtils.js +103 -10
- package/dist/lib.cjs/index.js +36 -0
- package/dist/lib.cjs/types/Eip5792/index.d.ts +3 -0
- package/dist/lib.cjs/types/Eip5792/walletGetCallsStatus.d.ts +19 -0
- package/dist/lib.cjs/types/Eip5792/walletGetCapabilities.d.ts +17 -0
- package/dist/lib.cjs/types/Eip5792/walletSendCalls.d.ts +45 -0
- package/dist/lib.cjs/types/Eip7702/eip7702Utils.d.ts +27 -0
- package/dist/lib.cjs/types/Eip7702/walletGetUpgradeStatus.d.ts +11 -0
- package/dist/lib.cjs/types/Eip7702/walletUpgradeAccount.d.ts +13 -0
- package/dist/lib.cjs/types/Keyring/KeyringController.d.ts +2 -0
- package/dist/lib.cjs/types/Network/createEthereumMiddleware.d.ts +53 -1
- package/dist/lib.cjs/types/Transaction/TransactionController.d.ts +9 -1
- package/dist/lib.cjs/types/Transaction/TransactionStateManager.d.ts +1 -0
- package/dist/lib.cjs/types/Transaction/TransactionUtils.d.ts +38 -1
- package/dist/lib.cjs/types/index.d.ts +4 -0
- package/dist/lib.cjs/types/utils/abis.d.ts +15 -0
- package/dist/lib.cjs/types/utils/constants.d.ts +2 -0
- package/dist/lib.cjs/types/utils/eip5792Types.d.ts +155 -0
- package/dist/lib.cjs/types/utils/eip7702Types.d.ts +60 -0
- package/dist/lib.cjs/types/utils/interfaces.d.ts +22 -2
- package/dist/lib.cjs/utils/abis.js +30 -0
- package/dist/lib.cjs/utils/constants.js +4 -1
- package/dist/lib.cjs/utils/eip5792Types.js +41 -0
- package/dist/lib.cjs/utils/eip7702Types.js +14 -0
- package/dist/lib.cjs/utils/interfaces.js +8 -0
- package/dist/lib.esm/Eip5792/walletGetCallsStatus.js +76 -0
- package/dist/lib.esm/Eip5792/walletGetCapabilities.js +63 -0
- package/dist/lib.esm/Eip5792/walletSendCalls.js +168 -0
- package/dist/lib.esm/Eip7702/eip7702Utils.js +81 -0
- package/dist/lib.esm/Eip7702/walletGetUpgradeStatus.js +33 -0
- package/dist/lib.esm/Eip7702/walletUpgradeAccount.js +56 -0
- package/dist/lib.esm/Keyring/KeyringController.js +53 -15
- package/dist/lib.esm/Network/createEthereumMiddleware.js +117 -2
- package/dist/lib.esm/Transaction/TransactionController.js +124 -10
- package/dist/lib.esm/Transaction/TransactionGasUtil.js +13 -0
- package/dist/lib.esm/Transaction/TransactionStateManager.js +9 -0
- package/dist/lib.esm/Transaction/TransactionUtils.js +103 -11
- package/dist/lib.esm/index.js +11 -4
- package/dist/lib.esm/utils/abis.js +30 -1
- package/dist/lib.esm/utils/constants.js +4 -2
- package/dist/lib.esm/utils/eip5792Types.js +105 -0
- package/dist/lib.esm/utils/eip7702Types.js +15 -0
- package/dist/lib.esm/utils/interfaces.js +28 -0
- package/package.json +7 -4
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { rpcErrors } from '@web3auth/auth';
|
|
2
|
+
import { TRANSACTION_ENVELOPE_TYPES } from '../utils/constants.js';
|
|
3
|
+
import { getIsEip7702UpgradeSupported } from './eip7702Utils.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Handle and prepare the transaction to upgrade an EOA to the EIP-7702 smart account.
|
|
7
|
+
*
|
|
8
|
+
* @param req - The request object.
|
|
9
|
+
* @param getEthCode - The function to get the code of an account.
|
|
10
|
+
* @param processTransaction - The function to process a transaction.
|
|
11
|
+
* @returns The transaction hash.
|
|
12
|
+
*/
|
|
13
|
+
async function walletUpgradeAccount(req, getEthCode, processTransaction) {
|
|
14
|
+
const {
|
|
15
|
+
method,
|
|
16
|
+
params
|
|
17
|
+
} = req;
|
|
18
|
+
if (method !== "wallet_upgradeAccount") {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const upgradeAccountParam = Array.isArray(params) ? params[0] : params;
|
|
22
|
+
const account = upgradeAccountParam.account;
|
|
23
|
+
const chainId = upgradeAccountParam.chainId;
|
|
24
|
+
if (!account || !chainId) {
|
|
25
|
+
throw rpcErrors.invalidParams("wallet_upgradeAccount: account and chainId are required");
|
|
26
|
+
}
|
|
27
|
+
const {
|
|
28
|
+
isSupported,
|
|
29
|
+
upgradeContractAddress,
|
|
30
|
+
delegationAddress
|
|
31
|
+
} = await getIsEip7702UpgradeSupported(account, chainId, getEthCode);
|
|
32
|
+
if (!isSupported) {
|
|
33
|
+
throw rpcErrors.methodNotSupported(`Account upgrade is not supported for chain ${chainId}`);
|
|
34
|
+
}
|
|
35
|
+
if (!upgradeContractAddress) {
|
|
36
|
+
throw rpcErrors.internal({
|
|
37
|
+
message: `Upgrade contract address not found for chain ${chainId}`
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
const isAlreadyUpgraded = Boolean(delegationAddress);
|
|
41
|
+
if (isAlreadyUpgraded) {
|
|
42
|
+
throw rpcErrors.invalidParams(`Account is already upgraded for chain ${chainId}`);
|
|
43
|
+
}
|
|
44
|
+
return processTransaction({
|
|
45
|
+
from: account,
|
|
46
|
+
to: account,
|
|
47
|
+
type: TRANSACTION_ENVELOPE_TYPES.SET_CODE,
|
|
48
|
+
// Type 4 transaction
|
|
49
|
+
authorizationList: [{
|
|
50
|
+
address: upgradeContractAddress,
|
|
51
|
+
chainId
|
|
52
|
+
}]
|
|
53
|
+
}, req);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export { walletUpgradeAccount };
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { encode } from '@ethereumjs/rlp';
|
|
1
2
|
import { addHexPrefix, bytesToHex, privateToPublic, toChecksumAddress, privateToAddress, stripHexPrefix, bigIntToBytes, isHexString } from '@ethereumjs/util';
|
|
2
3
|
import { BaseKeyringController, ecsignature, concatSig } from '@toruslabs/base-controllers';
|
|
3
|
-
import { SigningKey, hashMessage, TypedDataEncoder } from 'ethers';
|
|
4
|
+
import { SigningKey, concat, keccak256, hashMessage, TypedDataEncoder } from 'ethers';
|
|
5
|
+
import { toHex } from 'viem';
|
|
4
6
|
|
|
5
7
|
class KeyringController extends BaseKeyringController {
|
|
6
8
|
constructor({
|
|
@@ -23,24 +25,60 @@ class KeyringController extends BaseKeyringController {
|
|
|
23
25
|
localTx.signature = privKey.sign(localTx.unsignedHash);
|
|
24
26
|
return localTx;
|
|
25
27
|
}
|
|
28
|
+
async signEip7702Authorization(authorization, address) {
|
|
29
|
+
const wallet = this._getWalletForAccount(address);
|
|
30
|
+
const privKey = new SigningKey(addHexPrefix(wallet.privateKey));
|
|
31
|
+
|
|
32
|
+
// Convert hex strings to proper types for RLP encoding
|
|
33
|
+
// EIP-7702 authorization tuple: [chain_id_decimal, address, nonce_decimal]
|
|
34
|
+
const chainIdDecimal = parseInt(authorization.chainId, 16);
|
|
35
|
+
const nonceDecimal = parseInt(authorization.nonce, 16);
|
|
36
|
+
|
|
37
|
+
// RLP encode the eip7702 authorization
|
|
38
|
+
const encodedAuthorization = encode([chainIdDecimal, authorization.address, nonceDecimal]);
|
|
39
|
+
// Prefixed with EIP7702 Domain Separator (0x05)
|
|
40
|
+
const prefixedAuthorization = concat(["0x05", encodedAuthorization]);
|
|
41
|
+
const authorizationHash = keccak256(prefixedAuthorization);
|
|
42
|
+
const {
|
|
43
|
+
r,
|
|
44
|
+
s,
|
|
45
|
+
v
|
|
46
|
+
} = privKey.sign(authorizationHash);
|
|
47
|
+
const signedAuthorization = {
|
|
48
|
+
address: authorization.address,
|
|
49
|
+
chainId: authorization.chainId,
|
|
50
|
+
nonce: authorization.nonce,
|
|
51
|
+
r: addHexPrefix(r),
|
|
52
|
+
s: addHexPrefix(s),
|
|
53
|
+
yParity: toHex(v - 27 === 0 ? 0 : 1)
|
|
54
|
+
};
|
|
55
|
+
return signedAuthorization;
|
|
56
|
+
}
|
|
26
57
|
getAccounts() {
|
|
27
58
|
return this.state.wallets.map(w => w.publicKey);
|
|
28
59
|
}
|
|
29
60
|
importAccount(accountPrivateKey) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
61
|
+
try {
|
|
62
|
+
const hexPrivateKey = accountPrivateKey.padStart(64, "0");
|
|
63
|
+
const bufferPrivKey = Buffer.from(hexPrivateKey, "hex");
|
|
64
|
+
const publicKey = bytesToHex(privateToPublic(bufferPrivKey));
|
|
65
|
+
const address = toChecksumAddress(bytesToHex(privateToAddress(bufferPrivKey)));
|
|
66
|
+
const existingWallet = this.state.wallets.find(w => w.address === address);
|
|
67
|
+
if (existingWallet) return existingWallet.address;
|
|
68
|
+
this.update({
|
|
69
|
+
wallets: [...this.state.wallets, {
|
|
70
|
+
publicKey,
|
|
71
|
+
privateKey: accountPrivateKey,
|
|
72
|
+
address
|
|
73
|
+
}]
|
|
74
|
+
});
|
|
75
|
+
return address;
|
|
76
|
+
} catch (error) {
|
|
77
|
+
if (error instanceof Error && error.message.includes("expected 32 bytes")) {
|
|
78
|
+
throw new Error("invalid private key, expected hex or");
|
|
79
|
+
}
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
44
82
|
}
|
|
45
83
|
removeAccount(address) {
|
|
46
84
|
const newWallets = [...this.state.wallets];
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { PROVIDER_JRPC_METHODS, createFetchMiddleware, createGenericJRPCMiddleware } from '@toruslabs/base-controllers';
|
|
2
2
|
import { createAsyncMiddleware, mergeMiddleware, createScaffoldMiddleware } from '@web3auth/auth';
|
|
3
|
+
import { walletGetUpgradeStatus } from '../Eip7702/walletGetUpgradeStatus.js';
|
|
4
|
+
import { walletUpgradeAccount } from '../Eip7702/walletUpgradeAccount.js';
|
|
3
5
|
import { METHOD_TYPES, TRANSACTION_ENVELOPE_TYPES, BUNDLER_METHOD_TYPES, PAYMASTER_METHOD_TYPES } from '../utils/constants.js';
|
|
6
|
+
import { EIP_5792_METHODS } from '../utils/eip5792Types.js';
|
|
7
|
+
import { EIP_7702_METHODS } from '../utils/eip7702Types.js';
|
|
4
8
|
import { transactionDecoder } from '../utils/transaction.js';
|
|
9
|
+
import { walletGetCallsStatus } from '../Eip5792/walletGetCallsStatus.js';
|
|
10
|
+
import { walletSendCalls } from '../Eip5792/walletSendCalls.js';
|
|
11
|
+
import { walletGetCapabilities } from '../Eip5792/walletGetCapabilities.js';
|
|
5
12
|
|
|
6
13
|
function createGetAccountsMiddleware({
|
|
7
14
|
getAccounts
|
|
@@ -348,6 +355,101 @@ function createAAMiddleware({
|
|
|
348
355
|
}
|
|
349
356
|
return mergeMiddleware(middlewares);
|
|
350
357
|
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Middleware to handle EIP-7702 methods.
|
|
361
|
+
*
|
|
362
|
+
* @param getEthCode - Function to get the deployment bytecode of an address. (eth_getCode)
|
|
363
|
+
* @param processTransaction - Function to process a transaction.
|
|
364
|
+
* @returns JRPCMiddleware to handle EIP-7702 methods
|
|
365
|
+
*/
|
|
366
|
+
function createEip7702Middleware({
|
|
367
|
+
getEthCode,
|
|
368
|
+
processTransaction
|
|
369
|
+
}) {
|
|
370
|
+
return createScaffoldMiddleware({
|
|
371
|
+
[EIP_7702_METHODS.WALLET_UPGRADE_ACCOUNT]: createAsyncMiddleware(async (request, response, next) => {
|
|
372
|
+
if (request.method !== EIP_7702_METHODS.WALLET_UPGRADE_ACCOUNT) {
|
|
373
|
+
return next();
|
|
374
|
+
}
|
|
375
|
+
if (!getEthCode) throw new Error("WalletMiddleware - opts.getEthCode not provided");
|
|
376
|
+
if (!processTransaction) throw new Error("WalletMiddleware - opts.processTransaction not provided");
|
|
377
|
+
const result = await walletUpgradeAccount(request, getEthCode, processTransaction);
|
|
378
|
+
response.result = result;
|
|
379
|
+
}),
|
|
380
|
+
[EIP_7702_METHODS.WALLET_GET_ACCOUNT_UPGRADE_STATUS]: createAsyncMiddleware(async (request, response, next) => {
|
|
381
|
+
if (request.method !== EIP_7702_METHODS.WALLET_GET_ACCOUNT_UPGRADE_STATUS) {
|
|
382
|
+
return next();
|
|
383
|
+
}
|
|
384
|
+
if (!getEthCode) {
|
|
385
|
+
throw new Error("getEthCode not provided");
|
|
386
|
+
}
|
|
387
|
+
const result = await walletGetUpgradeStatus(request, getEthCode);
|
|
388
|
+
response.result = result;
|
|
389
|
+
})
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Middleware to handle EIP-5792 methods.
|
|
395
|
+
* Supports wallet capabilities and batch call operations for EIP-7702 upgraded accounts.
|
|
396
|
+
*
|
|
397
|
+
* @param eip5792Config - Configuration for EIP-5792 handlers.
|
|
398
|
+
* @param processTransaction - Function to process a transaction.
|
|
399
|
+
* @returns JRPCMiddleware to handle EIP-5792 methods
|
|
400
|
+
*/
|
|
401
|
+
function createEip5792Middleware({
|
|
402
|
+
eip5792Config,
|
|
403
|
+
processTransaction,
|
|
404
|
+
processTransactionBatch,
|
|
405
|
+
getTransactionByBatchId,
|
|
406
|
+
getEthCode
|
|
407
|
+
}) {
|
|
408
|
+
if (!eip5792Config) {
|
|
409
|
+
// Return a pass-through middleware if EIP-5792 is not configured
|
|
410
|
+
return (_req, _res, next) => next();
|
|
411
|
+
}
|
|
412
|
+
const {
|
|
413
|
+
getSupportedChains,
|
|
414
|
+
getCachedDelegations,
|
|
415
|
+
updateDelegationCache
|
|
416
|
+
} = eip5792Config;
|
|
417
|
+
|
|
418
|
+
// Context for wallet_getCapabilities
|
|
419
|
+
const getCapabilitiesContext = {
|
|
420
|
+
getSupportedChains,
|
|
421
|
+
getCachedDelegations,
|
|
422
|
+
updateDelegationCache
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
// Context for wallet_sendCalls
|
|
426
|
+
const sendCallsContext = {
|
|
427
|
+
processTransactionBatch
|
|
428
|
+
};
|
|
429
|
+
return createScaffoldMiddleware({
|
|
430
|
+
[EIP_5792_METHODS.WALLET_GET_CAPABILITIES]: createAsyncMiddleware(async (request, response, next) => {
|
|
431
|
+
if (request.method !== EIP_5792_METHODS.WALLET_GET_CAPABILITIES) {
|
|
432
|
+
return next();
|
|
433
|
+
}
|
|
434
|
+
const result = await walletGetCapabilities(request, getEthCode, getCapabilitiesContext);
|
|
435
|
+
response.result = result;
|
|
436
|
+
}),
|
|
437
|
+
[EIP_5792_METHODS.WALLET_SEND_CALLS]: createAsyncMiddleware(async (request, response, next) => {
|
|
438
|
+
if (request.method !== EIP_5792_METHODS.WALLET_SEND_CALLS) {
|
|
439
|
+
return next();
|
|
440
|
+
}
|
|
441
|
+
const result = await walletSendCalls(request, getEthCode, sendCallsContext);
|
|
442
|
+
response.result = result;
|
|
443
|
+
}),
|
|
444
|
+
[EIP_5792_METHODS.WALLET_GET_CALLS_STATUS]: createAsyncMiddleware(async (request, response, next) => {
|
|
445
|
+
if (request.method !== EIP_5792_METHODS.WALLET_GET_CALLS_STATUS) {
|
|
446
|
+
return next();
|
|
447
|
+
}
|
|
448
|
+
const result = walletGetCallsStatus(request, getTransactionByBatchId);
|
|
449
|
+
response.result = result;
|
|
450
|
+
})
|
|
451
|
+
});
|
|
452
|
+
}
|
|
351
453
|
function createEthereumMiddleware(providerHandlers, providerConfig, analytics) {
|
|
352
454
|
const {
|
|
353
455
|
requestAccounts,
|
|
@@ -355,6 +457,7 @@ function createEthereumMiddleware(providerHandlers, providerConfig, analytics) {
|
|
|
355
457
|
getPrivateKey,
|
|
356
458
|
getPublicKey,
|
|
357
459
|
processTransaction,
|
|
460
|
+
processTransactionBatch,
|
|
358
461
|
processSignTransaction,
|
|
359
462
|
processEstimateUserOperationGas,
|
|
360
463
|
processEthSignMessage,
|
|
@@ -362,16 +465,28 @@ function createEthereumMiddleware(providerHandlers, providerConfig, analytics) {
|
|
|
362
465
|
processPersonalMessage,
|
|
363
466
|
getPendingNonce,
|
|
364
467
|
getPendingTransactionByHash,
|
|
468
|
+
getTransactionByBatchId,
|
|
365
469
|
processSwitchEthereumChain,
|
|
366
470
|
processWalletSwitchChain,
|
|
367
471
|
processAddEthereumChain,
|
|
368
472
|
getProviderState,
|
|
369
473
|
aaConfig,
|
|
370
|
-
version
|
|
474
|
+
version,
|
|
475
|
+
getEthCode,
|
|
476
|
+
eip5792Config
|
|
371
477
|
} = providerHandlers;
|
|
372
478
|
const middlewares = [createScaffoldMiddleware({
|
|
373
479
|
version,
|
|
374
480
|
[PROVIDER_JRPC_METHODS.GET_PROVIDER_STATE]: getProviderState
|
|
481
|
+
}), createEip7702Middleware({
|
|
482
|
+
getEthCode,
|
|
483
|
+
processTransaction
|
|
484
|
+
}), createEip5792Middleware({
|
|
485
|
+
eip5792Config,
|
|
486
|
+
processTransaction,
|
|
487
|
+
processTransactionBatch,
|
|
488
|
+
getEthCode,
|
|
489
|
+
getTransactionByBatchId
|
|
375
490
|
}), createRequestAccountsMiddleware({
|
|
376
491
|
requestAccounts
|
|
377
492
|
}), createGetAccountsMiddleware({
|
|
@@ -410,4 +525,4 @@ function createEthereumMiddleware(providerHandlers, providerConfig, analytics) {
|
|
|
410
525
|
return mergeMiddleware(middlewares);
|
|
411
526
|
}
|
|
412
527
|
|
|
413
|
-
export { createAAMiddleware, createEthereumMiddleware, createGetAccountsMiddleware, createPendingNonceMiddleware, createPendingTxMiddleware, createProcessAddEthereumChain, createProcessEstimateUserOperationGasMiddleware, createProcessEthSignMessage, createProcessPersonalMessage, createProcessSignTransactionMiddleware, createProcessSwitchEthereumChain, createProcessTransactionMiddleware, createProcessTypedMessageV4, createProcessWalletSwitchChain, createRequestAccountsMiddleware, formatTxMetaForRpcResult };
|
|
528
|
+
export { createAAMiddleware, createEip5792Middleware, createEip7702Middleware, createEthereumMiddleware, createGetAccountsMiddleware, createPendingNonceMiddleware, createPendingTxMiddleware, createProcessAddEthereumChain, createProcessEstimateUserOperationGasMiddleware, createProcessEthSignMessage, createProcessPersonalMessage, createProcessSignTransactionMiddleware, createProcessSwitchEthereumChain, createProcessTransactionMiddleware, createProcessTypedMessageV4, createProcessWalletSwitchChain, createRequestAccountsMiddleware, formatTxMetaForRpcResult };
|
|
@@ -2,18 +2,22 @@ import _objectSpread from '@babel/runtime/helpers/objectSpread2';
|
|
|
2
2
|
import _defineProperty from '@babel/runtime/helpers/defineProperty';
|
|
3
3
|
import { addHexPrefix, stripHexPrefix } from '@ethereumjs/util';
|
|
4
4
|
import { TRANSACTION_TYPES, TX_EVENTS, TransactionStatus } from '@toruslabs/base-controllers';
|
|
5
|
-
import {
|
|
5
|
+
import { rpcErrors, providerErrors } from '@web3auth/auth';
|
|
6
6
|
import { BigNumber } from 'bignumber.js';
|
|
7
|
-
import { Transaction, keccak256 } from 'ethers';
|
|
7
|
+
import { Signature, Transaction, keccak256 } from 'ethers';
|
|
8
8
|
import log from 'loglevel';
|
|
9
|
-
import {
|
|
9
|
+
import { toHex } from 'viem';
|
|
10
|
+
import { generateEIP7702BatchTransaction } from '../Eip7702/eip7702Utils.js';
|
|
11
|
+
import { METHOD_TYPES, TRANSACTION_ENVELOPE_TYPES, CONTRACT_TYPE_ERC7821, GAS_ESTIMATE_TYPES, CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP } from '../utils/constants.js';
|
|
10
12
|
import { decGWEIToHexWEI } from '../utils/conversionUtils.js';
|
|
11
13
|
import { bnLessThan, getChainType, GAS_LIMITS } from '../utils/helpers.js';
|
|
14
|
+
import { TRANSACTION_CATEGORY_EIP7702 } from '../utils/interfaces.js';
|
|
12
15
|
import { NonceTracker } from './NonceTracker.js';
|
|
13
16
|
import { PendingTransactionTracker } from './PendingTransactionTracker.js';
|
|
14
17
|
import { TransactionGasUtil } from './TransactionGasUtil.js';
|
|
15
18
|
import { TransactionStateManager } from './TransactionStateManager.js';
|
|
16
|
-
import {
|
|
19
|
+
import { createNestedTransactionMeta, isEip7702SetCodeTx, isEip7702UpgradeWithDataToSelfTransaction, normalizeTxParameters, validateTxParameters, determineTransactionType, isEIP1559Transaction } from './TransactionUtils.js';
|
|
20
|
+
import { generateBatchId } from '../Eip5792/walletSendCalls.js';
|
|
17
21
|
|
|
18
22
|
class TransactionController extends TransactionStateManager {
|
|
19
23
|
constructor({
|
|
@@ -22,6 +26,7 @@ class TransactionController extends TransactionStateManager {
|
|
|
22
26
|
provider,
|
|
23
27
|
blockTracker,
|
|
24
28
|
signEthTx,
|
|
29
|
+
signEip7702Authorization,
|
|
25
30
|
getCurrentChainId,
|
|
26
31
|
getCurrentNetworkEIP1559Compatibility,
|
|
27
32
|
getCurrentAccountEIP1559Compatibility,
|
|
@@ -41,6 +46,7 @@ class TransactionController extends TransactionStateManager {
|
|
|
41
46
|
_defineProperty(this, "_getCurrentNetworkEIP1559Compatibility", void 0);
|
|
42
47
|
_defineProperty(this, "_getCurrentAccountEIP1559Compatibility", void 0);
|
|
43
48
|
_defineProperty(this, "signEthTx", void 0);
|
|
49
|
+
_defineProperty(this, "signEip7702Authorization", void 0);
|
|
44
50
|
_defineProperty(this, "provider", void 0);
|
|
45
51
|
_defineProperty(this, "blockTracker", void 0);
|
|
46
52
|
_defineProperty(this, "inProcessOfSigning", new Set());
|
|
@@ -52,6 +58,7 @@ class TransactionController extends TransactionStateManager {
|
|
|
52
58
|
this.getSelectedAddress = getSelectedAddress;
|
|
53
59
|
this.getEIP1559GasFeeEstimates = getEIP1559GasFeeEstimates;
|
|
54
60
|
this.signEthTx = signEthTx;
|
|
61
|
+
this.signEip7702Authorization = signEip7702Authorization;
|
|
55
62
|
this.provider = provider;
|
|
56
63
|
this.txGasUtil = new TransactionGasUtil(this.provider, this.blockTracker);
|
|
57
64
|
this.nonceTracker = new NonceTracker({
|
|
@@ -82,6 +89,57 @@ class TransactionController extends TransactionStateManager {
|
|
|
82
89
|
const txMeta = await this.createTransaction(txParams, req);
|
|
83
90
|
return this.processApproval(txMeta);
|
|
84
91
|
}
|
|
92
|
+
async addNewUnapprovedTransactionsBatch(batchRequest, req) {
|
|
93
|
+
var _batchRequest$batchId;
|
|
94
|
+
const batchRequestParams = Array.isArray(req.params) ? req.params[0] : req.params;
|
|
95
|
+
const {
|
|
96
|
+
from,
|
|
97
|
+
chainId
|
|
98
|
+
} = batchRequestParams;
|
|
99
|
+
const {
|
|
100
|
+
transactions,
|
|
101
|
+
requiredEip7702Upgrade,
|
|
102
|
+
eip7702UpgradeContractAddress: delegationAddress
|
|
103
|
+
} = batchRequest;
|
|
104
|
+
const batchId = (_batchRequest$batchId = batchRequest.batchId) !== null && _batchRequest$batchId !== void 0 ? _batchRequest$batchId : generateBatchId();
|
|
105
|
+
let txParams;
|
|
106
|
+
const txMetaOverride = {
|
|
107
|
+
batchId,
|
|
108
|
+
type: CONTRACT_TYPE_ERC7821
|
|
109
|
+
};
|
|
110
|
+
if (transactions.length === 1) {
|
|
111
|
+
// if it's single call, we can just process as a normal transaction
|
|
112
|
+
txParams = {
|
|
113
|
+
from,
|
|
114
|
+
to: transactions[0].params.to,
|
|
115
|
+
value: transactions[0].params.value,
|
|
116
|
+
data: transactions[0].params.data,
|
|
117
|
+
chainId
|
|
118
|
+
};
|
|
119
|
+
} else {
|
|
120
|
+
// process multiple transactions as a batch
|
|
121
|
+
const nestedTxMeta = await Promise.all(transactions.map(txParam => createNestedTransactionMeta(from, txParam, this.provider)));
|
|
122
|
+
const batchTransaction = generateEIP7702BatchTransaction(from, nestedTxMeta);
|
|
123
|
+
txParams = _objectSpread(_objectSpread({}, batchTransaction), {}, {
|
|
124
|
+
from
|
|
125
|
+
});
|
|
126
|
+
if (requiredEip7702Upgrade) {
|
|
127
|
+
if (!delegationAddress) {
|
|
128
|
+
throw rpcErrors.invalidParams("Delegation address is required for EIP-7702 upgrade");
|
|
129
|
+
}
|
|
130
|
+
txParams.authorizationList = [{
|
|
131
|
+
address: delegationAddress,
|
|
132
|
+
chainId: addHexPrefix(chainId)
|
|
133
|
+
}];
|
|
134
|
+
txParams.type = TRANSACTION_ENVELOPE_TYPES.SET_CODE;
|
|
135
|
+
}
|
|
136
|
+
txMetaOverride.nestedTransactions = nestedTxMeta;
|
|
137
|
+
txMetaOverride.transactionCategory = TRANSACTION_CATEGORY_EIP7702.BATCH;
|
|
138
|
+
}
|
|
139
|
+
const txMeta = await this.createTransaction(txParams, req, txMetaOverride);
|
|
140
|
+
await this.processApproval(txMeta);
|
|
141
|
+
return batchId;
|
|
142
|
+
}
|
|
85
143
|
async processApproval(txMeta) {
|
|
86
144
|
return new Promise((resolve, reject) => {
|
|
87
145
|
const handleFinished = msg => {
|
|
@@ -152,7 +210,7 @@ class TransactionController extends TransactionStateManager {
|
|
|
152
210
|
async signTransaction(txId, isSignOnly) {
|
|
153
211
|
const txMeta = this.getTransaction(txId);
|
|
154
212
|
const chainId = this.getCurrentChainId();
|
|
155
|
-
const type =
|
|
213
|
+
const type = this.getTransactionType(txMeta);
|
|
156
214
|
const txParams = {
|
|
157
215
|
type: Number.parseInt(type, 16),
|
|
158
216
|
chainId,
|
|
@@ -167,6 +225,34 @@ class TransactionController extends TransactionStateManager {
|
|
|
167
225
|
value: txMeta.transaction.value
|
|
168
226
|
};
|
|
169
227
|
const fromAddress = txMeta.transaction.from;
|
|
228
|
+
|
|
229
|
+
// Sign Authorization if present
|
|
230
|
+
if (txMeta.transaction.authorizationList) {
|
|
231
|
+
const signedAuthorizations = [];
|
|
232
|
+
txParams.authorizationList = [];
|
|
233
|
+
for (const authorization of txMeta.transaction.authorizationList) {
|
|
234
|
+
// prepare and normalize the authorization
|
|
235
|
+
const authorizationNonce = authorization.nonce ? parseInt(authorization.nonce, 16) : parseInt(txMeta.transaction.nonce, 16) + 1;
|
|
236
|
+
const normalizedAuthorization = {
|
|
237
|
+
address: authorization.address,
|
|
238
|
+
chainId: authorization.chainId,
|
|
239
|
+
nonce: toHex(authorizationNonce)
|
|
240
|
+
};
|
|
241
|
+
const signedAuthorization = await this.signEip7702Authorization(normalizedAuthorization, fromAddress);
|
|
242
|
+
signedAuthorizations.push(signedAuthorization);
|
|
243
|
+
txParams.authorizationList.push({
|
|
244
|
+
address: signedAuthorization.address,
|
|
245
|
+
chainId: BigInt(signedAuthorization.chainId),
|
|
246
|
+
nonce: BigInt(signedAuthorization.nonce),
|
|
247
|
+
signature: Signature.from({
|
|
248
|
+
r: signedAuthorization.r,
|
|
249
|
+
s: signedAuthorization.s,
|
|
250
|
+
yParity: parseInt(signedAuthorization.yParity, 16)
|
|
251
|
+
})
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
txMeta.transaction.authorizationList = signedAuthorizations;
|
|
255
|
+
}
|
|
170
256
|
const tx = Transaction.from(txParams);
|
|
171
257
|
const signedEthTx = await this.signEthTx(tx, fromAddress);
|
|
172
258
|
txMeta.r = signedEthTx.signature.r;
|
|
@@ -242,7 +328,11 @@ class TransactionController extends TransactionStateManager {
|
|
|
242
328
|
return updateTxMeta;
|
|
243
329
|
}
|
|
244
330
|
async addTxGasDefaults(txMeta) {
|
|
245
|
-
const
|
|
331
|
+
const isSetCodeTx = isEip7702SetCodeTx(txMeta);
|
|
332
|
+
|
|
333
|
+
// EIP-7702 type 4 (SET_CODE) transactions are EIP-1559 compatible
|
|
334
|
+
// They use maxFeePerGas and maxPriorityFeePerGas like type 2 transactions
|
|
335
|
+
const eip1559Compatibility = isSetCodeTx || txMeta.transaction.type !== TRANSACTION_ENVELOPE_TYPES.LEGACY && (await this.getEIP1559Compatibility());
|
|
246
336
|
const {
|
|
247
337
|
gasPrice: defaultGasPrice,
|
|
248
338
|
maxFeePerGas: defaultMaxFeePerGas,
|
|
@@ -374,13 +464,18 @@ class TransactionController extends TransactionStateManager {
|
|
|
374
464
|
};
|
|
375
465
|
}
|
|
376
466
|
async getDefaultGasLimit(txMeta) {
|
|
467
|
+
var _txMeta$nestedTransac;
|
|
377
468
|
const chainId = this.getCurrentChainId();
|
|
378
469
|
const customNetworkGasBuffer = CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP[chainId];
|
|
379
470
|
const chainType = getChainType(chainId);
|
|
471
|
+
const isSetCodeTx = isEip7702SetCodeTx(txMeta);
|
|
380
472
|
if (txMeta.transaction.gas) {
|
|
381
473
|
return {};
|
|
382
474
|
}
|
|
383
|
-
|
|
475
|
+
|
|
476
|
+
// For EIP-7702 type 4 transactions (SET_CODE), we need to estimate gas including authorization costs
|
|
477
|
+
// Simple send optimization doesn't apply to SET_CODE transactions as they involve code execution
|
|
478
|
+
if (!isSetCodeTx && txMeta.transaction.to && txMeta.transactionCategory === TRANSACTION_TYPES.SENT_ETHER && chainType !== "custom" && !txMeta.transaction.data) {
|
|
384
479
|
// This is a standard ether simple send, gas requirement is exactly 21k
|
|
385
480
|
return {
|
|
386
481
|
gasLimit: GAS_LIMITS.SIMPLE
|
|
@@ -393,13 +488,24 @@ class TransactionController extends TransactionStateManager {
|
|
|
393
488
|
} = await this.txGasUtil.analyzeGasUsage(txMeta);
|
|
394
489
|
|
|
395
490
|
// add additional gas buffer to our estimation for safety
|
|
396
|
-
|
|
491
|
+
let gasLimit;
|
|
492
|
+
if (isEip7702UpgradeWithDataToSelfTransaction(txMeta) && (_txMeta$nestedTransac = txMeta.nestedTransactions) !== null && _txMeta$nestedTransac !== void 0 && _txMeta$nestedTransac.length) {
|
|
493
|
+
// Base multiplier for EIP-7702 upgrade with data to self transactions
|
|
494
|
+
// Add additional buffer based on the number of batch calls in ERC7821 execute
|
|
495
|
+
const batchCallsCount = txMeta.nestedTransactions.length;
|
|
496
|
+
const baseMultiplier = 1.5;
|
|
497
|
+
const perCallBuffer = 0.1;
|
|
498
|
+
const eip7702UpgradeWithDataToSelfGasBufferMultiplier = baseMultiplier + perCallBuffer * batchCallsCount;
|
|
499
|
+
gasLimit = this.txGasUtil.addGasBuffer(addHexPrefix(estimatedGasHex), blockGasLimit, eip7702UpgradeWithDataToSelfGasBufferMultiplier);
|
|
500
|
+
} else {
|
|
501
|
+
gasLimit = this.txGasUtil.addGasBuffer(addHexPrefix(estimatedGasHex), blockGasLimit, customNetworkGasBuffer);
|
|
502
|
+
}
|
|
397
503
|
return {
|
|
398
504
|
gasLimit,
|
|
399
505
|
simulationFails
|
|
400
506
|
};
|
|
401
507
|
}
|
|
402
|
-
async createTransaction(txParameters, req) {
|
|
508
|
+
async createTransaction(txParameters, req, overrideTxMeta) {
|
|
403
509
|
const normalizedTxParameters = normalizeTxParameters(txParameters);
|
|
404
510
|
const eip1559Compatibility = await this.getEIP1559Compatibility(txParameters.from);
|
|
405
511
|
validateTxParameters(normalizedTxParameters, eip1559Compatibility);
|
|
@@ -411,13 +517,15 @@ class TransactionController extends TransactionStateManager {
|
|
|
411
517
|
type,
|
|
412
518
|
category,
|
|
413
519
|
methodParams
|
|
414
|
-
} = await determineTransactionType(txParameters, this.provider);
|
|
520
|
+
} = await determineTransactionType(txParameters, this.provider, overrideTxMeta);
|
|
415
521
|
txMeta.contractType = type;
|
|
416
522
|
txMeta.transactionCategory = category;
|
|
417
523
|
txMeta.methodParams = methodParams;
|
|
418
524
|
txMeta.isUserOperation = req.isUserOperation;
|
|
419
525
|
txMeta.isSignOnly = req.isSignOnly;
|
|
420
526
|
txMeta.transaction.value = txMeta.transaction.value ? addHexPrefix(txMeta.transaction.value) : "0x0";
|
|
527
|
+
txMeta.batchId = overrideTxMeta === null || overrideTxMeta === void 0 ? void 0 : overrideTxMeta.batchId;
|
|
528
|
+
txMeta.nestedTransactions = overrideTxMeta === null || overrideTxMeta === void 0 ? void 0 : overrideTxMeta.nestedTransactions;
|
|
421
529
|
this.emit(`${txMeta.id}:unapproved`, txMeta);
|
|
422
530
|
txMeta = this.addTransactionToState(txMeta);
|
|
423
531
|
txMeta = await this.addTransactionGasDefaults(txMeta);
|
|
@@ -499,6 +607,12 @@ class TransactionController extends TransactionStateManager {
|
|
|
499
607
|
if (tx.status !== TransactionStatus.failed) this.setTxStatusDropped(tx.id);
|
|
500
608
|
});
|
|
501
609
|
}
|
|
610
|
+
getTransactionType(txMeta) {
|
|
611
|
+
if (isEip7702SetCodeTx(txMeta)) {
|
|
612
|
+
return TRANSACTION_ENVELOPE_TYPES.SET_CODE;
|
|
613
|
+
}
|
|
614
|
+
return isEIP1559Transaction(txMeta) ? TRANSACTION_ENVELOPE_TYPES.FEE_MARKET : TRANSACTION_ENVELOPE_TYPES.LEGACY;
|
|
615
|
+
}
|
|
502
616
|
}
|
|
503
617
|
|
|
504
618
|
export { TransactionController };
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import _objectSpread from '@babel/runtime/helpers/objectSpread2';
|
|
1
2
|
import _defineProperty from '@babel/runtime/helpers/defineProperty';
|
|
2
3
|
import { stripHexPrefix, addHexPrefix } from '@ethereumjs/util';
|
|
3
4
|
import { cloneDeep } from '@toruslabs/base-controllers';
|
|
4
5
|
import { BN } from 'bn.js';
|
|
5
6
|
import log from 'loglevel';
|
|
7
|
+
import { DUMMY_AUTHORIZATION_SIGNATURE } from '../utils/eip7702Types.js';
|
|
8
|
+
import { isEip7702SetCodeTx } from './TransactionUtils.js';
|
|
6
9
|
|
|
7
10
|
class TransactionGasUtil {
|
|
8
11
|
constructor(provider, blockTracker) {
|
|
@@ -69,6 +72,16 @@ class TransactionGasUtil {
|
|
|
69
72
|
delete txParams.gasPrice;
|
|
70
73
|
delete txParams.maxFeePerGas;
|
|
71
74
|
delete txParams.maxPriorityFeePerGas;
|
|
75
|
+
const eip7702SetCodeTx = isEip7702SetCodeTx(txMeta);
|
|
76
|
+
if (eip7702SetCodeTx) {
|
|
77
|
+
// assign dummy signature for the gas estimation
|
|
78
|
+
txParams.authorizationList = txParams.authorizationList.map(authorization => _objectSpread(_objectSpread({}, authorization), {}, {
|
|
79
|
+
nonce: authorization.nonce || "0x1",
|
|
80
|
+
r: DUMMY_AUTHORIZATION_SIGNATURE,
|
|
81
|
+
s: DUMMY_AUTHORIZATION_SIGNATURE,
|
|
82
|
+
yParity: "0x1"
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
72
85
|
return this.provider.request({
|
|
73
86
|
method: "eth_estimateGas",
|
|
74
87
|
params: [txParams]
|
|
@@ -206,6 +206,15 @@ class TransactionStateManager extends BaseTransactionStateManager {
|
|
|
206
206
|
}
|
|
207
207
|
return filteredTransactions;
|
|
208
208
|
}
|
|
209
|
+
getTransactionByBatchId(batchId) {
|
|
210
|
+
const searchCriteria = {
|
|
211
|
+
batchId
|
|
212
|
+
};
|
|
213
|
+
const [batchTx] = this.getTransactions({
|
|
214
|
+
searchCriteria
|
|
215
|
+
});
|
|
216
|
+
return batchTx;
|
|
217
|
+
}
|
|
209
218
|
getApprovedTransactions(address) {
|
|
210
219
|
const searchCriteria = {
|
|
211
220
|
status: TransactionStatus.approved
|