diff --git a/packages/tron-wallet-snap/CHANGELOG.md b/packages/tron-wallet-snap/CHANGELOG.md index 78ed7a86b..60a6fa302 100644 --- a/packages/tron-wallet-snap/CHANGELOG.md +++ b/packages/tron-wallet-snap/CHANGELOG.md @@ -7,9 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Render the confirmation "Estimated changes" section with the shared `EstimatedChanges` component from `@metamask/snap-networks-utils` ([#396](https://github.com/MetaMask/internal-snaps/pull/396)) + - Estimated changes from the previous scan now stay visible while a confirmation re-scans, instead of showing a loading skeleton + ### Fixed +- Show "Estimated changes are not available" instead of "No estimated changes" when the transaction scan returns an error result, such as for a malformed transaction ([#396](https://github.com/MetaMask/internal-snaps/pull/396)) - Report the MetaMask origin as lowercase `metamask` instead of `MetaMask` for MetaMask-initiated operations, so the origin matches the value used by the other non-EVM snaps and granted to the keyring methods, and so transaction scan requests are attributed to `https://metamask.io`. The confirmation UI keeps displaying `MetaMask`. ([#392](https://github.com/MetaMask/internal-snaps/pull/392)) +- Show "Estimated changes are not available" instead of "No estimated changes" when the transaction scan returns an error result, such as for a malformed transaction ([#396](https://github.com/MetaMask/internal-snaps/pull/396)) ## [4.0.0] diff --git a/packages/tron-wallet-snap/jest.config.js b/packages/tron-wallet-snap/jest.config.js index 881de0403..947d0368e 100644 --- a/packages/tron-wallet-snap/jest.config.js +++ b/packages/tron-wallet-snap/jest.config.js @@ -19,10 +19,10 @@ module.exports = { // An object that configures minimum threshold enforcement for coverage results coverageThreshold: { global: { - branches: 72.69, - functions: 79.95, - lines: 85.85, - statements: 85.86, + branches: 72.49, + functions: 79.91, + lines: 85.79, + statements: 85.79, }, }, }; diff --git a/packages/tron-wallet-snap/snap.manifest.json b/packages/tron-wallet-snap/snap.manifest.json index 6344cb7ac..770eb56b0 100644 --- a/packages/tron-wallet-snap/snap.manifest.json +++ b/packages/tron-wallet-snap/snap.manifest.json @@ -7,7 +7,7 @@ "url": "https://github.com/MetaMask/internal-snaps.git" }, "source": { - "shasum": "L4NZ35MXuCRWHzwx4paNtKwrBWApWCqwQZ0dpp9XeoI=", + "shasum": "1IlkNeNaW28YQoE0wK3OdDJctxoEBDqK6cxLQ0GyifY=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/tron-wallet-snap/src/ui/confirmation/components/EstimatedChanges/EstimatedChanges.test.tsx b/packages/tron-wallet-snap/src/ui/confirmation/components/EstimatedChanges/EstimatedChanges.test.tsx new file mode 100644 index 000000000..c2de75528 --- /dev/null +++ b/packages/tron-wallet-snap/src/ui/confirmation/components/EstimatedChanges/EstimatedChanges.test.tsx @@ -0,0 +1,95 @@ +import type { TransactionScanAssetChange } from '../../../../services/transaction-scan/types'; +import { FetchStatus } from '../../../../types/snap'; +import type { Preferences } from '../../../../types/snap'; +import { EstimatedChanges } from './EstimatedChanges'; + +const preferences = { locale: 'en', currency: 'usd' } as Preferences; + +const trxOut: TransactionScanAssetChange = { + type: 'out', + value: '10', + price: null, + symbol: 'TRX', + name: 'Tron', + logo: 'https://example.com/trx.png', + assetType: 'native', +}; + +const usdtIn: TransactionScanAssetChange = { + ...trxOut, + type: 'in', + value: '5', + symbol: 'USDT', + logo: null, +}; + +const render = ( + props: Partial[0]>, +): string => + JSON.stringify( + EstimatedChanges({ + changes: { assets: [trxOut, usdtIn] }, + scanStatus: 'SUCCESS', + preferences, + scanFetchStatus: FetchStatus.Fetched, + ...props, + }), + ); + +describe('EstimatedChanges', () => { + it('renders send and receive rows with translated labels', () => { + const serialized = render({}); + + expect(serialized).toContain('Estimated changes'); + expect(serialized).toContain('You send'); + expect(serialized).toContain('-10 TRX'); + expect(serialized).toContain('You receive'); + expect(serialized).toContain('+5 USDT'); + expect(serialized).toContain(trxOut.logo); + }); + + it.each([FetchStatus.Loading, FetchStatus.Fetching])( + 'renders a skeleton while %s and nothing is estimated yet', + (scanFetchStatus) => { + expect(render({ changes: null, scanFetchStatus })).toContain( + '"type":"Skeleton"', + ); + }, + ); + + it('keeps previously estimated rows visible while re-fetching', () => { + const serialized = render({ scanFetchStatus: FetchStatus.Fetching }); + + expect(serialized).toContain('-10 TRX'); + expect(serialized).not.toContain('"type":"Skeleton"'); + }); + + it('renders not available when the scan fetch fails', () => { + expect( + render({ changes: null, scanFetchStatus: FetchStatus.Error }), + ).toContain('Estimated changes are not available'); + }); + + it('renders not available when the scan result is an error', () => { + const serialized = render({ + changes: { assets: [] }, + scanStatus: 'ERROR', + }); + + expect(serialized).toContain('Estimated changes are not available'); + expect(serialized).not.toContain('No estimated changes'); + }); + + it('keeps estimated rows visible when the scan result is an error', () => { + const serialized = render({ scanStatus: 'ERROR' }); + + expect(serialized).toContain('-10 TRX'); + expect(serialized).not.toContain('Estimated changes are not available'); + }); + + it('renders no changes when fetched without assets', () => { + expect(render({ changes: { assets: [] } })).toContain( + 'No estimated changes', + ); + }); +}); diff --git a/packages/tron-wallet-snap/src/ui/confirmation/components/EstimatedChanges/EstimatedChanges.tsx b/packages/tron-wallet-snap/src/ui/confirmation/components/EstimatedChanges/EstimatedChanges.tsx index 524de89a8..475b65a2b 100644 --- a/packages/tron-wallet-snap/src/ui/confirmation/components/EstimatedChanges/EstimatedChanges.tsx +++ b/packages/tron-wallet-snap/src/ui/confirmation/components/EstimatedChanges/EstimatedChanges.tsx @@ -1,15 +1,10 @@ +import { EstimatedChanges as SharedEstimatedChanges } from '@metamask/snap-networks-utils'; import type { ComponentOrElement } from '@metamask/snaps-sdk'; -import { - Box, - Section, - Text as SnapText, - Icon, - Tooltip, - Image, - Skeleton, -} from '@metamask/snaps-sdk/jsx'; -import type { TransactionScanEstimatedChanges } from '../../../../services/transaction-scan/types'; +import type { + TransactionScanEstimatedChanges, + TransactionScanStatus, +} from '../../../../services/transaction-scan/types'; import { FetchStatus } from '../../../../types/snap'; import type { Preferences } from '../../../../types/snap'; import { formatAmount } from '../../../../utils/formatAmount'; @@ -18,148 +13,39 @@ import { isFetchStatusLoadingOrFetching } from '../../../../utils/isFetchStatusL type EstimatedChangesProps = { changes: TransactionScanEstimatedChanges | null; + scanStatus: TransactionScanStatus | null; preferences: Preferences; scanFetchStatus: FetchStatus; }; -const EstimatedChangesSkeleton = ({ - preferences, -}: { - preferences: Preferences; -}): ComponentOrElement => { - const translate = i18n(preferences.locale); - - return ( -
- - - {translate('confirmation.estimatedChanges.title')} - - - - - - - - - -
- ); -}; - -const EstimatedChangesHeader = ({ - preferences, -}: { - preferences: Preferences; -}): ComponentOrElement => { - const translate = i18n(preferences.locale); - - return ( - - - {translate('confirmation.estimatedChanges.title')} - - - - - - ); -}; - -type AssetChangeProps = { - asset: TransactionScanEstimatedChanges['assets'][0]; -}; - -const AssetChange = ({ asset }: AssetChangeProps): ComponentOrElement => { - const formattedValue = formatAmount(asset.value); - const isOut = asset.type === 'out'; - - return ( - - {asset.logo ? ( - - - - ) : null} - - {isOut ? '-' : '+'} - {formattedValue} {asset.symbol} - - - ); -}; - export const EstimatedChanges = ({ changes, + scanStatus, preferences, scanFetchStatus, }: EstimatedChangesProps): ComponentOrElement => { const translate = i18n(preferences.locale); - // Keep "refreshing skeleton" for first loading + subsequent refreshes - const isFetching = isFetchStatusLoadingOrFetching(scanFetchStatus); - const isFetched = scanFetchStatus === FetchStatus.Fetched; - const isFetchError = scanFetchStatus === FetchStatus.Error; - - if (isFetching) { - return ; - } - - // API fetch error (network failure, etc.) - show "not available" - if (isFetchError) { - 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')} - -
- ); - } - return ( -
- - {send?.length > 0 ? ( - - - {translate('confirmation.estimatedChanges.send')} - - - {send?.map((asset) => ( - - ))} - - - ) : null} - {receive?.length > 0 ? ( - - - {translate('confirmation.estimatedChanges.receive')} - - - {receive?.map((asset) => ( - - ))} - - - ) : null} -
+ ({ + type: asset.type, + value: formatAmount(asset.value), + symbol: asset.symbol, + logo: asset.logo, + }))} + labels={{ + title: translate('confirmation.estimatedChanges.title'), + 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'), + }} + isFetching={isFetchStatusLoadingOrFetching(scanFetchStatus)} + isUnavailable={ + scanFetchStatus === FetchStatus.Error || scanStatus === 'ERROR' + } + /> ); }; diff --git a/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmSignTransaction/ConfirmSignTransaction.tsx b/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmSignTransaction/ConfirmSignTransaction.tsx index 3dfccc742..f80d34e85 100644 --- a/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmSignTransaction/ConfirmSignTransaction.tsx +++ b/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmSignTransaction/ConfirmSignTransaction.tsx @@ -87,6 +87,7 @@ export const ConfirmSignTransaction = ({ ); diff --git a/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmTransactionRequest/ConfirmTransactionRequest.tsx b/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmTransactionRequest/ConfirmTransactionRequest.tsx index 9d3076923..a579fe782 100644 --- a/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmTransactionRequest/ConfirmTransactionRequest.tsx +++ b/packages/tron-wallet-snap/src/ui/confirmation/views/ConfirmTransactionRequest/ConfirmTransactionRequest.tsx @@ -79,6 +79,7 @@ export const ConfirmTransactionRequest = ({ );