From 80c29105118042a523f819d46e58d82101aa15da Mon Sep 17 00:00:00 2001 From: Daniel Rocha <68558152+danroc@users.noreply.github.com> Date: Thu, 22 Jan 2026 17:41:37 +0100 Subject: [PATCH 1/3] feat: add optional details field to Transaction type Add optional details field containing transaction origin and security alert response. The details field includes: - origin: transaction request source (e.g., 'metamask' or dapp URL) - securityAlertResponse: Security Alert API response enum (benign, warning, malicious) The implementation uses exactOptional for full backward compatibility. --- packages/keyring-api/CHANGELOG.md | 4 + .../keyring-api/src/api/transaction.test-d.ts | 99 +++++++++++++++++++ .../keyring-api/src/api/transaction.test.ts | 86 ++++++++++++++++ packages/keyring-api/src/api/transaction.ts | 72 +++++++++++++- 4 files changed, 260 insertions(+), 1 deletion(-) create mode 100644 packages/keyring-api/src/api/transaction.test.ts diff --git a/packages/keyring-api/CHANGELOG.md b/packages/keyring-api/CHANGELOG.md index c7b3cfff3..b4267001e 100644 --- a/packages/keyring-api/CHANGELOG.md +++ b/packages/keyring-api/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Add optional `details` field to `Transaction` type + - Add `SecurityAlertResponse` enum with values: `benign`, `warning`, `malicious` + - Add optional `origin` field (string) to track transaction request source + - Add optional `securityAlertResponse` field for Security Alert API responses - Add support for custom capabilities and entropy types in `KeyringV2` ([#415](https://github.com/MetaMask/accounts/pull/415)) - Add `custom` capability to `KeyringCapabilities` for keyrings with non-standard `createAccounts` method. - Add `KeyringAccountEntropyTypeOption.Custom` for custom/opaque entropy sources. diff --git a/packages/keyring-api/src/api/transaction.test-d.ts b/packages/keyring-api/src/api/transaction.test-d.ts index 0b5d8e2e2..58638bb44 100644 --- a/packages/keyring-api/src/api/transaction.test-d.ts +++ b/packages/keyring-api/src/api/transaction.test-d.ts @@ -240,3 +240,102 @@ expectNotAssignable({ }, ], }); + +// Transaction with full details (valid) +expectAssignable({ + id: 'f5d8ee39a430901c91a5917b9f2dc19d6d1a0e9cea205b009ca73dd04470b9a6', + timestamp: null, + chain: 'eip155:1', + status: 'submitted', + type: 'send', + account: '5cd17616-ea18-4d72-974f-6dbaa3c56d15', + from: [], + to: [], + fees: [], + events: [], + details: { + origin: 'https://dapp.test', + securityAlertResponse: 'benign', + }, +}); + +// Transaction with empty details object (valid) +expectAssignable({ + id: 'f5d8ee39a430901c91a5917b9f2dc19d6d1a0e9cea205b009ca73dd04470b9a6', + timestamp: null, + chain: 'eip155:1', + status: 'submitted', + type: 'send', + account: '5cd17616-ea18-4d72-974f-6dbaa3c56d15', + from: [], + to: [], + fees: [], + events: [], + details: {}, +}); + +// Transaction with only origin in details (valid) +expectAssignable({ + id: 'f5d8ee39a430901c91a5917b9f2dc19d6d1a0e9cea205b009ca73dd04470b9a6', + timestamp: null, + chain: 'eip155:1', + status: 'submitted', + type: 'send', + account: '5cd17616-ea18-4d72-974f-6dbaa3c56d15', + from: [], + to: [], + fees: [], + events: [], + details: { + origin: 'metamask', + }, +}); + +// Transaction with only securityAlertResponse in details (valid) +expectAssignable({ + id: 'f5d8ee39a430901c91a5917b9f2dc19d6d1a0e9cea205b009ca73dd04470b9a6', + timestamp: null, + chain: 'eip155:1', + status: 'submitted', + type: 'send', + account: '5cd17616-ea18-4d72-974f-6dbaa3c56d15', + from: [], + to: [], + fees: [], + events: [], + details: { + securityAlertResponse: 'warning', + }, +}); + +// Transaction with undefined details (invalid - exactOptional doesn't allow undefined) +expectNotAssignable({ + id: 'f5d8ee39a430901c91a5917b9f2dc19d6d1a0e9cea205b009ca73dd04470b9a6', + timestamp: null, + chain: 'eip155:1', + status: 'submitted', + type: 'send', + account: '5cd17616-ea18-4d72-974f-6dbaa3c56d15', + from: [], + to: [], + fees: [], + events: [], + details: undefined, +}); + +// Transaction with invalid securityAlertResponse (invalid - must be 'benign', 'warning', or 'malicious') +expectNotAssignable({ + id: 'f5d8ee39a430901c91a5917b9f2dc19d6d1a0e9cea205b009ca73dd04470b9a6', + timestamp: null, + chain: 'eip155:1', + status: 'submitted', + type: 'send', + account: '5cd17616-ea18-4d72-974f-6dbaa3c56d15', + from: [], + to: [], + fees: [], + events: [], + details: { + securityAlertResponse: 'invalid', + }, +}); diff --git a/packages/keyring-api/src/api/transaction.test.ts b/packages/keyring-api/src/api/transaction.test.ts new file mode 100644 index 000000000..5a54fae82 --- /dev/null +++ b/packages/keyring-api/src/api/transaction.test.ts @@ -0,0 +1,86 @@ +import { is } from '@metamask/superstruct'; + +import { TransactionStruct } from './transaction'; + +describe('TransactionStruct', () => { + const baseTransaction = { + id: 'f5d8ee39a430901c91a5917b9f2dc19d6d1a0e9cea205b009ca73dd04470b9a6', + chain: 'eip155:1', + account: '5cd17616-ea18-4d72-974f-6dbaa3c56d15', + status: 'confirmed', + timestamp: 1716367781, + type: 'send', + from: [], + to: [], + fees: [], + events: [], + }; + + describe('details field', () => { + it.each([ + // Without details field + { transaction: baseTransaction, expected: true }, + // With empty details + { transaction: { ...baseTransaction, details: {} }, expected: true }, + // With only origin + { + transaction: { + ...baseTransaction, + details: { origin: 'https://dapp.test' }, + }, + expected: true, + }, + // With only securityAlertResponse + { + transaction: { + ...baseTransaction, + details: { securityAlertResponse: 'benign' }, + }, + expected: true, + }, + // With both fields + { + transaction: { + ...baseTransaction, + details: { origin: 'metamask', securityAlertResponse: 'warning' }, + }, + expected: true, + }, + // All valid securityAlertResponse values + { + transaction: { + ...baseTransaction, + details: { securityAlertResponse: 'benign' }, + }, + expected: true, + }, + { + transaction: { + ...baseTransaction, + details: { securityAlertResponse: 'warning' }, + }, + expected: true, + }, + { + transaction: { + ...baseTransaction, + details: { securityAlertResponse: 'malicious' }, + }, + expected: true, + }, + // Invalid securityAlertResponse + { + transaction: { + ...baseTransaction, + details: { securityAlertResponse: 'invalid' }, + }, + expected: false, + }, + ])( + 'returns $expected for is($transaction, TransactionStruct)', + ({ transaction, expected }) => { + expect(is(transaction, TransactionStruct)).toBe(expected); + }, + ); + }); +}); diff --git a/packages/keyring-api/src/api/transaction.ts b/packages/keyring-api/src/api/transaction.ts index 3d3f38566..b64bb8b08 100644 --- a/packages/keyring-api/src/api/transaction.ts +++ b/packages/keyring-api/src/api/transaction.ts @@ -1,5 +1,5 @@ import type { InferEquals } from '@metamask/keyring-utils'; -import { object, UuidStruct } from '@metamask/keyring-utils'; +import { exactOptional, object, UuidStruct } from '@metamask/keyring-utils'; import type { Infer } from '@metamask/superstruct'; import { array, enums, nullable, number, string } from '@metamask/superstruct'; @@ -171,6 +171,67 @@ export enum TransactionType { Unknown = 'unknown', } +/** + * Security alert response values from the Security Alert API. + */ +export enum SecurityAlertResponse { + /** + * The transaction is considered safe with no detected security issues. + */ + Benign = 'benign', + + /** + * The transaction has potential security concerns that warrant user attention. + */ + Warning = 'warning', + + /** + * The transaction has been identified as malicious and should be avoided. + */ + Malicious = 'malicious', +} + +/** + * This struct represents additional transaction details. + * + * @example + * ```ts + * { + * origin: 'https://dapp.example.com', + * securityAlertResponse: 'benign', + * } + * ``` + * + * @example + * ```ts + * { + * origin: 'metamask', + * securityAlertResponse: 'warning', + * } + * ``` + */ +const TransactionDetailsStruct = object({ + /** + * Origin of the original transaction request. + * + * This can be either 'metamask' for internally initiated transactions, or a URL + * (e.g., 'https://dapp.example.com') for dapp-initiated transactions. + */ + origin: exactOptional(string()), + + /** + * Response from the Security Alert API indicating the security assessment of the + * transaction. + */ + securityAlertResponse: exactOptional( + enums([ + `${SecurityAlertResponse.Benign}`, + `${SecurityAlertResponse.Warning}`, + `${SecurityAlertResponse.Malicious}`, + ]), + ), +}); + /** * This struct represents a transaction event. */ @@ -318,6 +379,15 @@ export const TransactionStruct = object({ * all transactions. */ events: array(TransactionEventStruct), + + /** + * Additional transaction details {@see TransactionDetailsStruct}. + * + * Contains contextual information about the transaction such as its origin and + * security assessment. This field is optional and may not be present for all + * transactions. + */ + details: exactOptional(TransactionDetailsStruct), }); /** From 0c70ddaeaa97967e47d99d9e66258cc9b6763b46 Mon Sep 17 00:00:00 2001 From: Daniel Rocha <68558152+danroc@users.noreply.github.com> Date: Thu, 22 Jan 2026 17:55:28 +0100 Subject: [PATCH 2/3] docs: add PR number to CHANGELOG --- packages/keyring-api/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/keyring-api/CHANGELOG.md b/packages/keyring-api/CHANGELOG.md index b4267001e..d375af812 100644 --- a/packages/keyring-api/CHANGELOG.md +++ b/packages/keyring-api/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Add optional `details` field to `Transaction` type +- Add optional `details` field to `Transaction` type ([#445](https://github.com/MetaMask/accounts/pull/445)) - Add `SecurityAlertResponse` enum with values: `benign`, `warning`, `malicious` - Add optional `origin` field (string) to track transaction request source - Add optional `securityAlertResponse` field for Security Alert API responses From 18cacd17b965b8e1ce4c0ac5a41226c78d9007c7 Mon Sep 17 00:00:00 2001 From: Daniel Rocha <68558152+danroc@users.noreply.github.com> Date: Fri, 23 Jan 2026 14:09:50 +0100 Subject: [PATCH 3/3] style: add blank line in TransactionType enum --- packages/keyring-api/src/api/transaction.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/keyring-api/src/api/transaction.ts b/packages/keyring-api/src/api/transaction.ts index b64bb8b08..d24770c03 100644 --- a/packages/keyring-api/src/api/transaction.ts +++ b/packages/keyring-api/src/api/transaction.ts @@ -164,6 +164,7 @@ export enum TransactionType { * Represents a stake withdrawal transaction. */ StakeWithdraw = 'stake:withdraw', + /** * The transaction type is unknown. It's not possible to determine the * transaction type based on the information available.