From 835b7608e03420ddbba7344bf1ca9834f163d233 Mon Sep 17 00:00:00 2001 From: Julien Fontanel Date: Tue, 29 Sep 2026 16:52:54 +0200 Subject: [PATCH 1/2] feat(solana-wallet-snap): use the shared EstimatedChanges UI component --- eslint-suppressions.json | 15 --- packages/solana-wallet-snap/CHANGELOG.md | 2 + packages/solana-wallet-snap/jest.config.js | 8 +- .../solana-wallet-snap/snap.manifest.json | 2 +- .../components/AssetChange/AssetChange.tsx | 45 --------- .../EstimatedChanges.test.tsx | 58 +++++++++++ .../EstimatedChanges/EstimatedChanges.tsx | 96 +++++-------------- .../EstimatedChangesHeader.tsx | 22 ----- .../EstimatedChangesSkeleton.tsx | 24 ----- 9 files changed, 90 insertions(+), 182 deletions(-) delete mode 100644 packages/solana-wallet-snap/src/features/confirmation/components/AssetChange/AssetChange.tsx create mode 100644 packages/solana-wallet-snap/src/features/confirmation/components/EstimatedChanges/EstimatedChanges.test.tsx delete mode 100644 packages/solana-wallet-snap/src/features/confirmation/components/EstimatedChanges/EstimatedChangesHeader.tsx delete mode 100644 packages/solana-wallet-snap/src/features/confirmation/components/EstimatedChanges/EstimatedChangesSkeleton.tsx diff --git a/eslint-suppressions.json b/eslint-suppressions.json index 884886d8b..793459a30 100644 --- a/eslint-suppressions.json +++ b/eslint-suppressions.json @@ -795,11 +795,6 @@ "count": 1 } }, - "packages/solana-wallet-snap/src/features/confirmation/components/AssetChange/AssetChange.tsx": { - "@typescript-eslint/no-shadow": { - "count": 1 - } - }, "packages/solana-wallet-snap/src/features/confirmation/components/BasicNullableField/BasicNullableField.tsx": { "@typescript-eslint/explicit-function-return-type": { "count": 1 @@ -808,16 +803,6 @@ "count": 1 } }, - "packages/solana-wallet-snap/src/features/confirmation/components/EstimatedChanges/EstimatedChanges.tsx": { - "@typescript-eslint/no-shadow": { - "count": 1 - } - }, - "packages/solana-wallet-snap/src/features/confirmation/components/EstimatedChanges/EstimatedChangesHeader.tsx": { - "@typescript-eslint/no-shadow": { - "count": 1 - } - }, "packages/solana-wallet-snap/src/features/confirmation/components/Instruction/Instruction.tsx": { "@typescript-eslint/explicit-function-return-type": { "count": 2 diff --git a/packages/solana-wallet-snap/CHANGELOG.md b/packages/solana-wallet-snap/CHANGELOG.md index 2a15d06fb..ab0e26d4a 100644 --- a/packages/solana-wallet-snap/CHANGELOG.md +++ b/packages/solana-wallet-snap/CHANGELOG.md @@ -26,6 +26,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **BREAKING** Bump `@metamask/keyring-api` from `^23.7.0` to `^24.1.0` ([#214](https://github.com/MetaMask/internal-snaps/pull/214)) - **BREAKING** Bump `@metamask/keyring-snap-sdk` from `^9.2.1` to `^10.0.0` ([#214](https://github.com/MetaMask/internal-snaps/pull/214)) - **BREAKING** Bump `@metamask/snaps-sdk` from `^11.2.0` to `^12.0.1` ([#214](https://github.com/MetaMask/internal-snaps/pull/214)) +- Render the confirmation "Estimated changes" section with the shared `EstimatedChanges` component from `@metamask/snap-networks-utils` ([#384](https://github.com/MetaMask/internal-snaps/pull/384)) + - Estimated changes from the previous scan now stay visible while a confirmation re-scans, instead of showing a loading skeleton ### Removed diff --git a/packages/solana-wallet-snap/jest.config.js b/packages/solana-wallet-snap/jest.config.js index 12459b455..b787d50ce 100644 --- a/packages/solana-wallet-snap/jest.config.js +++ b/packages/solana-wallet-snap/jest.config.js @@ -27,10 +27,10 @@ module.exports = { coverageThreshold: { global: { - branches: 68.38, - functions: 78.28, - lines: 87.1, - statements: 87.24, + branches: 69, + functions: 79.01, + lines: 87.2, + statements: 87.37, }, }, }; diff --git a/packages/solana-wallet-snap/snap.manifest.json b/packages/solana-wallet-snap/snap.manifest.json index 576d3c81d..0b8571f8f 100644 --- a/packages/solana-wallet-snap/snap.manifest.json +++ b/packages/solana-wallet-snap/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/internal-snaps.git" }, "source": { - "shasum": "JcoWP+Q/tJLW3D8uvcA0dYPPvQhgSut5+27i+w0RNMY=", + "shasum": "urQPGAYX3VKpvLrahiRnEZXB4FZfanxpgEwTiLwKOJY=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/solana-wallet-snap/src/features/confirmation/components/AssetChange/AssetChange.tsx b/packages/solana-wallet-snap/src/features/confirmation/components/AssetChange/AssetChange.tsx deleted file mode 100644 index 390641f47..000000000 --- a/packages/solana-wallet-snap/src/features/confirmation/components/AssetChange/AssetChange.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import type { SnapComponent } from '@metamask/snaps-sdk/jsx'; -import { Box, Image, Text } from '@metamask/snaps-sdk/jsx'; - -import type { TransactionScanAssetChange } from '../../../../core/services/transaction-scan/types'; -import type { Preferences } from '../../../../core/types/snap'; -import { formatCryptoBalance } from '../../../../core/utils/formatCryptoBalance'; -import { formatFiat } from '../../../../core/utils/formatFiat'; - -export const AssetChange: SnapComponent<{ - asset: TransactionScanAssetChange; - preferences: Preferences; -}> = ({ asset, preferences }) => { - const changeType = asset.type; - - return ( - - - - {changeType === 'in' ? '+' : '-'} - {formatCryptoBalance(asset.value ?? 0, preferences.locale)} - - {asset.logo ? ( - - - - ) : null} - {asset.symbol ?? ''} - - {asset.price ? ( - - {formatFiat( - asset.price.toString(), - preferences.currency, - preferences.locale, - )} - - ) : null} - - ); -}; diff --git a/packages/solana-wallet-snap/src/features/confirmation/components/EstimatedChanges/EstimatedChanges.test.tsx b/packages/solana-wallet-snap/src/features/confirmation/components/EstimatedChanges/EstimatedChanges.test.tsx new file mode 100644 index 000000000..51bccf10f --- /dev/null +++ b/packages/solana-wallet-snap/src/features/confirmation/components/EstimatedChanges/EstimatedChanges.test.tsx @@ -0,0 +1,58 @@ +import type { TransactionScanAssetChange } from '../../../../core/services/transaction-scan/types'; +import type { Preferences } from '../../../../core/types/snap'; +import { EstimatedChanges } from './EstimatedChanges'; + +const preferences = { locale: 'en', currency: 'usd' } as Preferences; + +const sol: TransactionScanAssetChange = { + type: 'out', + value: 1.5, + price: 300, + symbol: 'SOL', + name: 'Solana', + logo: 'https://example.com/sol.png', +}; + +const render = ( + props: Partial[0]>, +): string => + JSON.stringify( + EstimatedChanges({ + changes: { assets: [sol] }, + scanStatus: 'SUCCESS', + scanFetchStatus: 'fetched', + preferences, + ...props, + }), + ); + +describe('EstimatedChanges', () => { + it('renders formatted amounts, fiat and translated labels', () => { + const serialized = render({}); + + expect(serialized).toContain('Estimated changes'); + expect(serialized).toContain('You send'); + expect(serialized).toContain('-1.5 SOL'); + expect(serialized).toContain('$300.00'); + expect(serialized).toContain(sol.logo); + }); + + it('omits fiat when the asset has no price', () => { + const serialized = render({ + changes: { assets: [{ ...sol, price: null }] }, + }); + + expect(serialized).not.toContain('$'); + }); + + it('renders not available when the scan result is an error', () => { + const serialized = render({ changes: null, scanStatus: 'ERROR' }); + + expect(serialized).toContain('Not available'); + expect(serialized).not.toContain('No changes'); + }); + + it('renders no changes when there are no estimated changes', () => { + expect(render({ changes: null })).toContain('No changes'); + }); +}); diff --git a/packages/solana-wallet-snap/src/features/confirmation/components/EstimatedChanges/EstimatedChanges.tsx b/packages/solana-wallet-snap/src/features/confirmation/components/EstimatedChanges/EstimatedChanges.tsx index 172b3ee0d..7848f5cd5 100644 --- a/packages/solana-wallet-snap/src/features/confirmation/components/EstimatedChanges/EstimatedChanges.tsx +++ b/packages/solana-wallet-snap/src/features/confirmation/components/EstimatedChanges/EstimatedChanges.tsx @@ -1,4 +1,4 @@ -import { Box, Section, Text } from '@metamask/snaps-sdk/jsx'; +import { EstimatedChanges as SharedEstimatedChanges } from '@metamask/snap-networks-utils'; import type { SnapComponent } from '@metamask/snaps-sdk/jsx'; import type { @@ -6,10 +6,9 @@ import type { TransactionScanStatus, } from '../../../../core/services/transaction-scan/types'; import type { FetchStatus, Preferences } from '../../../../core/types/snap'; +import { formatCryptoBalance } from '../../../../core/utils/formatCryptoBalance'; +import { formatFiat } from '../../../../core/utils/formatFiat'; import { i18n } from '../../../../core/utils/i18n'; -import { AssetChange } from '../AssetChange/AssetChange'; -import { EstimatedChangesHeader } from './EstimatedChangesHeader'; -import { EstimatedChangesSkeleton } from './EstimatedChangesSkeleton'; type EstimatedChangesProps = { changes: TransactionScanEstimatedChanges | null; @@ -25,74 +24,29 @@ export const EstimatedChanges: SnapComponent = ({ scanStatus, }) => { const translate = i18n(preferences.locale); - - const isFetching = scanFetchStatus === 'fetching'; - const isFetched = scanFetchStatus === 'fetched'; - const isError = scanFetchStatus === 'error'; - - if (isFetching) { - return ; - } - - if (isError || (isFetched && scanStatus === 'ERROR')) { - return ( -
- - - {translate('confirmation.estimatedChanges.notAvailable')} - -
- ); - } - - const send = changes?.assets.filter((asset) => asset.type === 'out') ?? []; - const receive = changes?.assets.filter((asset) => asset.type === 'in') ?? []; - - const hasChanges = send.length > 0 || receive.length > 0; - - if (isFetched && !hasChanges) { - return ( -
- - - {translate('confirmation.estimatedChanges.noChanges')} - -
- ); - } + const { locale, currency } = preferences; return ( -
- - {send?.length > 0 ? ( - - - {translate('confirmation.estimatedChanges.send')} - - - - {send?.map((asset) => ( - - ))} - - - - ) : null} - {null} - {receive?.length > 0 ? ( - - - {translate('confirmation.estimatedChanges.receive')} - - - - {receive?.map((asset) => ( - - ))} - - - - ) : null} -
+ ({ + type: asset.type, + value: formatCryptoBalance(asset.value ?? 0, locale), + symbol: asset.symbol, + logo: asset.logo, + fiat: asset.price + ? formatFiat(asset.price.toString(), currency, locale) + : null, + }))} + labels={{ + title: translate('confirmation.estimatedChanges'), + tooltip: translate('confirmation.estimatedChanges.tooltip'), + send: translate('confirmation.estimatedChanges.send'), + receive: translate('confirmation.estimatedChanges.receive'), + notAvailable: translate('confirmation.estimatedChanges.notAvailable'), + noChanges: translate('confirmation.estimatedChanges.noChanges'), + }} + scanFetchStatus={scanFetchStatus} + scanError={scanStatus === 'ERROR'} + /> ); }; diff --git a/packages/solana-wallet-snap/src/features/confirmation/components/EstimatedChanges/EstimatedChangesHeader.tsx b/packages/solana-wallet-snap/src/features/confirmation/components/EstimatedChanges/EstimatedChangesHeader.tsx deleted file mode 100644 index 5d1965de7..000000000 --- a/packages/solana-wallet-snap/src/features/confirmation/components/EstimatedChanges/EstimatedChangesHeader.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import type { SnapComponent } from '@metamask/snaps-sdk/jsx'; -import { Icon, Text, Tooltip, Box } from '@metamask/snaps-sdk/jsx'; - -import type { Preferences } from '../../../../core/types/snap'; -import { i18n } from '../../../../core/utils/i18n'; - -export const EstimatedChangesHeader: SnapComponent<{ - preferences: Preferences; -}> = ({ preferences }) => { - const translate = i18n(preferences.locale); - - return ( - - - {translate('confirmation.estimatedChanges')} - - - - - - ); -}; diff --git a/packages/solana-wallet-snap/src/features/confirmation/components/EstimatedChanges/EstimatedChangesSkeleton.tsx b/packages/solana-wallet-snap/src/features/confirmation/components/EstimatedChanges/EstimatedChangesSkeleton.tsx deleted file mode 100644 index 3821024fd..000000000 --- a/packages/solana-wallet-snap/src/features/confirmation/components/EstimatedChanges/EstimatedChangesSkeleton.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import type { SnapComponent } from '@metamask/snaps-sdk/jsx'; -import { Box, Section, Skeleton } from '@metamask/snaps-sdk/jsx'; - -import type { Preferences } from '../../../../core/types/snap'; -import { EstimatedChangesHeader } from './EstimatedChangesHeader'; - -export const EstimatedChangesSkeleton: SnapComponent<{ - preferences: Preferences; -}> = ({ preferences }) => { - return ( -
- - - - - - - - - - -
- ); -}; From bf7bfd876ffdb5e346b83cc71521bb68f0aa11f1 Mon Sep 17 00:00:00 2001 From: Julink Date: Tue, 29 Sep 2026 17:06:29 +0200 Subject: [PATCH 2/2] Handle null values for asset balances in EstimatedChanges Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../components/EstimatedChanges/EstimatedChanges.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/solana-wallet-snap/src/features/confirmation/components/EstimatedChanges/EstimatedChanges.tsx b/packages/solana-wallet-snap/src/features/confirmation/components/EstimatedChanges/EstimatedChanges.tsx index 7848f5cd5..78f241153 100644 --- a/packages/solana-wallet-snap/src/features/confirmation/components/EstimatedChanges/EstimatedChanges.tsx +++ b/packages/solana-wallet-snap/src/features/confirmation/components/EstimatedChanges/EstimatedChanges.tsx @@ -30,7 +30,10 @@ export const EstimatedChanges: SnapComponent = ({ ({ type: asset.type, - value: formatCryptoBalance(asset.value ?? 0, locale), + value: + asset.value === null + ? null + : formatCryptoBalance(asset.value, locale), symbol: asset.symbol, logo: asset.logo, fiat: asset.price