Skip to content

Commit 590fb36

Browse files
feat: prevent external transactions to internal accounts if data included (#5418)
## Explanation Update validation preventing external transactions to internal accounts to only throw if `data` is included in the parameters. ## References ## Changelog See `CHANGELOG.md`. ## Checklist - [x] I've updated the test suite for new or updated code as appropriate - [x] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - [x] I've highlighted breaking changes using the "BREAKING" category above as appropriate - [x] I've prepared draft pull requests for clients and consumer packages to resolve any breaking changes
1 parent f9a6ac9 commit 590fb36

File tree

4 files changed

+40
-7
lines changed

4 files changed

+40
-7
lines changed

packages/transaction-controller/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Changed
11+
12+
- Prevent external transactions to internal accounts if `data` included ([#5418](https://github.com/MetaMask/core/pull/5418))
13+
1014
## [48.0.0]
1115

1216
### Changed

packages/transaction-controller/src/TransactionController.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1096,6 +1096,7 @@ export class TransactionController extends BaseController<
10961096
const internalAccounts = this.#getInternalAccounts();
10971097

10981098
await validateTransactionOrigin({
1099+
data: txParams.data,
10991100
from: txParams.from,
11001101
internalAccounts,
11011102
origin,

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

+27-5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
import { TransactionEnvelopeType, TransactionType } from '../types';
1111
import type { TransactionParams } from '../types';
1212

13+
const DATA_MOCK = '0x12345678';
1314
const FROM_MOCK = '0x1678a085c290ebd122dc42cba69373b5953b831d';
1415
const TO_MOCK = '0xfbb5595c18ca76bab52d66188e4ca50c7d95f77a';
1516
const ORIGIN_MOCK = 'test-origin';
@@ -680,9 +681,10 @@ describe('validation', () => {
680681
);
681682
});
682683

683-
it('throws if external and to is internal account', async () => {
684+
it('throws if external and to is internal account and data', async () => {
684685
await expect(
685686
validateTransactionOrigin({
687+
data: DATA_MOCK,
686688
from: FROM_MOCK,
687689
internalAccounts: [TO_MOCK],
688690
origin: ORIGIN_MOCK,
@@ -693,11 +695,33 @@ describe('validation', () => {
693695
}),
694696
).rejects.toThrow(
695697
rpcErrors.invalidParams(
696-
'External transactions to internal accounts are not supported',
698+
'External transactions to internal accounts cannot include data',
697699
),
698700
);
699701
});
700702

703+
it.each([
704+
['undefined', undefined],
705+
['empty', ''],
706+
['empty hex', '0x'],
707+
])(
708+
'does not throw if external and to is internal account but data is %s',
709+
async (_title, data) => {
710+
expect(
711+
await validateTransactionOrigin({
712+
data,
713+
from: FROM_MOCK,
714+
internalAccounts: [TO_MOCK],
715+
origin: ORIGIN_MOCK,
716+
selectedAddress: '0x123',
717+
txParams: {
718+
to: TO_MOCK,
719+
} as TransactionParams,
720+
}),
721+
).toBeUndefined();
722+
},
723+
);
724+
701725
it('does not throw if external and to is internal account but type is batch', async () => {
702726
expect(
703727
await validateTransactionOrigin({
@@ -752,9 +776,7 @@ describe('validation', () => {
752776
},
753777
}),
754778
).toThrow(
755-
rpcErrors.invalidParams(
756-
'External transactions to internal accounts are not supported',
757-
),
779+
rpcErrors.invalidParams('Calls to internal accounts are not supported'),
758780
);
759781
});
760782

packages/transaction-controller/src/utils/validation.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type GasFieldsToValidate =
2828
* Validates whether a transaction initiated by a specific 'from' address is permitted by the origin.
2929
*
3030
* @param options - Options bag.
31+
* @param options.data - The data included in the transaction.
3132
* @param options.from - The address from which the transaction is initiated.
3233
* @param options.internalAccounts - The internal accounts added to the wallet.
3334
* @param options.origin - The origin or source of the transaction.
@@ -38,6 +39,7 @@ type GasFieldsToValidate =
3839
* @throws Throws an error if the transaction is not permitted.
3940
*/
4041
export async function validateTransactionOrigin({
42+
data,
4143
from,
4244
internalAccounts,
4345
origin,
@@ -46,6 +48,7 @@ export async function validateTransactionOrigin({
4648
txParams,
4749
type,
4850
}: {
51+
data?: string;
4952
from: string;
5053
internalAccounts?: string[];
5154
origin?: string;
@@ -82,15 +85,18 @@ export async function validateTransactionOrigin({
8285
);
8386
}
8487

88+
const hasData = Boolean(data && data !== '0x');
89+
8590
if (
8691
isExternal &&
92+
hasData &&
8793
internalAccounts?.some(
8894
(account) => account.toLowerCase() === to?.toLowerCase(),
8995
) &&
9096
type !== TransactionType.batch
9197
) {
9298
throw rpcErrors.invalidParams(
93-
'External transactions to internal accounts are not supported',
99+
'External transactions to internal accounts cannot include data',
94100
);
95101
}
96102
}
@@ -279,7 +285,7 @@ export function validateBatchRequest({
279285
)
280286
) {
281287
throw rpcErrors.invalidParams(
282-
'External transactions to internal accounts are not supported',
288+
'Calls to internal accounts are not supported',
283289
);
284290
}
285291
}

0 commit comments

Comments
 (0)