From 0d52e892be3f840c7914a5225e78128b453a92e5 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/5] 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 9b1b04c60..d419d76a2 100644 --- a/packages/keyring-api/CHANGELOG.md +++ b/packages/keyring-api/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- 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 - Add `EthAddressStrictStruct` struct and `EthAddressStrict` types ([#465](https://github.com/MetaMask/accounts/pull/465)) - This is a stricter variant of `EthAddressStruct` which uses `Hex` instead of `string` for its inferred type. - Add `assertCreateAccountOptionIsSupported` helper ([#464](https://github.com/MetaMask/accounts/pull/464)) 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 65305a53bfac030a6c88cd7b8c079356c2e2089e 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 2/5] style: add blank line in TransactionType enum --- packages/keyring-api/CHANGELOG.md | 7 +++++-- packages/keyring-api/src/api/transaction.ts | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/keyring-api/CHANGELOG.md b/packages/keyring-api/CHANGELOG.md index d419d76a2..0d9df7907 100644 --- a/packages/keyring-api/CHANGELOG.md +++ b/packages/keyring-api/CHANGELOG.md @@ -7,14 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [21.5.0] - ### Added - 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 + +## [21.5.0] + +### Added + - Add `EthAddressStrictStruct` struct and `EthAddressStrict` types ([#465](https://github.com/MetaMask/accounts/pull/465)) - This is a stricter variant of `EthAddressStruct` which uses `Hex` instead of `string` for its inferred type. - Add `assertCreateAccountOptionIsSupported` helper ([#464](https://github.com/MetaMask/accounts/pull/464)) 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. From 49274fcd9fb608aa0870799d5f5fa80c8fb00e50 Mon Sep 17 00:00:00 2001 From: Daniel Rocha <68558152+danroc@users.noreply.github.com> Date: Mon, 23 Feb 2026 11:29:46 +0100 Subject: [PATCH 3/5] chore: export TransactionDetailsStruct and add type alias --- packages/keyring-api/src/api/transaction.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/keyring-api/src/api/transaction.ts b/packages/keyring-api/src/api/transaction.ts index d24770c03..8248ed0cd 100644 --- a/packages/keyring-api/src/api/transaction.ts +++ b/packages/keyring-api/src/api/transaction.ts @@ -211,7 +211,7 @@ export enum SecurityAlertResponse { * } * ``` */ -const TransactionDetailsStruct = object({ +export const TransactionDetailsStruct = object({ /** * Origin of the original transaction request. * @@ -391,6 +391,13 @@ export const TransactionStruct = object({ details: exactOptional(TransactionDetailsStruct), }); +/** + * Transaction details object. + * + * See {@link TransactionDetailsStruct}. + */ +export type TransactionDetails = Infer; + /** * Transaction object. * From 378326d9472446db9e6047114b2f9e74ddb868ec Mon Sep 17 00:00:00 2001 From: Daniel Rocha <68558152+danroc@users.noreply.github.com> Date: Tue, 24 Feb 2026 14:35:39 +0100 Subject: [PATCH 4/5] fix: update SecurityAlertResponse enum values to use proper casing --- packages/keyring-api/src/api/transaction.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/keyring-api/src/api/transaction.ts b/packages/keyring-api/src/api/transaction.ts index 8248ed0cd..c78f6b838 100644 --- a/packages/keyring-api/src/api/transaction.ts +++ b/packages/keyring-api/src/api/transaction.ts @@ -179,17 +179,17 @@ export enum SecurityAlertResponse { /** * The transaction is considered safe with no detected security issues. */ - Benign = 'benign', + Benign = 'Benign', /** * The transaction has potential security concerns that warrant user attention. */ - Warning = 'warning', + Warning = 'Warning', /** * The transaction has been identified as malicious and should be avoided. */ - Malicious = 'malicious', + Malicious = 'Malicious', } /** From 4c64edebdafef70cfdb902f9dc16837d9f7f4b00 Mon Sep 17 00:00:00 2001 From: Daniel Rocha <68558152+danroc@users.noreply.github.com> Date: Tue, 24 Feb 2026 14:51:03 +0100 Subject: [PATCH 5/5] fix: update tests and examples for SecurityAlertResponse casing --- packages/keyring-api/src/api/transaction.test-d.ts | 6 +++--- packages/keyring-api/src/api/transaction.test.ts | 12 ++++++------ packages/keyring-api/src/api/transaction.ts | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/keyring-api/src/api/transaction.test-d.ts b/packages/keyring-api/src/api/transaction.test-d.ts index 58638bb44..7312cf36a 100644 --- a/packages/keyring-api/src/api/transaction.test-d.ts +++ b/packages/keyring-api/src/api/transaction.test-d.ts @@ -255,7 +255,7 @@ expectAssignable({ events: [], details: { origin: 'https://dapp.test', - securityAlertResponse: 'benign', + securityAlertResponse: 'Benign', }, }); @@ -304,7 +304,7 @@ expectAssignable({ fees: [], events: [], details: { - securityAlertResponse: 'warning', + securityAlertResponse: 'Warning', }, }); @@ -336,6 +336,6 @@ expectNotAssignable({ fees: [], events: [], details: { - securityAlertResponse: 'invalid', + securityAlertResponse: 'Invalid', }, }); diff --git a/packages/keyring-api/src/api/transaction.test.ts b/packages/keyring-api/src/api/transaction.test.ts index 5a54fae82..356bb6675 100644 --- a/packages/keyring-api/src/api/transaction.test.ts +++ b/packages/keyring-api/src/api/transaction.test.ts @@ -34,7 +34,7 @@ describe('TransactionStruct', () => { { transaction: { ...baseTransaction, - details: { securityAlertResponse: 'benign' }, + details: { securityAlertResponse: 'Benign' }, }, expected: true, }, @@ -42,7 +42,7 @@ describe('TransactionStruct', () => { { transaction: { ...baseTransaction, - details: { origin: 'metamask', securityAlertResponse: 'warning' }, + details: { origin: 'metamask', securityAlertResponse: 'Warning' }, }, expected: true, }, @@ -50,21 +50,21 @@ describe('TransactionStruct', () => { { transaction: { ...baseTransaction, - details: { securityAlertResponse: 'benign' }, + details: { securityAlertResponse: 'Benign' }, }, expected: true, }, { transaction: { ...baseTransaction, - details: { securityAlertResponse: 'warning' }, + details: { securityAlertResponse: 'Warning' }, }, expected: true, }, { transaction: { ...baseTransaction, - details: { securityAlertResponse: 'malicious' }, + details: { securityAlertResponse: 'Malicious' }, }, expected: true, }, @@ -72,7 +72,7 @@ describe('TransactionStruct', () => { { transaction: { ...baseTransaction, - details: { securityAlertResponse: 'invalid' }, + details: { securityAlertResponse: 'Invalid' }, }, expected: false, }, diff --git a/packages/keyring-api/src/api/transaction.ts b/packages/keyring-api/src/api/transaction.ts index c78f6b838..de87bc640 100644 --- a/packages/keyring-api/src/api/transaction.ts +++ b/packages/keyring-api/src/api/transaction.ts @@ -199,7 +199,7 @@ export enum SecurityAlertResponse { * ```ts * { * origin: 'https://dapp.example.com', - * securityAlertResponse: 'benign', + * securityAlertResponse: 'Benign', * } * ``` * @@ -207,7 +207,7 @@ export enum SecurityAlertResponse { * ```ts * { * origin: 'metamask', - * securityAlertResponse: 'warning', + * securityAlertResponse: 'Warning', * } * ``` */