Skip to content

Commit efae3d4

Browse files
committed
Generate authorization list
1 parent 36f7862 commit efae3d4

File tree

5 files changed

+274
-192
lines changed

5 files changed

+274
-192
lines changed

packages/transaction-controller/src/TransactionController.test.ts

+26-15
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,12 @@ import {
7979
WalletDevice,
8080
} from './types';
8181
import { addTransactionBatch } from './utils/batch';
82-
import { DELEGATION_PREFIX, getDelegationAddress } from './utils/eip7702';
82+
import {
83+
DELEGATION_PREFIX,
84+
doesChainSupportEIP7702,
85+
getDelegationAddress,
86+
} from './utils/eip7702';
87+
import { getEIP7702UpgradeContractAddress } from './utils/feature-flags';
8388
import { addGasBuffer, estimateGas, updateGas } from './utils/gas';
8489
import { updateGasFees } from './utils/gas-fees';
8590
import { getGasFeeFlow } from './utils/gas-flow';
@@ -125,6 +130,7 @@ jest.mock('./helpers/MultichainTrackingHelper');
125130
jest.mock('./helpers/PendingTransactionTracker');
126131
jest.mock('./hooks/ExtraTransactionsPublishHook');
127132
jest.mock('./utils/batch');
133+
jest.mock('./utils/feature-flags');
128134
jest.mock('./utils/gas');
129135
jest.mock('./utils/gas-fees');
130136
jest.mock('./utils/gas-flow');
@@ -141,6 +147,7 @@ jest.mock('./helpers/ResimulateHelper', () => ({
141147
jest.mock('./utils/eip7702', () => ({
142148
...jest.requireActual('./utils/eip7702'),
143149
getDelegationAddress: jest.fn(),
150+
doesChainSupportEIP7702: jest.fn(),
144151
}));
145152

146153
// TODO: Replace `any` with type
@@ -535,6 +542,10 @@ describe('TransactionController', () => {
535542
const addTransactionBatchMock = jest.mocked(addTransactionBatch);
536543
const methodDataHelperClassMock = jest.mocked(MethodDataHelper);
537544
const getDelegationAddressMock = jest.mocked(getDelegationAddress);
545+
const doesChainSupportEIP7702Mock = jest.mocked(doesChainSupportEIP7702);
546+
const getEIP7702UpgradeContractAddressMock = jest.mocked(
547+
getEIP7702UpgradeContractAddress,
548+
);
538549

539550
let mockEthQuery: EthQuery;
540551
let getNonceLockSpy: jest.Mock;
@@ -674,6 +685,7 @@ describe('TransactionController', () => {
674685
getPermittedAccounts: async () => [ACCOUNT_MOCK],
675686
hooks: {},
676687
isEIP7702GasFeeTokensEnabled: isEIP7702GasFeeTokensEnabledMock,
688+
publicKeyEIP7702: '0x1234',
677689
sign: signMock,
678690
transactionHistoryLimit: 40,
679691
...givenOptions,
@@ -2290,13 +2302,15 @@ describe('TransactionController', () => {
22902302
);
22912303
});
22922304

2293-
it('with use7702Fees if isEIP7702GasFeeTokensEnabled returns true', async () => {
2305+
it('with use7702Fees if isEIP7702GasFeeTokensEnabled returns true and chain supports 7702', async () => {
2306+
isEIP7702GasFeeTokensEnabledMock.mockResolvedValueOnce(true);
2307+
doesChainSupportEIP7702Mock.mockReturnValueOnce(true);
2308+
getDelegationAddressMock.mockResolvedValueOnce(ACCOUNT_2_MOCK);
2309+
22942310
getSimulationDataMock.mockResolvedValueOnce(
22952311
SIMULATION_DATA_RESULT_MOCK,
22962312
);
22972313

2298-
isEIP7702GasFeeTokensEnabledMock.mockResolvedValueOnce(true);
2299-
23002314
const { controller } = setupController();
23012315

23022316
await controller.addTransaction(
@@ -2319,27 +2333,24 @@ describe('TransactionController', () => {
23192333
);
23202334
});
23212335

2322-
it('with authorizationList', async () => {
2336+
it('with authorization list if isEIP7702GasFeeTokensEnabled returns true and no delegation address', async () => {
2337+
isEIP7702GasFeeTokensEnabledMock.mockResolvedValueOnce(true);
2338+
doesChainSupportEIP7702Mock.mockReturnValueOnce(true);
2339+
23232340
getSimulationDataMock.mockResolvedValueOnce(
23242341
SIMULATION_DATA_RESULT_MOCK,
23252342
);
23262343

2344+
getEIP7702UpgradeContractAddressMock.mockReturnValueOnce(
2345+
ACCOUNT_2_MOCK,
2346+
);
2347+
23272348
const { controller } = setupController();
23282349

23292350
await controller.addTransaction(
23302351
{
23312352
from: ACCOUNT_MOCK,
23322353
to: ACCOUNT_MOCK,
2333-
authorizationList: [
2334-
{
2335-
address: ACCOUNT_2_MOCK,
2336-
chainId: CHAIN_ID_MOCK,
2337-
nonce: toHex(NONCE_MOCK),
2338-
r: '0x1',
2339-
s: '0x2',
2340-
yParity: '0x1',
2341-
},
2342-
],
23432354
},
23442355
{
23452356
networkClientId: NETWORK_CLIENT_ID_MOCK,

packages/transaction-controller/src/TransactionController.ts

+101-48
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,21 @@ import {
118118
TransactionStatus,
119119
SimulationErrorCode,
120120
} from './types';
121-
import { addTransactionBatch, isAtomicBatchSupported } from './utils/batch';
121+
import {
122+
addTransactionBatch,
123+
ERROR_MESSAGE_NO_UPGRADE_CONTRACT,
124+
isAtomicBatchSupported,
125+
} from './utils/batch';
122126
import {
123127
DELEGATION_PREFIX,
128+
doesChainSupportEIP7702,
129+
ERROR_MESSGE_PUBLIC_KEY,
124130
generateEIP7702BatchTransaction,
125131
getDelegationAddress,
126132
signAuthorizationList,
127133
} from './utils/eip7702';
128134
import { validateConfirmedExternalTransaction } from './utils/external-transactions';
135+
import { getEIP7702UpgradeContractAddress } from './utils/feature-flags';
129136
import { addGasBuffer, estimateGas, updateGas } from './utils/gas';
130137
import { updateGasFees } from './utils/gas-fees';
131138
import { getGasFeeFlow } from './utils/gas-flow';
@@ -143,6 +150,7 @@ import {
143150
} from './utils/nonce';
144151
import { prepareTransaction, serializeTransaction } from './utils/prepare';
145152
import { getTransactionParamsWithIncreasedGasFee } from './utils/retry';
153+
import type { GetSimulationDataRequest } from './utils/simulation';
146154
import { getSimulationData } from './utils/simulation';
147155
import {
148156
updatePostTransactionBalance,
@@ -3974,20 +3982,8 @@ export class TransactionController extends BaseController<
39743982
traceContext?: TraceContext;
39753983
} = {},
39763984
) {
3977-
const {
3978-
id: transactionId,
3979-
chainId,
3980-
txParams,
3981-
simulationData: prevSimulationData,
3982-
} = transactionMeta;
3983-
3984-
const {
3985-
authorizationList: authorizationListRaw,
3986-
from,
3987-
to,
3988-
value,
3989-
data,
3990-
} = txParams;
3985+
const { id: transactionId, simulationData: prevSimulationData } =
3986+
transactionMeta;
39913987

39923988
let simulationData: SimulationData = {
39933989
error: {
@@ -4000,39 +3996,11 @@ export class TransactionController extends BaseController<
40003996
let gasFeeTokens: GasFeeToken[] = [];
40013997

40023998
if (this.#isSimulationEnabled()) {
4003-
const authorizationAddress = txParams?.authorizationList?.[0]?.address;
4004-
4005-
const senderCode =
4006-
authorizationAddress &&
4007-
((DELEGATION_PREFIX + remove0x(authorizationAddress)) as Hex);
4008-
4009-
const use7702Fees =
4010-
await this.#isEIP7702GasFeeTokensEnabled(transactionMeta);
4011-
4012-
const authorizationList = authorizationListRaw?.map((authorization) => ({
4013-
address: authorization.address,
4014-
from: from as Hex,
4015-
}));
4016-
4017-
const result = await this.#trace(
4018-
{ name: 'Simulate', parentContext: traceContext },
4019-
() =>
4020-
getSimulationData(
4021-
{
4022-
authorizationList,
4023-
chainId,
4024-
data: data as Hex,
4025-
from: from as Hex,
4026-
to: to as Hex,
4027-
value: value as Hex,
4028-
},
4029-
{
4030-
blockTime,
4031-
senderCode,
4032-
use7702Fees,
4033-
},
4034-
),
4035-
);
3999+
const result = await this.#getSimulationData({
4000+
blockTime,
4001+
traceContext,
4002+
transactionMeta,
4003+
});
40364004

40374005
gasFeeTokens = result?.gasFeeTokens;
40384006
simulationData = result?.simulationData;
@@ -4264,4 +4232,89 @@ export class TransactionController extends BaseController<
42644232
newTransactionMeta,
42654233
);
42664234
}
4235+
4236+
async #getSimulationData({
4237+
blockTime,
4238+
traceContext,
4239+
transactionMeta,
4240+
}: {
4241+
blockTime?: number;
4242+
traceContext?: TraceContext;
4243+
transactionMeta: TransactionMeta;
4244+
}) {
4245+
const { chainId, delegationAddress, txParams } = transactionMeta;
4246+
const { data, from, to, value } = txParams;
4247+
const authorizationAddress = txParams?.authorizationList?.[0]?.address;
4248+
4249+
const senderCode =
4250+
authorizationAddress &&
4251+
((DELEGATION_PREFIX + remove0x(authorizationAddress)) as Hex);
4252+
4253+
const is7702GasFeeTokensEnabled =
4254+
await this.#isEIP7702GasFeeTokensEnabled(transactionMeta);
4255+
4256+
const use7702Fees =
4257+
is7702GasFeeTokensEnabled &&
4258+
doesChainSupportEIP7702(chainId, this.messagingSystem);
4259+
4260+
let authorizationList:
4261+
| GetSimulationDataRequest['authorizationList']
4262+
| undefined;
4263+
4264+
if (use7702Fees && !delegationAddress) {
4265+
authorizationList = this.#getSimulationAuthorizationList({
4266+
chainId,
4267+
from: from as Hex,
4268+
});
4269+
}
4270+
4271+
return await this.#trace(
4272+
{ name: 'Simulate', parentContext: traceContext },
4273+
() =>
4274+
getSimulationData(
4275+
{
4276+
authorizationList,
4277+
chainId,
4278+
data: data as Hex,
4279+
from: from as Hex,
4280+
to: to as Hex,
4281+
value: value as Hex,
4282+
},
4283+
{
4284+
blockTime,
4285+
senderCode,
4286+
use7702Fees,
4287+
},
4288+
),
4289+
);
4290+
}
4291+
4292+
#getSimulationAuthorizationList({
4293+
chainId,
4294+
from,
4295+
}: {
4296+
chainId: Hex;
4297+
from: Hex;
4298+
}): GetSimulationDataRequest['authorizationList'] | undefined {
4299+
if (!this.#publicKeyEIP7702) {
4300+
throw rpcErrors.internal(ERROR_MESSGE_PUBLIC_KEY);
4301+
}
4302+
4303+
const upgradeAddress = getEIP7702UpgradeContractAddress(
4304+
chainId,
4305+
this.messagingSystem,
4306+
this.#publicKeyEIP7702,
4307+
);
4308+
4309+
if (!upgradeAddress) {
4310+
throw rpcErrors.internal(ERROR_MESSAGE_NO_UPGRADE_CONTRACT);
4311+
}
4312+
4313+
return [
4314+
{
4315+
address: upgradeAddress,
4316+
from: from as Hex,
4317+
},
4318+
];
4319+
}
42674320
}

packages/transaction-controller/src/utils/batch.test.ts

+9-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import { rpcErrors } from '@metamask/rpc-errors';
22

3-
import { addTransactionBatch, isAtomicBatchSupported } from './batch';
43
import {
4+
ERROR_MESSAGE_NO_UPGRADE_CONTRACT,
5+
addTransactionBatch,
6+
isAtomicBatchSupported,
7+
} from './batch';
8+
import {
9+
ERROR_MESSGE_PUBLIC_KEY,
510
doesChainSupportEIP7702,
611
generateEIP7702BatchTransaction,
712
isAccountUpgradedToEIP7702,
@@ -371,9 +376,7 @@ describe('Batch Utils', () => {
371376

372377
await expect(
373378
addTransactionBatch({ ...request, publicKeyEIP7702: undefined }),
374-
).rejects.toThrow(
375-
rpcErrors.internal('EIP-7702 public key not specified'),
376-
);
379+
).rejects.toThrow(rpcErrors.internal(ERROR_MESSGE_PUBLIC_KEY));
377380
});
378381

379382
it('throws if account upgraded to unsupported contract', async () => {
@@ -399,7 +402,7 @@ describe('Batch Utils', () => {
399402
getEIP7702UpgradeContractAddressMock.mockReturnValueOnce(undefined);
400403

401404
await expect(addTransactionBatch(request)).rejects.toThrow(
402-
rpcErrors.internal('Upgrade contract address not found'),
405+
rpcErrors.internal(ERROR_MESSAGE_NO_UPGRADE_CONTRACT),
403406
);
404407
});
405408

@@ -1159,9 +1162,7 @@ describe('Batch Utils', () => {
11591162
messenger: MESSENGER_MOCK,
11601163
publicKeyEIP7702: undefined,
11611164
}),
1162-
).rejects.toThrow(
1163-
rpcErrors.internal('EIP-7702 public key not specified'),
1164-
);
1165+
).rejects.toThrow(rpcErrors.internal(ERROR_MESSGE_PUBLIC_KEY));
11651166
});
11661167

11671168
it('does not throw if error getting provider', async () => {

0 commit comments

Comments
 (0)