diff --git a/packages/keyring-api/CHANGELOG.md b/packages/keyring-api/CHANGELOG.md index 87d6da0bd..16b9aa20d 100644 --- a/packages/keyring-api/CHANGELOG.md +++ b/packages/keyring-api/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add `{Balance,FungibleAssetAmountStruct}.metadata` ([#601](https://github.com/MetaMask/accounts/pull/601)) + - This can be used to pass extra-metadata alongside assets balances. + - `FungibleAssetAmountStruct` now composes `BalanceStruct` + - `notify:accountBalancesUpdated` also accepts `metadata` now. + ## [23.6.0] ### Added diff --git a/packages/keyring-api/src/api/asset.test-d.ts b/packages/keyring-api/src/api/asset.test-d.ts index f150b7210..bd162d502 100644 --- a/packages/keyring-api/src/api/asset.test-d.ts +++ b/packages/keyring-api/src/api/asset.test-d.ts @@ -9,6 +9,30 @@ expectAssignable({ amount: '0.01', }); +expectAssignable({ + fungible: true, + type: 'eip155:1/slip44:60', + unit: 'ETH', + amount: '0.01', + metadata: { foo: 'bar', count: 42, flag: true }, +}); + +expectNotAssignable({ + fungible: true, + type: 'eip155:1/slip44:60', + unit: 'ETH', + amount: '0.01', + metadata: 'string', +}); + +expectNotAssignable({ + fungible: true, + type: 'eip155:1/slip44:60', + unit: 'ETH', + amount: '0.01', + metadata: 42, +}); + expectAssignable({ fungible: false, id: 'hedera:mainnet/nft:0.0.55492/12', diff --git a/packages/keyring-api/src/api/asset.test.ts b/packages/keyring-api/src/api/asset.test.ts index 572086554..861c6ead4 100644 --- a/packages/keyring-api/src/api/asset.test.ts +++ b/packages/keyring-api/src/api/asset.test.ts @@ -36,6 +36,28 @@ describe('AssetStruct', () => { }, expected: true, }, + // Valid with metadata + { + asset: { + fungible: true, + type: 'eip155:1/slip44:60', + unit: 'ETH', + amount: '0.01', + metadata: { foo: 'bar', count: 42 }, + }, + expected: true, + }, + // Invalid metadata type + { + asset: { + fungible: true, + type: 'eip155:1/slip44:60', + unit: 'ETH', + amount: '0.01', + metadata: 'string', + }, + expected: false, + }, ])('returns $expected for is($asset, AssetStruct)', ({ asset, expected }) => { expect(is(asset, AssetStruct)).toBe(expected); }); diff --git a/packages/keyring-api/src/api/asset.ts b/packages/keyring-api/src/api/asset.ts index 04be00d22..98314f55f 100644 --- a/packages/keyring-api/src/api/asset.ts +++ b/packages/keyring-api/src/api/asset.ts @@ -1,25 +1,19 @@ -import { selectiveUnion, StringNumberStruct } from '@metamask/keyring-utils'; +import { selectiveUnion } from '@metamask/keyring-utils'; import type { Infer } from '@metamask/superstruct'; -import { literal, object, string } from '@metamask/superstruct'; +import { literal, object } from '@metamask/superstruct'; import { CaipAssetIdStruct, CaipAssetTypeStruct, isPlainObject, } from '@metamask/utils'; +import { BalanceStruct } from './balance'; + /** * Fungible asset amount struct. */ export const FungibleAssetAmountStruct = object({ - /** - * Asset unit. - */ - unit: string(), - - /** - * Asset amount. - */ - amount: StringNumberStruct, + ...BalanceStruct.schema, }); /** diff --git a/packages/keyring-api/src/api/balance.test-d.ts b/packages/keyring-api/src/api/balance.test-d.ts index ccb0cc662..47f59e689 100644 --- a/packages/keyring-api/src/api/balance.test-d.ts +++ b/packages/keyring-api/src/api/balance.test-d.ts @@ -17,3 +17,28 @@ expectNotAssignable({ amount: '1.0', unit: 1 }); expectNotAssignable({ amount: '1.0', unit: true }); expectNotAssignable({ amount: '1.0', unit: undefined }); expectNotAssignable({ amount: '1.0', unit: null }); + +// metadata is optional and accepts arbitrary JSON-compatible key/value pairs +expectAssignable({ + amount: '1.0', + unit: 'ETH', + metadata: { foo: 'bar' }, +}); +expectAssignable({ + amount: '1.0', + unit: 'ETH', + metadata: { count: 42, flag: true }, +}); +expectAssignable({ + amount: '1.0', + unit: 'ETH', + metadata: { nested: { x: 1 } }, +}); +expectAssignable({ amount: '1.0', unit: 'ETH' }); + +expectNotAssignable({ + amount: '1.0', + unit: 'ETH', + metadata: 'string', +}); +expectNotAssignable({ amount: '1.0', unit: 'ETH', metadata: 42 }); diff --git a/packages/keyring-api/src/api/balance.test.ts b/packages/keyring-api/src/api/balance.test.ts index 27ce466fb..63b7f2f69 100644 --- a/packages/keyring-api/src/api/balance.test.ts +++ b/packages/keyring-api/src/api/balance.test.ts @@ -19,6 +19,34 @@ describe('BalanceStruct', () => { { balance: { amount: '1.0', unit: 1 }, expected: false }, { balance: { amount: '1.0', unit: true }, expected: false }, { balance: { amount: '1.0', unit: null }, expected: false }, + // With metadata + { + balance: { amount: '1.0', unit: 'ETH', metadata: { foo: 'bar' } }, + expected: true, + }, + { + balance: { + amount: '1.0', + unit: 'ETH', + metadata: { count: 42, flag: true, nested: { x: 1 } }, + }, + expected: true, + }, + // Without metadata (optional — key must be absent, not explicitly undefined) + { + balance: { amount: '1.0', unit: 'ETH', metadata: undefined }, + expected: false, + }, + // Invalid metadata values + { + balance: { amount: '1.0', unit: 'ETH', metadata: 'string' }, + expected: false, + }, + { balance: { amount: '1.0', unit: 'ETH', metadata: 42 }, expected: false }, + { + balance: { amount: '1.0', unit: 'ETH', metadata: { key: undefined } }, + expected: false, + }, ])( 'returns $expected for is($balance, BalanceStruct)', ({ balance, expected }) => { diff --git a/packages/keyring-api/src/api/balance.ts b/packages/keyring-api/src/api/balance.ts index 2ab2a2989..02231a928 100644 --- a/packages/keyring-api/src/api/balance.ts +++ b/packages/keyring-api/src/api/balance.ts @@ -1,10 +1,23 @@ import { StringNumberStruct } from '@metamask/keyring-utils'; import type { Infer } from '@metamask/superstruct'; -import { object, string } from '@metamask/superstruct'; +import { exactOptional, object, record, string } from '@metamask/superstruct'; +import { JsonStruct } from '@metamask/utils'; export const BalanceStruct = object({ + /** + * Asset amount. + */ amount: StringNumberStruct, + + /** + * Asset unit. + */ unit: string(), + + /** + * Optional arbitrary metadata associated with this balance. + */ + metadata: exactOptional(record(string(), JsonStruct)), }); export type Balance = Infer;