@@ -856,6 +856,154 @@ describe('AssetsController', () => {
856856 } ) ;
857857 } ) ;
858858
859+ describe ( 'getAsset' , ( ) => {
860+ const metadata = {
861+ type : 'erc20' as const ,
862+ symbol : 'USDC' ,
863+ name : 'USD Coin' ,
864+ decimals : 6 ,
865+ } ;
866+
867+ it ( 'returns the combined asset with computed fiatValue' , async ( ) => {
868+ const initialState : Partial < AssetsControllerState > = {
869+ assetsInfo : { [ MOCK_ASSET_ID ] : metadata } ,
870+ assetsBalance : {
871+ [ MOCK_ACCOUNT_ID ] : { [ MOCK_ASSET_ID ] : { amount : '100' } } ,
872+ } ,
873+ assetsPrice : { [ MOCK_ASSET_ID ] : { price : 2 , lastUpdated : 123 } } ,
874+ } ;
875+
876+ await withController ( { state : initialState } , ( { controller } ) => {
877+ const asset = controller . getAsset ( MOCK_ACCOUNT_ID , MOCK_ASSET_ID ) ;
878+
879+ expect ( asset ) . toStrictEqual ( {
880+ id : MOCK_ASSET_ID ,
881+ chainId : 'eip155:1' ,
882+ balance : { amount : '100' } ,
883+ metadata,
884+ price : { price : 2 , lastUpdated : 123 } ,
885+ fiatValue : 200 ,
886+ } ) ;
887+ } ) ;
888+ } ) ;
889+
890+ it ( 'normalizes a lowercase EVM asset ID before lookup' , async ( ) => {
891+ const initialState : Partial < AssetsControllerState > = {
892+ assetsInfo : { [ MOCK_ASSET_ID ] : metadata } ,
893+ assetsBalance : {
894+ [ MOCK_ACCOUNT_ID ] : { [ MOCK_ASSET_ID ] : { amount : '1' } } ,
895+ } ,
896+ assetsPrice : { [ MOCK_ASSET_ID ] : { price : 1 , lastUpdated : 1 } } ,
897+ } ;
898+
899+ await withController ( { state : initialState } , ( { controller } ) => {
900+ const asset = controller . getAsset (
901+ MOCK_ACCOUNT_ID ,
902+ MOCK_ASSET_ID_LOWERCASE ,
903+ ) ;
904+
905+ expect ( asset ?. id ) . toBe ( MOCK_ASSET_ID ) ;
906+ } ) ;
907+ } ) ;
908+
909+ it ( 'returns undefined when the balance is missing' , async ( ) => {
910+ const initialState : Partial < AssetsControllerState > = {
911+ assetsInfo : { [ MOCK_ASSET_ID ] : metadata } ,
912+ } ;
913+
914+ await withController ( { state : initialState } , ( { controller } ) => {
915+ expect (
916+ controller . getAsset ( MOCK_ACCOUNT_ID , MOCK_ASSET_ID ) ,
917+ ) . toBeUndefined ( ) ;
918+ } ) ;
919+ } ) ;
920+
921+ it ( 'returns undefined when the metadata is missing' , async ( ) => {
922+ const initialState : Partial < AssetsControllerState > = {
923+ assetsBalance : {
924+ [ MOCK_ACCOUNT_ID ] : { [ MOCK_ASSET_ID ] : { amount : '100' } } ,
925+ } ,
926+ } ;
927+
928+ await withController ( { state : initialState } , ( { controller } ) => {
929+ expect (
930+ controller . getAsset ( MOCK_ACCOUNT_ID , MOCK_ASSET_ID ) ,
931+ ) . toBeUndefined ( ) ;
932+ } ) ;
933+ } ) ;
934+
935+ it ( 'falls back to a zero price and fiatValue when the price is missing' , async ( ) => {
936+ const initialState : Partial < AssetsControllerState > = {
937+ assetsInfo : { [ MOCK_ASSET_ID ] : metadata } ,
938+ assetsBalance : {
939+ [ MOCK_ACCOUNT_ID ] : { [ MOCK_ASSET_ID ] : { amount : '100' } } ,
940+ } ,
941+ } ;
942+
943+ await withController ( { state : initialState } , ( { controller } ) => {
944+ const asset = controller . getAsset ( MOCK_ACCOUNT_ID , MOCK_ASSET_ID ) ;
945+
946+ expect ( asset ?. price ) . toStrictEqual ( { price : 0 , lastUpdated : 0 } ) ;
947+ expect ( asset ?. fiatValue ) . toBe ( 0 ) ;
948+ } ) ;
949+ } ) ;
950+
951+ it ( 'returns undefined for a hidden asset' , async ( ) => {
952+ const initialState : Partial < AssetsControllerState > = {
953+ assetsInfo : { [ MOCK_ASSET_ID ] : metadata } ,
954+ assetsBalance : {
955+ [ MOCK_ACCOUNT_ID ] : { [ MOCK_ASSET_ID ] : { amount : '100' } } ,
956+ } ,
957+ assetPreferences : { [ MOCK_ASSET_ID ] : { hidden : true } } ,
958+ } ;
959+
960+ await withController ( { state : initialState } , ( { controller } ) => {
961+ expect (
962+ controller . getAsset ( MOCK_ACCOUNT_ID , MOCK_ASSET_ID ) ,
963+ ) . toBeUndefined ( ) ;
964+ } ) ;
965+ } ) ;
966+
967+ it ( 'throws when accountId is empty' , async ( ) => {
968+ await withController ( ( { controller } ) => {
969+ expect ( ( ) =>
970+ controller . getAsset ( '' as AccountId , MOCK_ASSET_ID ) ,
971+ ) . toThrow ( 'accountId must be a non-empty string' ) ;
972+ } ) ;
973+ } ) ;
974+
975+ it ( 'throws when assetId is not a valid CAIP-19 asset ID' , async ( ) => {
976+ await withController ( ( { controller } ) => {
977+ expect ( ( ) =>
978+ controller . getAsset (
979+ MOCK_ACCOUNT_ID ,
980+ 'not-a-caip-19' as Caip19AssetId ,
981+ ) ,
982+ ) . toThrow ( 'invalid CAIP-19 assetId' ) ;
983+ } ) ;
984+ } ) ;
985+
986+ it ( 'is exposed as the AssetsController:getAsset messenger action' , async ( ) => {
987+ const initialState : Partial < AssetsControllerState > = {
988+ assetsInfo : { [ MOCK_ASSET_ID ] : metadata } ,
989+ assetsBalance : {
990+ [ MOCK_ACCOUNT_ID ] : { [ MOCK_ASSET_ID ] : { amount : '100' } } ,
991+ } ,
992+ assetsPrice : { [ MOCK_ASSET_ID ] : { price : 2 , lastUpdated : 123 } } ,
993+ } ;
994+
995+ await withController ( { state : initialState } , ( { messenger } ) => {
996+ const asset = messenger . call (
997+ 'AssetsController:getAsset' ,
998+ MOCK_ACCOUNT_ID ,
999+ MOCK_ASSET_ID ,
1000+ ) ;
1001+
1002+ expect ( asset ?. fiatValue ) . toBe ( 200 ) ;
1003+ } ) ;
1004+ } ) ;
1005+ } ) ;
1006+
8591007 describe ( 'getAssets' , ( ) => {
8601008 it ( 'returns empty object when no balances exist' , async ( ) => {
8611009 await withController ( async ( { controller } ) => {
0 commit comments