From b8855fd4aee02d2cd1a7348c8dc397f160505cc1 Mon Sep 17 00:00:00 2001 From: Douglas Daniel Date: Mon, 14 Mar 2022 16:30:30 -0600 Subject: [PATCH] feat(wallet): Multichain Support in Portfolio UI --- .../brave_wallet_provider_impl_unittest.cc | 2 +- .../brave_wallet_service_unittest.cc | 141 +++++++--- .../browser/asset_ratio_response_parser.cc | 2 +- .../asset_ratio_response_parser_unittest.cc | 4 +- .../browser/asset_ratio_service_unittest.cc | 2 +- .../browser/brave_wallet_constants.h | 38 +-- .../browser/brave_wallet_service.cc | 4 +- .../brave_wallet/common/brave_wallet.mojom | 1 + .../brave_wallet/common/eth_request_helper.cc | 2 +- .../common/eth_request_helper_unittest.cc | 2 +- .../common/value_conversion_utils_unittest.cc | 6 +- .../assets/asset-icons/sol-asset-icon.svg | 25 +- .../common/actions/wallet_actions.ts | 12 +- .../brave_wallet_ui/common/async/handlers.ts | 64 +++-- .../brave_wallet_ui/common/async/lib.ts | 261 +++++++++++++----- .../brave_wallet_ui/common/constants/mocks.ts | 12 +- .../common/hooks/assets-management.test.ts | 8 +- .../common/hooks/assets-management.ts | 9 +- .../common/hooks/assets.test.ts | 4 +- .../brave_wallet_ui/common/hooks/assets.ts | 30 +- .../brave_wallet_ui/common/hooks/balance.ts | 20 +- .../common/hooks/select-preset.test.ts | 4 +- .../common/hooks/select-preset.ts | 4 +- .../brave_wallet_ui/common/hooks/swap.test.ts | 37 ++- .../brave_wallet_ui/common/hooks/swap.ts | 3 +- .../common/hooks/transaction-parser.test.ts | 24 +- .../common/hooks/transaction-parser.ts | 2 +- .../common/reducers/wallet_reducer.ts | 60 ++-- .../accounts-assets-networks/index.tsx | 5 +- .../select-account-with-header/index.tsx | 15 +- .../components/buy-send-swap/tabs/buy-tab.tsx | 5 +- .../buy-send-swap/tabs/send-tab.tsx | 5 +- .../buy-send-swap/tabs/swap-tab.tsx | 5 +- .../desktop/asset-watchlist-item/index.tsx | 31 ++- .../edit-visible-assets-modal/index.tsx | 14 +- .../desktop/portfolio-asset-item/index.tsx | 74 ++--- .../desktop/views/accounts/index.tsx | 41 ++- .../components/desktop/views/crypto/index.tsx | 14 +- .../accounts-and-transctions-list/index.tsx | 6 +- .../components/token-lists/index.tsx | 12 +- .../desktop/views/portfolio/index.tsx | 90 +++--- .../desktop/views/portfolio/style.ts | 17 +- .../extension/assets-panel/index.tsx | 8 +- .../extension/connected-panel/index.tsx | 4 +- .../shared/create-network-icon/index.tsx | 14 +- .../shared/select-account/index.tsx | 17 +- .../shared/select-network/index.tsx | 17 +- components/brave_wallet_ui/constants/types.ts | 41 ++- .../brave_wallet_ui/options/asset-options.ts | 56 +++- .../page/async/wallet_page_async_handler.ts | 7 +- components/brave_wallet_ui/page/container.tsx | 37 ++- .../brave_wallet_ui/panel/container.tsx | 21 +- .../stories/screens/buy-send-swap.tsx | 5 +- .../stories/screens/crypto-story-view.tsx | 4 +- .../stories/wallet-concept.tsx | 7 +- .../stories/wallet-extension-panels.tsx | 22 +- .../brave_wallet_ui/utils/api-utils.test.ts | 37 +-- components/brave_wallet_ui/utils/api-utils.ts | 16 +- .../utils/network-utils.test.ts | 51 +++- .../brave_wallet_ui/utils/network-utils.ts | 33 ++- 60 files changed, 1033 insertions(+), 481 deletions(-) diff --git a/browser/brave_wallet/brave_wallet_provider_impl_unittest.cc b/browser/brave_wallet/brave_wallet_provider_impl_unittest.cc index 3b729b63f866..2845ea33a015 100644 --- a/browser/brave_wallet/brave_wallet_provider_impl_unittest.cc +++ b/browser/brave_wallet/brave_wallet_provider_impl_unittest.cc @@ -2022,7 +2022,7 @@ TEST_F(BraveWalletProviderImplUnitTest, AddSuggestToken) { mojom::BlockchainTokenPtr token = mojom::BlockchainToken::New( "0x0D8775F648430679A709E98d2b0Cb6250d2887EF", "BAT", "", true, false, - "BAT", 18, true, "", ""); + "BAT", 18, true, "", "", "0x1"); bool approved = false; mojom::ProviderError error; std::string error_message; diff --git a/browser/brave_wallet/brave_wallet_service_unittest.cc b/browser/brave_wallet/brave_wallet_service_unittest.cc index 0f928400de4b..e5b21e144037 100644 --- a/browser/brave_wallet/brave_wallet_service_unittest.cc +++ b/browser/brave_wallet/brave_wallet_service_unittest.cc @@ -45,7 +45,8 @@ const char token_list_json[] = R"( "erc20": true, "erc721": false, "symbol": "USDC", - "decimals": 6 + "decimals": 6, + "chainId": "0x1" }, "0x06012c8cf97BEaD5deAe237070F9587f8E7A266d": { "name": "Crypto Kitties", @@ -53,14 +54,16 @@ const char token_list_json[] = R"( "erc20": false, "erc721": true, "symbol": "CK", - "decimals": 0 + "decimals": 0, + "chainId": "0x1" }, "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984": { "name": "Uniswap", "logo": "uni.svg", "erc20": true, "symbol": "UNI", - "decimals": 18 + "decimals": 18, + "chainId": "0x1" } })"; @@ -263,6 +266,7 @@ class BraveWalletServiceUnitTest : public testing::Test { eth_token_->is_erc721 = false; eth_token_->decimals = 18; eth_token_->visible = true; + eth_token_->chain_id = "0x1"; bat_token_ = mojom::BlockchainToken::New(); bat_token_->contract_address = "0x0D8775F648430679A709E98d2b0Cb6250d2887EF"; @@ -273,6 +277,7 @@ class BraveWalletServiceUnitTest : public testing::Test { bat_token_->decimals = 18; bat_token_->visible = true; bat_token_->logo = "bat.png"; + bat_token_->chain_id = "0x1"; } mojom::BlockchainTokenPtr GetToken1() { return token1_.Clone(); } @@ -605,11 +610,15 @@ TEST_F(BraveWalletServiceUnitTest, GetUserAssets) { EXPECT_EQ(tokens[0], GetEthToken()); EXPECT_EQ(tokens[1], GetBatToken()); + // Create ETH token with 0x3 chain_id. + mojom::BlockchainTokenPtr eth_0x3_token = GetEthToken(); + eth_0x3_token->chain_id = "0x3"; + // ETH should be returned before any token is added. GetUserAssets("0x3", &callback_called, &tokens); EXPECT_TRUE(callback_called); EXPECT_EQ(tokens.size(), 1u); - EXPECT_EQ(tokens[0], GetEthToken()); + EXPECT_EQ(tokens[0], eth_0x3_token); // Prepare tokens to add. mojom::BlockchainTokenPtr token1 = GetToken1(); @@ -633,19 +642,31 @@ TEST_F(BraveWalletServiceUnitTest, GetUserAssets) { EXPECT_TRUE(callback_called); EXPECT_TRUE(success); + // Create Token1 with 0x1 chain_id. + mojom::BlockchainTokenPtr token1_0x1 = token1.Clone(); + token1_0x1->chain_id = "0x1"; + GetUserAssets("0x1", &callback_called, &tokens); EXPECT_TRUE(callback_called); EXPECT_EQ(tokens.size(), 3u); EXPECT_EQ(GetEthToken(), tokens[0]); EXPECT_EQ(GetBatToken(), tokens[1]); - EXPECT_EQ(token1, tokens[2]); + EXPECT_EQ(token1_0x1, tokens[2]); + + // Create Tokens with 0x4 chain_id. + mojom::BlockchainTokenPtr eth_0x4_token = GetEthToken(); + eth_0x4_token->chain_id = "0x4"; + mojom::BlockchainTokenPtr token1_0x4 = token1.Clone(); + token1_0x4->chain_id = "0x4"; + mojom::BlockchainTokenPtr token2_0x4 = token2.Clone(); + token2_0x4->chain_id = "0x4"; GetUserAssets("0x4", &callback_called, &tokens); EXPECT_TRUE(callback_called); EXPECT_EQ(tokens.size(), 3u); - EXPECT_EQ(GetEthToken(), tokens[0]); - EXPECT_EQ(token1, tokens[1]); - EXPECT_EQ(token2, tokens[2]); + EXPECT_EQ(eth_0x4_token, tokens[0]); + EXPECT_EQ(token1_0x4, tokens[1]); + EXPECT_EQ(token2_0x4, tokens[2]); // Remove token1 from "0x1" and token2 from "0x4" and test GetUserAssets. RemoveUserAsset(token1.Clone(), "0x1", &callback_called, &success); @@ -665,11 +686,12 @@ TEST_F(BraveWalletServiceUnitTest, GetUserAssets) { GetUserAssets("0x4", &callback_called, &tokens); EXPECT_TRUE(callback_called); EXPECT_EQ(tokens.size(), 2u); - EXPECT_EQ(GetEthToken(), tokens[0]); - EXPECT_EQ(token1, tokens[1]); + EXPECT_EQ(eth_0x4_token, tokens[0]); + EXPECT_EQ(token1_0x4, tokens[1]); } TEST_F(BraveWalletServiceUnitTest, DefaultAssets) { + mojom::BlockchainTokenPtr ethToken = GetEthToken(); std::vector ids = { mojom::kMainnetChainId, mojom::kRinkebyChainId, mojom::kRopstenChainId, mojom::kGoerliChainId, mojom::kKovanChainId, mojom::kLocalhostChainId}; @@ -683,8 +705,9 @@ TEST_F(BraveWalletServiceUnitTest, DefaultAssets) { EXPECT_EQ(GetEthToken(), tokens[0]); EXPECT_EQ(GetBatToken(), tokens[1]); } else { + ethToken->chain_id = id; EXPECT_EQ(tokens.size(), 1u); - EXPECT_EQ(GetEthToken(), tokens[0]); + EXPECT_EQ(ethToken, tokens[0]); } } } @@ -722,13 +745,17 @@ TEST_F(BraveWalletServiceUnitTest, AddUserAsset) { EXPECT_TRUE(callback_called); EXPECT_TRUE(success); + // Create Token1 with 0x1 chainId. + mojom::BlockchainTokenPtr token1_0x1 = GetToken1(); + token1_0x1->chain_id = "0x1"; + // Check token is added as expected. GetUserAssets("0x1", &callback_called, &tokens); EXPECT_TRUE(callback_called); EXPECT_EQ(tokens.size(), 3u); EXPECT_EQ(tokens[0], GetEthToken()); EXPECT_EQ(tokens[1], GetBatToken()); - EXPECT_EQ(tokens[2], token); + EXPECT_EQ(tokens[2], token1_0x1); // Adding token with same address in the same chain will fail. AddUserAsset(token.Clone(), "0x1", &callback_called, &success); @@ -744,12 +771,18 @@ TEST_F(BraveWalletServiceUnitTest, AddUserAsset) { EXPECT_TRUE(callback_called); EXPECT_FALSE(success); + // Create Tokens with 0x4 chain_id. + mojom::BlockchainTokenPtr eth_0x4_token = GetEthToken(); + eth_0x4_token->chain_id = "0x4"; + mojom::BlockchainTokenPtr token1_0x4 = GetToken1(); + token1_0x4->chain_id = "0x4"; + // Adding token with same address in a different chain will succeed. // And the address will be converted to checksum address. GetUserAssets("0x4", &callback_called, &tokens); EXPECT_TRUE(callback_called); EXPECT_EQ(tokens.size(), 1u); - EXPECT_EQ(tokens[0], GetEthToken()); + EXPECT_EQ(tokens[0], eth_0x4_token); AddUserAsset(token_with_unchecked_address.Clone(), "0x4", &callback_called, &success); @@ -759,13 +792,21 @@ TEST_F(BraveWalletServiceUnitTest, AddUserAsset) { GetUserAssets("0x4", &callback_called, &tokens); EXPECT_TRUE(callback_called); EXPECT_EQ(tokens.size(), 2u); - EXPECT_EQ(tokens[0], GetEthToken()); - EXPECT_EQ(tokens[1], token); + EXPECT_EQ(tokens[0], eth_0x4_token); + EXPECT_EQ(tokens[1], token1_0x4); } TEST_F(BraveWalletServiceUnitTest, RemoveUserAsset) { mojom::BlockchainTokenPtr token1 = GetToken1(); mojom::BlockchainTokenPtr token2 = GetToken2(); + mojom::BlockchainTokenPtr token1_0x1 = GetToken1(); + token1_0x1->chain_id = "0x1"; + mojom::BlockchainTokenPtr token2_0x1 = GetToken2(); + token2_0x1->chain_id = "0x1"; + mojom::BlockchainTokenPtr token2_0x4 = GetToken2(); + token2_0x4->chain_id = "0x4"; + mojom::BlockchainTokenPtr eth_0x4_token = GetEthToken(); + eth_0x4_token->chain_id = "0x4"; bool callback_called = false; bool success = false; @@ -789,14 +830,14 @@ TEST_F(BraveWalletServiceUnitTest, RemoveUserAsset) { EXPECT_EQ(tokens.size(), 4u); EXPECT_EQ(tokens[0], GetEthToken()); EXPECT_EQ(tokens[1], GetBatToken()); - EXPECT_EQ(tokens[2], token1); - EXPECT_EQ(tokens[3], token2); + EXPECT_EQ(tokens[2], token1_0x1); + EXPECT_EQ(tokens[3], token2_0x1); GetUserAssets("0x4", &callback_called, &tokens); EXPECT_TRUE(callback_called); EXPECT_EQ(tokens.size(), 2u); - EXPECT_EQ(tokens[0], GetEthToken()); - EXPECT_EQ(tokens[1], token2); + EXPECT_EQ(tokens[0], eth_0x4_token); + EXPECT_EQ(tokens[1], token2_0x4); // Remove token with invalid contract_address returns false. auto invalid_eth_token = GetEthToken().Clone(); @@ -840,12 +881,20 @@ TEST_F(BraveWalletServiceUnitTest, RemoveUserAsset) { EXPECT_TRUE(callback_called); EXPECT_EQ(tokens.size(), 2u); EXPECT_EQ(tokens[0], GetEthToken()); - EXPECT_EQ(tokens[1], token1); + EXPECT_EQ(tokens[1], token1_0x1); } TEST_F(BraveWalletServiceUnitTest, SetUserAssetVisible) { mojom::BlockchainTokenPtr token1 = GetToken1(); mojom::BlockchainTokenPtr token2 = GetToken2(); + mojom::BlockchainTokenPtr token1_0x1 = GetToken1(); + token1_0x1->chain_id = "0x1"; + mojom::BlockchainTokenPtr token2_0x1 = GetToken2(); + token2_0x1->chain_id = "0x1"; + mojom::BlockchainTokenPtr token2_0x4 = GetToken2(); + token2_0x4->chain_id = "0x4"; + mojom::BlockchainTokenPtr eth_0x4_token = GetEthToken(); + eth_0x4_token->chain_id = "0x4"; bool callback_called = false; bool success = false; @@ -869,14 +918,14 @@ TEST_F(BraveWalletServiceUnitTest, SetUserAssetVisible) { EXPECT_EQ(tokens.size(), 4u); EXPECT_EQ(tokens[0], GetEthToken()); EXPECT_EQ(tokens[1], GetBatToken()); - EXPECT_EQ(tokens[2], token1); - EXPECT_EQ(tokens[3], token2); + EXPECT_EQ(tokens[2], token1_0x1); + EXPECT_EQ(tokens[3], token2_0x1); GetUserAssets("0x4", &callback_called, &tokens); EXPECT_TRUE(callback_called); EXPECT_EQ(tokens.size(), 2u); - EXPECT_EQ(tokens[0], GetEthToken()); - EXPECT_EQ(tokens[1], token2); + EXPECT_EQ(tokens[0], eth_0x4_token); + EXPECT_EQ(tokens[1], token2_0x4); // Invalid contract_address return false. auto invalid_eth = GetEthToken(); @@ -1012,6 +1061,8 @@ TEST_F(BraveWalletServiceUnitTest, GetAndSetDefaultBaseCryptocurrency) { } TEST_F(BraveWalletServiceUnitTest, EthAddRemoveSetUserAssetVisible) { + mojom::BlockchainTokenPtr eth_0x4_token = GetEthToken(); + eth_0x4_token->chain_id = "0x4"; bool success = false; bool callback_called = false; std::vector tokens; @@ -1019,7 +1070,7 @@ TEST_F(BraveWalletServiceUnitTest, EthAddRemoveSetUserAssetVisible) { GetUserAssets("0x4", &callback_called, &tokens); EXPECT_TRUE(callback_called); EXPECT_EQ(tokens.size(), 1u); - EXPECT_EQ(GetEthToken(), tokens[0]); + EXPECT_EQ(eth_0x4_token, tokens[0]); // Add ETH again will fail. AddUserAsset(GetEthToken(), "0x4", &callback_called, &success); @@ -1060,7 +1111,7 @@ TEST_F(BraveWalletServiceUnitTest, EthAddRemoveSetUserAssetVisible) { GetUserAssets("0x4", &callback_called, &tokens); EXPECT_TRUE(callback_called); EXPECT_EQ(tokens.size(), 1u); - EXPECT_EQ(GetEthToken(), tokens[0]); + EXPECT_EQ(eth_0x4_token, tokens[0]); } TEST_F(BraveWalletServiceUnitTest, NetworkListChangedEvent) { @@ -1101,7 +1152,7 @@ TEST_F(BraveWalletServiceUnitTest, auto native_asset = mojom::BlockchainToken::New("", "Test Coin", "https://url1.com", false, - false, "TC", 11, true, "", ""); + false, "TC", 11, true, "", "", "0x5566"); bool success = false; bool callback_called = false; @@ -1185,12 +1236,19 @@ TEST_F(BraveWalletServiceUnitTest, ERC721TokenAddRemoveSetUserAssetVisible) { EXPECT_TRUE(callback_called); EXPECT_TRUE(success); + mojom::BlockchainTokenPtr erc721_token_1_0x4 = erc721_token_1.Clone(); + erc721_token_1_0x4->chain_id = "0x4"; + mojom::BlockchainTokenPtr erc721_token_2_0x4 = erc721_token_2.Clone(); + erc721_token_2_0x4->chain_id = "0x4"; + mojom::BlockchainTokenPtr eth_0x4_token = GetEthToken(); + eth_0x4_token->chain_id = "0x4"; + GetUserAssets("0x4", &callback_called, &tokens); EXPECT_TRUE(callback_called); EXPECT_EQ(tokens.size(), 3u); - EXPECT_EQ(GetEthToken(), tokens[0]); - EXPECT_EQ(erc721_token_1, tokens[1]); - EXPECT_EQ(erc721_token_2, tokens[2]); + EXPECT_EQ(eth_0x4_token, tokens[0]); + EXPECT_EQ(erc721_token_1_0x4, tokens[1]); + EXPECT_EQ(erc721_token_2_0x4, tokens[2]); SetUserAssetVisible(erc721_token_1.Clone(), "0x4", false, &callback_called, &success); @@ -1203,10 +1261,11 @@ TEST_F(BraveWalletServiceUnitTest, ERC721TokenAddRemoveSetUserAssetVisible) { auto erc721_token_1_visible_false = erc721_token_1.Clone(); erc721_token_1_visible_false->visible = false; + erc721_token_1_visible_false->chain_id = "0x4"; GetUserAssets("0x4", &callback_called, &tokens); EXPECT_TRUE(callback_called); EXPECT_EQ(tokens.size(), 2u); - EXPECT_EQ(GetEthToken(), tokens[0]); + EXPECT_EQ(eth_0x4_token, tokens[0]); EXPECT_EQ(erc721_token_1_visible_false, tokens[1]); } @@ -1506,24 +1565,24 @@ TEST_F(BraveWalletServiceUnitTest, AddSuggestToken) { mojom::BlockchainTokenPtr usdc_from_blockchain_registry = mojom::BlockchainToken::New( "0x6B175474E89094C44Da98b954EedeAC495271d0F", "USD Coin", - "usdc.png", true, false, "USDC", 6, true, "", ""); + "usdc.png", true, false, "USDC", 6, true, "", "", ""); ASSERT_EQ(usdc_from_blockchain_registry, GetRegistry()->GetTokenByContract( chain_id, "0x6B175474E89094C44Da98b954EedeAC495271d0F")); mojom::BlockchainTokenPtr usdc_from_user_assets = mojom::BlockchainToken::New( "0x6B175474E89094C44Da98b954EedeAC495271d0F", "USD Coin", "", true, - false, "USDC", 6, true, "", ""); + false, "USDC", 6, true, "", "", ""); ASSERT_TRUE( service_->AddUserAsset(usdc_from_user_assets.Clone(), chain_id)); mojom::BlockchainTokenPtr usdc_from_request = mojom::BlockchainToken::New( "0x6B175474E89094C44Da98b954EedeAC495271d0F", "USDC", "", true, false, - "USDC", 6, true, "", ""); + "USDC", 6, true, "", "", ""); mojom::BlockchainTokenPtr custom_token = mojom::BlockchainToken::New( "0x6b175474e89094C44Da98b954eEdeAC495271d1e", "COLOR", "", true, false, - "COLOR", 18, true, "", ""); + "COLOR", 18, true, "", "", ""); // Case 1: Suggested token does not exist (no entry with the same contract // address) in BlockchainRegistry nor user assets. @@ -1577,13 +1636,13 @@ TEST_F(BraveWalletServiceUnitTest, AddSuggestToken) { mojom::BlockchainTokenPtr usdt_from_user_assets = mojom::BlockchainToken::New( "0xdAC17F958D2ee523a2206206994597C13D831ec7", "Tether", "usdt.png", - true, false, "USDT", 6, true, "", ""); + true, false, "USDT", 6, true, "", "", ""); ASSERT_TRUE( service_->AddUserAsset(usdt_from_user_assets.Clone(), chain_id)); mojom::BlockchainTokenPtr usdt_from_request = mojom::BlockchainToken::New( "0xdAC17F958D2ee523a2206206994597C13D831ec7", "USDT", "", true, false, - "USDT", 18, true, "", ""); + "USDT", 18, true, "", "", ""); // Case 5: Suggested token exists in user asset list and is visible, does // not exist in BlockchainRegistry. Token should be in user asset list and // is visible, and the data should be the same as the one in user asset @@ -1616,14 +1675,14 @@ TEST_F(BraveWalletServiceUnitTest, AddSuggestToken) { // kUserRejectedRequest error. mojom::BlockchainTokenPtr busd = mojom::BlockchainToken::New( "0x4Fabb145d64652a948d72533023f6E7A623C7C53", "Binance USD", "", true, - false, "BUSD", 18, true, "", ""); + false, "BUSD", 18, true, "", "", ""); AddSuggestToken(busd.Clone(), busd.Clone(), false, true /* run_switch_network */); // Test reject request. mojom::BlockchainTokenPtr brb_from_request = mojom::BlockchainToken::New( "0x6B175474E89094C44Da98b954EedeAC495271d0A", "BRB", "", true, false, - "BRB", 6, true, "", ""); + "BRB", 6, true, "", "", ""); ASSERT_TRUE(service_->RemoveUserAsset(brb_from_request.Clone(), chain_id)); AddSuggestToken(brb_from_request.Clone(), brb_from_request.Clone(), false); token = service_->GetUserAsset(brb_from_request->contract_address, @@ -1636,7 +1695,7 @@ TEST_F(BraveWalletServiceUnitTest, AddSuggestToken) { TEST_F(BraveWalletServiceUnitTest, GetUserAsset) { mojom::BlockchainTokenPtr usdc = mojom::BlockchainToken::New( "0x6B175474E89094C44Da98b954EedeAC495271d0F", "USD Coin", "usdc.png", - true, false, "USDC", 6, true, "", ""); + true, false, "USDC", 6, true, "", "", ""); ASSERT_TRUE(service_->AddUserAsset(usdc.Clone(), mojom::kRopstenChainId)); EXPECT_EQ(usdc, service_->GetUserAsset(usdc->contract_address, usdc->token_id, @@ -1684,7 +1743,7 @@ TEST_F(BraveWalletServiceUnitTest, Reset) { [](bool, const std::string&, const std::string&) {})); mojom::BlockchainTokenPtr custom_token = mojom::BlockchainToken::New( "0x6b175474e89094C44Da98b954eEdeAC495271d1e", "COLOR", "", true, false, - "COLOR", 18, true, "", ""); + "COLOR", 18, true, "", "", ""); AddSuggestToken(custom_token.Clone(), custom_token.Clone(), true); service_->Reset(); diff --git a/components/brave_wallet/browser/asset_ratio_response_parser.cc b/components/brave_wallet/browser/asset_ratio_response_parser.cc index 59785240574f..401a17ff1fee 100644 --- a/components/brave_wallet/browser/asset_ratio_response_parser.cc +++ b/components/brave_wallet/browser/asset_ratio_response_parser.cc @@ -419,7 +419,7 @@ mojom::BlockchainTokenPtr ParseTokenInfo(const std::string& json) { return mojom::BlockchainToken::New(eth_addr.ToChecksumAddress(), *name, "" /* logo */, is_erc20, is_erc721, - *symbol, decimals, true, "", ""); + *symbol, decimals, true, "", "", ""); } } // namespace brave_wallet diff --git a/components/brave_wallet/browser/asset_ratio_response_parser_unittest.cc b/components/brave_wallet/browser/asset_ratio_response_parser_unittest.cc index 5aacb6fc88e1..da82657e3ad0 100644 --- a/components/brave_wallet/browser/asset_ratio_response_parser_unittest.cc +++ b/components/brave_wallet/browser/asset_ratio_response_parser_unittest.cc @@ -261,7 +261,7 @@ TEST(AssetRatioResponseParserUnitTest, ParseGetTokenInfo) { mojom::BlockchainTokenPtr expected_token = mojom::BlockchainToken::New( "0xdAC17F958D2ee523a2206206994597C13D831ec7", "Tether USD", "", true, - false, "USDT", 6, true, "", ""); + false, "USDT", 6, true, "", "", ""); EXPECT_EQ(ParseTokenInfo(json), expected_token); // ERC721 @@ -283,7 +283,7 @@ TEST(AssetRatioResponseParserUnitTest, ParseGetTokenInfo) { )"); expected_token = mojom::BlockchainToken::New( "0x0E3A2A1f2146d86A604adc220b4967A898D7Fe07", "Gods Unchained Cards", "", - false, true, "CARD", 0, true, "", ""); + false, true, "CARD", 0, true, "", "", ""); EXPECT_EQ(ParseTokenInfo(json), expected_token); const std::string valid_json = (R"( diff --git a/components/brave_wallet/browser/asset_ratio_service_unittest.cc b/components/brave_wallet/browser/asset_ratio_service_unittest.cc index b7cb0c1048c2..38bfcfa15255 100644 --- a/components/brave_wallet/browser/asset_ratio_service_unittest.cc +++ b/components/brave_wallet/browser/asset_ratio_service_unittest.cc @@ -418,7 +418,7 @@ TEST_F(AssetRatioServiceUnitTest, GetTokenInfo) { GetTokenInfo("0xdac17f958d2ee523a2206206994597c13d831ec7", mojom::BlockchainToken::New( "0xdAC17F958D2ee523a2206206994597C13D831ec7", "Tether USD", - "", true, false, "USDT", 6, true, "", "")); + "", true, false, "USDT", 6, true, "", "", "")); SetInterceptor("unexpected response"); GetTokenInfo("0xdac17f958d2ee523a2206206994597c13d831ec7", nullptr); diff --git a/components/brave_wallet/browser/brave_wallet_constants.h b/components/brave_wallet/browser/brave_wallet_constants.h index 95e0888d6aff..ffb9b918d6f4 100644 --- a/components/brave_wallet/browser/brave_wallet_constants.h +++ b/components/brave_wallet/browser/brave_wallet_constants.h @@ -29,42 +29,42 @@ constexpr int32_t kAutoLockMinutesMax = 10080; // List of assets from Wyre, available to buy static base::NoDestructor> kBuyTokens( {{"0x0D8775F648430679A709E98d2b0Cb6250d2887EF", "Basic Attention Token", - "bat.png", true, false, "BAT", 18, true, "", ""}, - {"", "Ethereum", "", false, false, "ETH", 18, true, "", ""}, + "bat.png", true, false, "BAT", 18, true, "", "", ""}, + {"", "Ethereum", "", false, false, "ETH", 18, true, "", "", ""}, {"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", "USD Coin", "usdc.png", - true, false, "USDC", 6, true, "", ""}, + true, false, "USDC", 6, true, "", "", ""}, {"0x6B175474E89094C44Da98b954EedeAC495271d0F", "DAI", "dai.png", true, - false, "DAI", 18, true, "", ""}, + false, "DAI", 18, true, "", "", ""}, {"0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9", "AAVE", "AAVE.png", true, - false, "AAVE", 18, true, "", ""}, + false, "AAVE", 18, true, "", "", ""}, {"0x4Fabb145d64652a948d72533023f6E7A623C7C53", "Binance USD", "busd.png", - true, false, "BUSD", 18, true, "", ""}, + true, false, "BUSD", 18, true, "", "", ""}, {"0xc00e94Cb662C3520282E6f5717214004A7f26888", "Compound", "comp.png", - true, false, "Comp", 18, true, "", ""}, + true, false, "Comp", 18, true, "", "", ""}, {"0xD533a949740bb3306d119CC777fa900bA034cd52", "Curve", "curve.png", true, - false, "CRV", 18, true, "", ""}, + false, "CRV", 18, true, "", "", ""}, {"0x056Fd409E1d7A124BD7017459dFEa2F387b6d5Cd", "Gemini Dollar", "gusd.png", - true, false, "GUSD", 2, true, "", ""}, + true, false, "GUSD", 2, true, "", "", ""}, {"0x514910771AF9Ca656af840dff83E8264EcF986CA", "Chainlink", - "chainlink.png", true, false, "LINK", 18, true, "", ""}, + "chainlink.png", true, false, "LINK", 18, true, "", "", ""}, {"0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2", "Maker", "mkr.png", true, - false, "MKR", 18, true, "", ""}, + false, "MKR", 18, true, "", "", ""}, {"0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F", "Synthetix", - "synthetix.png", true, false, "SNX", 18, true, "", ""}, + "synthetix.png", true, false, "SNX", 18, true, "", "", ""}, {"0x04Fa0d235C4abf4BcF4787aF4CF447DE572eF828", "UMA", "UMA.png", true, - false, "UMA", 18, true, "", ""}, + false, "UMA", 18, true, "", "", ""}, {"0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984", "Uniswap", "uni.png", true, - false, "UNI", 18, true, "", ""}, + false, "UNI", 18, true, "", "", ""}, {"0xA4Bdb11dc0a2bEC88d24A3aa1E6Bb17201112eBe", "Stably Dollar", "usds.png", - true, false, "USDS", 6, true, "", ""}, + true, false, "USDS", 6, true, "", "", ""}, {"0xdAC17F958D2ee523a2206206994597C13D831ec7", "Tether", "usdt.png", true, - false, "USDT", 6, true, "", ""}, + false, "USDT", 6, true, "", "", ""}, {"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", "Wrapped Bitcoin", - "wbtc.png", true, false, "WBTC", 8, true, "", ""}, + "wbtc.png", true, false, "WBTC", 8, true, "", "", ""}, {"0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e", "Year.Finance", "yfi.png", - true, false, "YFI", 18, true, "", ""}, + true, false, "YFI", 18, true, "", "", ""}, {"0x9043d4d51C9d2e31e3F169de4551E416970c27Ef", "Palm DAI", "pdai.png", - true, false, "PDAI", 18, true, "", ""}}); + true, false, "PDAI", 18, true, "", "", ""}}); const char kWalletBaseDirectory[] = "BraveWallet"; const char kImageSourceHost[] = "erc-token-images"; diff --git a/components/brave_wallet/browser/brave_wallet_service.cc b/components/brave_wallet/browser/brave_wallet_service.cc index d63aba7f54ff..c0eb8aa3e49b 100644 --- a/components/brave_wallet/browser/brave_wallet_service.cc +++ b/components/brave_wallet/browser/brave_wallet_service.cc @@ -222,8 +222,10 @@ void BraveWalletService::GetUserAssets(const std::string& chain_id, std::vector result; for (const auto& token : tokens->GetList()) { mojom::BlockchainTokenPtr tokenPtr = ValueToBlockchainToken(token); - if (tokenPtr) + if (tokenPtr) { + tokenPtr->chain_id = chain_id; result.push_back(std::move(tokenPtr)); + } } std::move(callback).Run(std::move(result)); diff --git a/components/brave_wallet/common/brave_wallet.mojom b/components/brave_wallet/common/brave_wallet.mojom index bee4c752b542..eb7fbcf20214 100644 --- a/components/brave_wallet/common/brave_wallet.mojom +++ b/components/brave_wallet/common/brave_wallet.mojom @@ -301,6 +301,7 @@ struct BlockchainToken { bool visible = true; string token_id = ""; // Non-empty for ERC721 tokens. string coingecko_id = ""; + string chain_id; }; // WebUI-side handler for requests from the browser. diff --git a/components/brave_wallet/common/eth_request_helper.cc b/components/brave_wallet/common/eth_request_helper.cc index f1338cb1eeca..4607a7703468 100644 --- a/components/brave_wallet/common/eth_request_helper.cc +++ b/components/brave_wallet/common/eth_request_helper.cc @@ -550,7 +550,7 @@ bool ParseWalletWatchAssetParams(const std::string& json, *token = mojom::BlockchainToken::New(eth_addr.ToChecksumAddress(), *symbol /* name */, logo, true, false, - *symbol, decimals, true, "", ""); + *symbol, decimals, true, "", "", ""); return true; } diff --git a/components/brave_wallet/common/eth_request_helper_unittest.cc b/components/brave_wallet/common/eth_request_helper_unittest.cc index fdbc8e1a61bd..7b67872d9f18 100644 --- a/components/brave_wallet/common/eth_request_helper_unittest.cc +++ b/components/brave_wallet/common/eth_request_helper_unittest.cc @@ -749,7 +749,7 @@ TEST(EthRequestHelperUnitTest, ParseWalletWatchAssetParams) { mojom::BlockchainTokenPtr expected_token = mojom::BlockchainToken::New( "0x0D8775F648430679A709E98d2b0Cb6250d2887EF", "BAT", - "https://test.com/test.png", true, false, "BAT", 18, true, "", ""); + "https://test.com/test.png", true, false, "BAT", 18, true, "", "", ""); mojom::BlockchainTokenPtr token; std::string error_message; diff --git a/components/brave_wallet/common/value_conversion_utils_unittest.cc b/components/brave_wallet/common/value_conversion_utils_unittest.cc index 51e1871c6854..b4937ea267e7 100644 --- a/components/brave_wallet/common/value_conversion_utils_unittest.cc +++ b/components/brave_wallet/common/value_conversion_utils_unittest.cc @@ -154,13 +154,14 @@ TEST(ValueConversionUtilsUnitTest, ValueToBlockchainToken) { "decimals": 18, "visible": true, "token_id": "", - "coingecko_id": "" + "coingecko_id": "", + "chain_id": "" })"); ASSERT_TRUE(json_value); mojom::BlockchainTokenPtr expected_token = mojom::BlockchainToken::New( "0x0D8775F648430679A709E98d2b0Cb6250d2887EF", "Basic Attention Token", - "bat.png", true, false, "BAT", 18, true, "", ""); + "bat.png", true, false, "BAT", 18, true, "", "", ""); mojom::BlockchainTokenPtr token = ValueToBlockchainToken(json_value.value()); EXPECT_EQ(token, expected_token); @@ -175,6 +176,7 @@ TEST(ValueConversionUtilsUnitTest, ValueToBlockchainToken) { optional_value.RemoveKey("logo"); optional_value.RemoveKey("token_id"); optional_value.RemoveKey("coingecko_id"); + optional_value.RemoveKey("chain_id"); expected_token->logo = ""; token = ValueToBlockchainToken(optional_value); EXPECT_EQ(token, expected_token); diff --git a/components/brave_wallet_ui/assets/asset-icons/sol-asset-icon.svg b/components/brave_wallet_ui/assets/asset-icons/sol-asset-icon.svg index a7707104fc47..79e92671eefa 100644 --- a/components/brave_wallet_ui/assets/asset-icons/sol-asset-icon.svg +++ b/components/brave_wallet_ui/assets/asset-icons/sol-asset-icon.svg @@ -1,24 +1 @@ - - - - - - - - - - - - - - - - - - - - - - - - +solana-logo-freelogovectors.net_ \ No newline at end of file diff --git a/components/brave_wallet_ui/common/actions/wallet_actions.ts b/components/brave_wallet_ui/common/actions/wallet_actions.ts index 94b17f6f4ac1..2053ad7554ad 100644 --- a/components/brave_wallet_ui/common/actions/wallet_actions.ts +++ b/components/brave_wallet_ui/common/actions/wallet_actions.ts @@ -29,9 +29,7 @@ import { import { BraveWallet, WalletAccountType, - GetAllNetworksList, - GetAllTokensReturnInfo, - GetNativeAssetBalancesReturnInfo, + GetNativeAssetBalancesPayload, GetBlockchainTokenBalanceReturnInfo, PortfolioTokenHistoryAndInfo, SendTransactionParams, @@ -61,7 +59,7 @@ export const selectAccount = createAction('selectAccount') export const selectNetwork = createAction('selectNetwork') export const setNetwork = createAction('setNetwork') export const getAllNetworks = createAction('getAllNetworks') -export const setAllNetworks = createAction('getAllNetworks') +export const setAllNetworks = createAction('getAllNetworks') export const chainChangedEvent = createAction('chainChangedEvent') export const isEip1559Changed = createAction('isEip1559Changed') export const keyringCreated = createAction('keyringCreated') @@ -72,9 +70,9 @@ export const unlocked = createAction('unlocked') export const backedUp = createAction('backedUp') export const accountsChanged = createAction('accountsChanged') export const selectedAccountChanged = createAction('selectedAccountChanged') -export const setAllTokensList = createAction('setAllTokensList') +export const setAllTokensList = createAction('setAllTokensList') export const getAllTokensList = createAction('getAllTokensList') -export const nativeAssetBalancesUpdated = createAction('nativeAssetBalancesUpdated') +export const nativeAssetBalancesUpdated = createAction('nativeAssetBalancesUpdated') export const tokenBalancesUpdated = createAction('tokenBalancesUpdated') export const pricesUpdated = createAction('tokenBalancesUpdated') export const portfolioPriceHistoryUpdated = createAction('portfolioPriceHistoryUpdated') @@ -117,3 +115,5 @@ export const defaultCurrenciesUpdated = createAction('default export const expandWalletNetworks = createAction('expandWalletNetworks') export const refreshBalancesAndPriceHistory = createAction('refreshBalancesAndPriceHistory') export const setTransactionProviderError = createAction('setTransactionProviderError') +export const setSelectedCoin = createAction('setSelectedCoin') +export const setDefaultNetworks = createAction('setDefaultNetworks') diff --git a/components/brave_wallet_ui/common/async/handlers.ts b/components/brave_wallet_ui/common/async/handlers.ts index 0e894a2a524a..e820f591d91e 100644 --- a/components/brave_wallet_ui/common/async/handlers.ts +++ b/components/brave_wallet_ui/common/async/handlers.ts @@ -31,7 +31,8 @@ import { WalletAccountType, WalletState, WalletInfo, - TransactionProviderError + TransactionProviderError, + SupportedCoinTypes } from '../../constants/types' // Utils @@ -120,10 +121,6 @@ handler.on(WalletActions.initialize.getType(), async (store) => { await refreshWalletInfo(store) }) -handler.on(WalletActions.chainChangedEvent.getType(), async (store: Store, payload: ChainChangedEventPayloadType) => { - await refreshWalletInfo(store) -}) - handler.on(WalletActions.keyringCreated.getType(), async (store) => { await refreshWalletInfo(store) }) @@ -154,9 +151,12 @@ handler.on(WalletActions.accountsChanged.getType(), async (store) => { await updateAccountInfo(store) }) -handler.on(WalletActions.selectedAccountChanged.getType(), async (store) => { - await refreshWalletInfo(store) -}) +// Will use this observer selectedAccountChanged action again once +// selectedCoin is implemented here https://github.com/brave/brave-browser/issues/21465 + +// handler.on(WalletActions.selectedAccountChanged.getType(), async (store) => { +// await refreshWalletInfo(store) +// }) handler.on(WalletActions.defaultWalletChanged.getType(), async (store) => { await refreshWalletInfo(store) @@ -193,17 +193,31 @@ handler.on(WalletActions.removeFavoriteApp.getType(), async (store: Store, appIt await refreshWalletInfo(store) }) +handler.on(WalletActions.chainChangedEvent.getType(), async (store: Store, payload: ChainChangedEventPayloadType) => { + const keyringService = getAPIProxy().keyringService + const state = getWalletState(store) + const { accounts } = state + const selectedAccountAddress = await keyringService.getSelectedAccount(payload.coin) + const selectedAccount = accounts.find((account) => account.address === selectedAccountAddress.address) ?? accounts[0] + store.dispatch(WalletActions.setSelectedAccount(selectedAccount)) + store.dispatch(WalletActions.setSelectedCoin(payload.coin)) +}) + handler.on(WalletActions.selectNetwork.getType(), async (store: Store, payload: BraveWallet.NetworkInfo) => { const jsonRpcService = getAPIProxy().jsonRpcService - await jsonRpcService.setNetwork(payload.chainId, BraveWallet.CoinType.ETH) - await refreshWalletInfo(store) + await jsonRpcService.setNetwork(payload.chainId, payload.coin) + store.dispatch(WalletActions.setNetwork(payload)) }) handler.on(WalletActions.selectAccount.getType(), async (store: Store, payload: WalletAccountType) => { const { keyringService } = getAPIProxy() - + const state = getWalletState(store) + const { defaultNetworks } = state + const defaultCoinTypesNetwork = defaultNetworks.find((network) => network.coin === payload.coin) ?? defaultNetworks[0] await keyringService.setSelectedAccount(payload.address, payload.coin) + store.dispatch(WalletActions.setNetwork(defaultCoinTypesNetwork)) store.dispatch(WalletActions.setSelectedAccount(payload)) + store.dispatch(WalletActions.setSelectedCoin(payload.coin)) await store.dispatch(refreshTransactionHistory(payload.address)) }) @@ -244,15 +258,31 @@ handler.on(WalletActions.initialized.getType(), async (store: Store, payload: Wa handler.on(WalletActions.getAllNetworks.getType(), async (store) => { const jsonRpcService = getAPIProxy().jsonRpcService - const fullList = await jsonRpcService.getAllNetworks(BraveWallet.CoinType.ETH) - store.dispatch(WalletActions.setAllNetworks(fullList)) + + const getFullNetworkList = await Promise.all(SupportedCoinTypes.map(async (coin: BraveWallet.CoinType) => { + const networkList = await jsonRpcService.getAllNetworks(coin) + return networkList.networks + })) + const networkList = getFullNetworkList.flat(1) + store.dispatch(WalletActions.setAllNetworks(networkList)) }) handler.on(WalletActions.getAllTokensList.getType(), async (store) => { - const { blockchainRegistry, jsonRpcService } = getAPIProxy() - const { chainId } = await jsonRpcService.getChainId(BraveWallet.CoinType.ETH) - const fullList = await blockchainRegistry.getAllTokens(chainId) - store.dispatch(WalletActions.setAllTokensList(fullList)) + const state = getWalletState(store) + const { networkList } = state + const { blockchainRegistry } = getAPIProxy() + const getAllTokensList = await Promise.all(networkList.map(async (network) => { + const list = await blockchainRegistry.getAllTokens(network.chainId) + return list.tokens.map((token) => { + return { + ...token, + chainId: network.chainId, + logo: `chrome://erc-token-images/${token.logo}` + } + }) + })) + const allTokensList = getAllTokensList.flat(1) + store.dispatch(WalletActions.setAllTokensList(allTokensList)) }) handler.on(WalletActions.addUserAsset.getType(), async (store: Store, payload: AddUserAssetPayloadType) => { diff --git a/components/brave_wallet_ui/common/async/lib.ts b/components/brave_wallet_ui/common/async/lib.ts index 6800e5e174fc..dfc8497ac09d 100644 --- a/components/brave_wallet_ui/common/async/lib.ts +++ b/components/brave_wallet_ui/common/async/lib.ts @@ -13,13 +13,15 @@ import { WalletAccountType, AccountInfo, BraveKeyrings, - GetBlockchainTokenInfoReturnInfo + GetBlockchainTokenInfoReturnInfo, + SupportedCoinTypes, + SupportedTestNetworks } from '../../constants/types' import * as WalletActions from '../actions/wallet_actions' // Utils -import { GetNetworkInfo } from '../../utils/network-utils' -import { GetTokenParam, GetFlattenedAccountBalances } from '../../utils/api-utils' +import { getNetworkInfo, getNetworksByCoinType } from '../../utils/network-utils' +import { getTokenParam, getFlattenedAccountBalances } from '../../utils/api-utils' import Amount from '../../utils/amount' import getAPIProxy from './bridge' @@ -163,25 +165,46 @@ export async function getIsSwapSupported (network: BraveWallet.NetworkInfo): Pro } export function refreshVisibleTokenInfo (currentNetwork: BraveWallet.NetworkInfo) { - return async (dispatch: Dispatch) => { + return async (dispatch: Dispatch, getState: () => State) => { const { braveWalletService } = getAPIProxy() + const { wallet: { networkList } } = getState() + + const getVisibleAssets = await Promise.all(networkList.map(async (network) => { + // Creates a network's Native Asset if not returned + const nativeAsset: BraveWallet.BlockchainToken = { + contractAddress: '', + decimals: network.decimals, + isErc20: false, + isErc721: false, + logo: network.iconUrls[0] ?? '', + name: network.symbolName, + symbol: network.symbol, + // MULTICHAIN: Change visible back to false once getUserAssets returns + // SOL and FIL by default. + visible: network.coin === BraveWallet.CoinType.SOL || network.coin === BraveWallet.CoinType.FIL, + tokenId: '', + coingeckoId: '', + chainId: network.chainId + } - // Selected Network's Native Asset - const nativeAsset: BraveWallet.BlockchainToken = { - contractAddress: '', - decimals: currentNetwork.decimals, - isErc20: false, - isErc721: false, - logo: currentNetwork.iconUrls[0] ?? '', - name: currentNetwork.symbolName, - symbol: currentNetwork.symbol, - visible: false, - tokenId: '', - coingeckoId: '' - } - - const visibleTokensInfo = await braveWalletService.getUserAssets(currentNetwork.chainId) - const visibleAssets: BraveWallet.BlockchainToken[] = visibleTokensInfo.tokens.length === 0 ? [nativeAsset] : visibleTokensInfo.tokens + // Get a list of user tokens for each coinType and network. + const getTokenList = network.chainId === BraveWallet.LOCALHOST_CHAIN_ID && + network.coin === BraveWallet.CoinType.SOL || network.coin === BraveWallet.CoinType.FIL + // Since LOCALHOST's chainId is shared between coinType networks, + // this check will make sure we create the correct Native Asset for + // that network. + ? { tokens: [nativeAsset] } // MULTICHAIN: We do not yet support getting userAssets for FIL and SOL + // Will be implemented here https://github.com/brave/brave-browser/issues/21547 + : await braveWalletService.getUserAssets(network.chainId) + + // Adds a logo and chainId to each token object + const tokenList = getTokenList.tokens.map((token) => ({ + ...token, + logo: token.symbol.toLowerCase() === 'sol' ? '' : `chrome://erc-token-images/${token.logo}` + })) as BraveWallet.BlockchainToken[] + return tokenList.length === 0 ? [nativeAsset] : tokenList + })) + const visibleAssets = getVisibleAssets.flat(1) await dispatch(WalletActions.setVisibleTokensInfo(visibleAssets)) } } @@ -189,29 +212,95 @@ export function refreshVisibleTokenInfo (currentNetwork: BraveWallet.NetworkInfo export function refreshBalances () { return async (dispatch: Dispatch, getState: () => State) => { const { jsonRpcService } = getAPIProxy() - const { wallet: { accounts, userVisibleTokensInfo, selectedNetwork } } = getState() + const { wallet: { accounts, userVisibleTokensInfo, networkList, defaultNetworks } } = getState() + + const emptyBalance = { + balance: '0x0', + error: 0, + errorMessage: '' + } + + const getNativeAssetsBalanceReturnInfos = await Promise.all(accounts.map(async (account) => { + const networks = getNetworksByCoinType(networkList, account.coin) + + return Promise.all(networks.map(async (network) => { + // MULTICHAIN: Backend needs to allow chainId for getSolanaBalance method + // Will be implemented here https://github.com/brave/brave-browser/issues/21695 + if (account.coin === BraveWallet.CoinType.SOL || account.coin === BraveWallet.CoinType.FIL) { + if (defaultNetworks.some(n => n.chainId === network.chainId)) { + // Get CoinType SOL balances + if (network.coin === BraveWallet.CoinType.SOL) { + const solBalanceInfo = await jsonRpcService.getSolanaBalance(account.address) + return { + ...solBalanceInfo, + balance: solBalanceInfo.balance.toString(), + chainId: network.chainId + } + } + + // Get CoinType FIL balances + if (network.coin === BraveWallet.CoinType.FIL) { + const balanceInfo = await jsonRpcService.getBalance(account.address, account.coin, network.chainId) + return { + ...balanceInfo, + chainId: network.chainId + } + } + } + + // Return emptyBalance for CoinType FIL or SOL if network + // is not included in defaultNetworks. Will update when this + // is implemented https://github.com/brave/brave-browser/issues/21695 + return { + ...emptyBalance, + chainId: network.chainId + } + } + + // LOCALHOST will return an error until a local instance is + // detected, we now will will return a 0 balance until it's detected. + if (network.chainId === BraveWallet.LOCALHOST_CHAIN_ID) { + const localhostBalanceInfo = await jsonRpcService.getBalance(account.address, account.coin, network.chainId) + const info = localhostBalanceInfo.error === 0 ? localhostBalanceInfo : emptyBalance + return { + ...info, + chainId: network.chainId + } + } - const getBalanceReturnInfos = await Promise.all(accounts.map(async (account) => { - const balanceInfo = await jsonRpcService.getBalance(account.address, account.coin, selectedNetwork.chainId) - return balanceInfo + // Get CoinType ETH balances + const balanceInfo = await jsonRpcService.getBalance(account.address, account.coin, network.chainId) + return { + ...balanceInfo, + chainId: network.chainId + } + })) })) + await dispatch(WalletActions.nativeAssetBalancesUpdated({ - balances: getBalanceReturnInfos + balances: getNativeAssetsBalanceReturnInfos })) const visibleTokens = userVisibleTokensInfo.filter(asset => asset.contractAddress !== '') - const getBlockchainTokenBalanceReturnInfos = await Promise.all(accounts.map(async (account) => { - return Promise.all(visibleTokens.map(async (token) => { - if (token.isErc721) { - return jsonRpcService.getERC721TokenBalance(token.contractAddress, token.tokenId ?? '', account.address, selectedNetwork.chainId) - } - return jsonRpcService.getERC20TokenBalance(token.contractAddress, account.address, selectedNetwork.chainId) - })) + const getBlockchainTokensBalanceReturnInfos = await Promise.all(accounts.map(async (account) => { + if (account.coin === BraveWallet.CoinType.ETH) { + return Promise.all(visibleTokens.map(async (token) => { + if (token.isErc721) { + return jsonRpcService.getERC721TokenBalance(token.contractAddress, token.tokenId ?? '', account.address, token?.chainId ?? '') + } + return jsonRpcService.getERC20TokenBalance(token.contractAddress, account.address, token?.chainId ?? '') + })) + } else { + // MULTICHAIN: We do not yet support getting + // token balances for SOL and FIL + // Will be implemented here https://github.com/brave/brave-browser/issues/21695 + return [] + } })) await dispatch(WalletActions.tokenBalancesUpdated({ - balances: getBlockchainTokenBalanceReturnInfos + balances: getBlockchainTokensBalanceReturnInfos })) } } @@ -219,19 +308,30 @@ export function refreshBalances () { export function refreshPrices () { return async (dispatch: Dispatch, getState: () => State) => { const { assetRatioService } = getAPIProxy() - const { wallet: { accounts, selectedPortfolioTimeline, selectedNetwork, userVisibleTokensInfo, defaultCurrencies } } = getState() - + const { wallet: { accounts, selectedPortfolioTimeline, userVisibleTokensInfo, defaultCurrencies, networkList } } = getState() const defaultFiatCurrency = defaultCurrencies.fiat.toLowerCase() - // Fetch native asset (ETH) price - const getNativeAssetPrice = await assetRatioService.getPrice([selectedNetwork.symbol.toLowerCase()], [defaultFiatCurrency], selectedPortfolioTimeline) - const nativeAssetPrice = getNativeAssetPrice.success ? getNativeAssetPrice.values.find((i) => i.toAsset === defaultFiatCurrency)?.price ?? '' : '' - // Update Token Prices + // Return if userVisibleTokensInfo is empty if (!userVisibleTokensInfo) { return } - const getTokenPrices = await Promise.all(GetFlattenedAccountBalances(accounts, userVisibleTokensInfo).map(async (token) => { + // Get prices for each networks native asset + const mainnetList = networkList.filter((network) => !SupportedTestNetworks.includes(network.chainId)) + const getNativeAssetPrices = await Promise.all(mainnetList.map(async (network) => { + const getNativeAssetPrice = await assetRatioService.getPrice([network.symbol.toLowerCase()], [defaultFiatCurrency], selectedPortfolioTimeline) + const nativeAssetPrice = getNativeAssetPrice.success ? getNativeAssetPrice.values.find((i) => i.toAsset === defaultFiatCurrency)?.price ?? '' : '' + return { + fromAsset: network.symbol.toLowerCase(), + toAsset: defaultFiatCurrency, + price: nativeAssetPrice, + assetTimeframeChange: '' + } + })) + + // Get prices for all other tokens on each network + const blockChainTokenInfo = userVisibleTokensInfo.filter((token) => token.contractAddress !== '') + const getTokenPrices = await Promise.all(getFlattenedAccountBalances(accounts, blockChainTokenInfo).map(async (token) => { const emptyPrice = { fromAsset: token.token.symbol, toAsset: defaultFiatCurrency, @@ -241,7 +341,7 @@ export function refreshPrices () { // If a tokens balance is 0 we do not make an unnecessary api call for the price of that token const price = token.balance > 0 && token.token.isErc20 - ? await assetRatioService.getPrice([GetTokenParam(selectedNetwork, token.token)], [defaultFiatCurrency], selectedPortfolioTimeline) + ? await assetRatioService.getPrice([getTokenParam(token.token)], [defaultFiatCurrency], selectedPortfolioTimeline) : { values: [{ ...emptyPrice, price: '0' }], success: true } const tokenPrice = { @@ -254,12 +354,7 @@ export function refreshPrices () { await dispatch(WalletActions.pricesUpdated({ success: true, values: [ - { - fromAsset: selectedNetwork.symbol.toLowerCase(), - toAsset: defaultFiatCurrency, - price: nativeAssetPrice, - assetTimeframeChange: '' - }, + ...getNativeAssetPrices, ...getTokenPrices ] })) @@ -271,34 +366,42 @@ export function refreshTokenPriceHistory (selectedPortfolioTimeline: BraveWallet const apiProxy = getAPIProxy() const { assetRatioService } = apiProxy - const { wallet: { accounts, defaultCurrencies, selectedNetwork, userVisibleTokensInfo } } = getState() + const { wallet: { accounts, defaultCurrencies, userVisibleTokensInfo } } = getState() - // If a tokens balance is 0 we do not make an unnecessary api call for price history of that token - const priceHistory = await Promise.all(GetFlattenedAccountBalances(accounts, userVisibleTokensInfo) - .filter(({ token, balance }) => !token.isErc721 && balance > 0) + // Get all Price History + const priceHistory = await Promise.all(getFlattenedAccountBalances(accounts, userVisibleTokensInfo) + // If a tokens balance is 0 we do not make an unnecessary api call for price history of that token + // Will remove testnetwork filter when this is implemented + // https://github.com/brave/brave-browser/issues/20780 + .filter(({ token, balance }) => !token.isErc721 && balance > 0 && !SupportedTestNetworks.includes(token.chainId)) .map(async ({ token }) => ({ - contractAddress: token.contractAddress, + // If a visible asset has a contractAddress of '' + // it is a native asset so we use a symbol instead. + contractAddress: token.contractAddress ? token.contractAddress : token.symbol, history: await assetRatioService.getPriceHistory( - GetTokenParam(selectedNetwork, token), defaultCurrencies.fiat.toLowerCase(), selectedPortfolioTimeline + getTokenParam(token), defaultCurrencies.fiat.toLowerCase(), selectedPortfolioTimeline ) })) ) + // Combine Price History and Balances const priceHistoryWithBalances = accounts.map((account) => { return userVisibleTokensInfo - .filter((token) => !token.isErc721) + // Will remove testnetwork filter when this is implemented + // https://github.com/brave/brave-browser/issues/20780 + .filter((token) => !token.isErc721 && !SupportedTestNetworks.includes(token.chainId)) .map((token) => { const balance = token.contractAddress ? account.tokenBalanceRegistry[token.contractAddress.toLowerCase()] - : account.balance + : account.nativeBalanceRegistry[token.chainId || ''] + const contractAddress = token.contractAddress ? token.contractAddress : token.symbol return { token, balance: balance || '0', - history: priceHistory.find((t) => token.contractAddress === t.contractAddress)?.history ?? { success: true, values: [] } + history: priceHistory.find((t) => contractAddress === t.contractAddress)?.history ?? { success: true, values: [] } } }) }) - dispatch(WalletActions.portfolioPriceHistoryUpdated(priceHistoryWithBalances)) } } @@ -307,7 +410,6 @@ export function refreshTransactionHistory (address?: string) { return async (dispatch: Dispatch, getState: () => State) => { const apiProxy = getAPIProxy() const { txService } = apiProxy - const { wallet: { accounts, transactions } } = getState() const accountsToUpdate = address !== undefined @@ -329,21 +431,50 @@ export function refreshTransactionHistory (address?: string) { } export function refreshNetworkInfo () { - return async (dispatch: Dispatch) => { + return async (dispatch: Dispatch, getState: () => State) => { const apiProxy = getAPIProxy() const { jsonRpcService } = apiProxy - - const networkList = await jsonRpcService.getAllNetworks(BraveWallet.CoinType.ETH) + const { wallet: { selectedCoin, isFilecoinEnabled, isSolanaEnabled, isTestNetworksEnabled } } = getState() + + // Get All Networks + const getFullNetworkList = await Promise.all(SupportedCoinTypes.map(async (coin: BraveWallet.CoinType) => { + // MULTICHAIN: While we are still in development for FIL and SOL, + // we will not use their networks unless enabled by brave://flags + if (coin === BraveWallet.CoinType.FIL && !isFilecoinEnabled) { + return [] + } + if (coin === BraveWallet.CoinType.SOL && !isSolanaEnabled) { + return [] + } + const networkList = await jsonRpcService.getAllNetworks(coin) + return networkList.networks + })) + const flattenedNetworkList = getFullNetworkList.flat(1) + const networkList = + isTestNetworksEnabled + ? flattenedNetworkList + : flattenedNetworkList.filter((network) => !SupportedTestNetworks.includes(network.chainId)) dispatch(WalletActions.setAllNetworks(networkList)) - const chainId = await jsonRpcService.getChainId(BraveWallet.CoinType.ETH) - const currentNetwork = GetNetworkInfo(chainId.chainId, networkList.networks) + + // Get default network for each coinType + const defaultNetworks = await Promise.all(SupportedCoinTypes.map(async (coin: BraveWallet.CoinType) => { + const coinsChainId = await jsonRpcService.getChainId(coin) + const network = getNetworkInfo(coinsChainId.chainId, networkList) + return network + })) + dispatch(WalletActions.setDefaultNetworks(defaultNetworks)) + + // Get current selected networks info + const chainId = await jsonRpcService.getChainId(selectedCoin) + const currentNetwork = getNetworkInfo(chainId.chainId, networkList) dispatch(WalletActions.setNetwork(currentNetwork)) return currentNetwork } } export function refreshKeyringInfo () { - return async (dispatch: Dispatch) => { + return async (dispatch: Dispatch, getState: () => State) => { + const { wallet: { selectedCoin } } = getState() const apiProxy = getAPIProxy() const { keyringService, walletHandler } = apiProxy @@ -357,7 +488,7 @@ export function refreshKeyringInfo () { } // Get selectedAccountAddress - const getSelectedAccount = await keyringService.getSelectedAccount(BraveWallet.CoinType.ETH) + const getSelectedAccount = await keyringService.getSelectedAccount(selectedCoin) const selectedAddress = getSelectedAccount.address // Fallback account address if selectedAccount returns null diff --git a/components/brave_wallet_ui/common/constants/mocks.ts b/components/brave_wallet_ui/common/constants/mocks.ts index ee83413ab5d7..8c9329c49e21 100644 --- a/components/brave_wallet_ui/common/constants/mocks.ts +++ b/components/brave_wallet_ui/common/constants/mocks.ts @@ -39,7 +39,7 @@ export const getMockedTransactionInfo = (): BraveWallet.TransactionInfo => { } } -export const mockNetwork: BraveWallet.EthereumChain = { +export const mockNetwork: BraveWallet.NetworkInfo = { chainId: '0x1', chainName: 'Ethereum Main Net', rpcUrls: ['https://mainnet.infura.io/v3/'], @@ -48,7 +48,7 @@ export const mockNetwork: BraveWallet.EthereumChain = { symbolName: 'Ethereum', decimals: 18, iconUrls: [], - isEip1559: true + coin: BraveWallet.CoinType.ETH } export const mockERC20Token: BraveWallet.BlockchainToken = { @@ -61,14 +61,18 @@ export const mockERC20Token: BraveWallet.BlockchainToken = { decimals: 18, visible: true, tokenId: '', - coingeckoId: '' + coingeckoId: '', + chainId: BraveWallet.MAINNET_CHAIN_ID } export const mockAccount: WalletAccountType = { id: 'mockId', name: 'mockAccountName', address: 'mockAddress', - balance: '123456', + nativeBalanceRegistry: { + '0x1': '123456' + }, + coin: BraveWallet.CoinType.ETH, accountType: 'Primary', tokenBalanceRegistry: {} } diff --git a/components/brave_wallet_ui/common/hooks/assets-management.test.ts b/components/brave_wallet_ui/common/hooks/assets-management.test.ts index af1475ec9ac3..dc0468396a4d 100644 --- a/components/brave_wallet_ui/common/hooks/assets-management.test.ts +++ b/components/brave_wallet_ui/common/hooks/assets-management.test.ts @@ -21,7 +21,8 @@ const mockCustomToken = { decimals: 18, visible: true, tokenId: '', - coingeckoId: '' + coingeckoId: '', + chainId: '0x1' } describe('useAssetManagement hook', () => { @@ -41,7 +42,6 @@ describe('useAssetManagement hook', () => { WalletActions.setUserAssetVisible, WalletActions.removeUserAsset, WalletActions.refreshBalancesAndPriceHistory, - mockNetwork, AccountAssetOptions, mockUserVisibleTokensInfo )) @@ -56,7 +56,6 @@ describe('useAssetManagement hook', () => { WalletActions.setUserAssetVisible, WalletActions.removeUserAsset, WalletActions.refreshBalancesAndPriceHistory, - mockNetwork, AccountAssetOptions, mockUserVisibleTokensInfo )) @@ -71,7 +70,6 @@ describe('useAssetManagement hook', () => { WalletActions.setUserAssetVisible, WalletActions.removeUserAsset, WalletActions.refreshBalancesAndPriceHistory, - mockNetwork, AccountAssetOptions, mockUserVisibleTokensInfo )) @@ -87,7 +85,6 @@ describe('useAssetManagement hook', () => { WalletActions.setUserAssetVisible, WalletActions.removeUserAsset, WalletActions.refreshBalancesAndPriceHistory, - mockNetwork, AccountAssetOptions, [...mockUserVisibleTokensInfo, mockCustomToken] )) @@ -102,7 +99,6 @@ describe('useAssetManagement hook', () => { WalletActions.setUserAssetVisible, WalletActions.removeUserAsset, WalletActions.refreshBalancesAndPriceHistory, - mockNetwork, AccountAssetOptions, [...mockUserVisibleTokensInfo, { ...mockCustomToken, visible: false }] )) diff --git a/components/brave_wallet_ui/common/hooks/assets-management.ts b/components/brave_wallet_ui/common/hooks/assets-management.ts index f44b68f7073d..2837ec65c470 100644 --- a/components/brave_wallet_ui/common/hooks/assets-management.ts +++ b/components/brave_wallet_ui/common/hooks/assets-management.ts @@ -22,7 +22,6 @@ export default function useAssetManagement ( setUserAssetVisible: SimpleActionCreator, removeUserAsset: SimpleActionCreator, refreshBalancesPricesAndHistory: EmptyActionCreator, - selectedNetwork: BraveWallet.NetworkInfo, fullTokenList: BraveWallet.BlockchainToken[], userVisibleTokensInfo: BraveWallet.BlockchainToken[] ) { @@ -32,7 +31,7 @@ export default function useAssetManagement ( ...token, logo: stripERC20TokenImageURL(token.logo) || '' }, - chainId: selectedNetwork.chainId + chainId: token?.chainId ?? '' }) } @@ -56,7 +55,7 @@ export default function useAssetManagement ( userVisibleTokensInfo.filter((firstItem) => !updatedTokensList.some((secondItem) => firstItem.contractAddress.toLowerCase() === secondItem.contractAddress.toLowerCase())) - .forEach((token) => removeUserAsset({ token, chainId: selectedNetwork.chainId })) + .forEach((token) => removeUserAsset({ token, chainId: token?.chainId ?? '' })) // Gets a list of custom tokens returned from updatedTokensList payload // then compares customTokens against userVisibleTokensInfo list and updates the custom tokens visibility if it has changed @@ -73,13 +72,13 @@ export default function useAssetManagement ( } // Updates token visibility exluding a networks native token if (foundToken?.visible !== token.visible && token.contractAddress.toLowerCase() !== '') { - setUserAssetVisible({ token, chainId: selectedNetwork.chainId, isVisible: token.visible }) + setUserAssetVisible({ token, chainId: token?.chainId ?? '', isVisible: token.visible }) } }) // Refreshes Balances, Prices and Price History when done. refreshBalancesPricesAndHistory() - }, [userVisibleTokensInfo, selectedNetwork]) + }, [userVisibleTokensInfo]) return { onUpdateVisibleAssets, diff --git a/components/brave_wallet_ui/common/hooks/assets.test.ts b/components/brave_wallet_ui/common/hooks/assets.test.ts index 4a33326f132f..ea235f6d6b7b 100644 --- a/components/brave_wallet_ui/common/hooks/assets.test.ts +++ b/components/brave_wallet_ui/common/hooks/assets.test.ts @@ -37,7 +37,7 @@ const getBuyAssets = async () => { describe('useAssets hook', () => { it('Selected account has balances, should return expectedResult', async () => { - const { result, waitForNextUpdate } = renderHook(() => useAssets(mockAccounts, mockAccounts[0], mockNetwork, mockVisibleList, mockVisibleList, mockAssetPrices, getBuyAssets)) + const { result, waitForNextUpdate } = renderHook(() => useAssets(mockAccounts[0], [mockNetwork], mockNetwork, mockVisibleList, mockVisibleList, mockAssetPrices, getBuyAssets)) await act(async () => { await waitForNextUpdate() }) @@ -45,7 +45,7 @@ describe('useAssets hook', () => { }) it('should return empty array for panelUserAssetList if visible assets is empty', async () => { - const { result, waitForNextUpdate } = renderHook(() => useAssets(mockAccounts, mockAccount, mockNetwork, mockVisibleList, [], mockAssetPrices, getBuyAssets)) + const { result, waitForNextUpdate } = renderHook(() => useAssets(mockAccount, [mockNetwork], mockNetwork, mockVisibleList, [], mockAssetPrices, getBuyAssets)) await act(async () => { await waitForNextUpdate() }) diff --git a/components/brave_wallet_ui/common/hooks/assets.ts b/components/brave_wallet_ui/common/hooks/assets.ts index 617027e2c071..9d82b7423b17 100644 --- a/components/brave_wallet_ui/common/hooks/assets.ts +++ b/components/brave_wallet_ui/common/hooks/assets.ts @@ -19,8 +19,8 @@ import usePricing from './pricing' import useBalance from './balance' export default function useAssets ( - accounts: WalletAccountType[], selectedAccount: WalletAccountType, + networkList: BraveWallet.NetworkInfo[], selectedNetwork: BraveWallet.NetworkInfo, fullTokenList: BraveWallet.BlockchainToken[], userVisibleTokensInfo: BraveWallet.BlockchainToken[], @@ -28,24 +28,32 @@ export default function useAssets ( getBuyAssets: () => Promise ) { const { computeFiatAmount } = usePricing(spotPrices) - const getBalance = useBalance(selectedNetwork) + const getBalance = useBalance(networkList) const nativeAsset = React.useMemo( () => makeNetworkAsset(selectedNetwork), [selectedNetwork] ) + const assetsByNetwork = React.useMemo(() => { + if (!userVisibleTokensInfo) { + return [] + } + + return userVisibleTokensInfo.filter((token) => token.chainId === selectedNetwork.chainId) + }, [userVisibleTokensInfo, selectedNetwork]) + const swapAssetOptions: BraveWallet.BlockchainToken[] = React.useMemo(() => { return [ nativeAsset, ...fullTokenList.filter((asset) => asset.symbol.toUpperCase() === 'BAT'), - ...userVisibleTokensInfo + ...assetsByNetwork .filter(asset => !['BAT', nativeAsset.symbol.toUpperCase()].includes(asset.symbol.toUpperCase())), ...fullTokenList .filter(asset => !['BAT', nativeAsset.symbol.toUpperCase()].includes(asset.symbol.toUpperCase())) - .filter(asset => !userVisibleTokensInfo + .filter(asset => !assetsByNetwork .some(token => token.symbol.toUpperCase() === asset.symbol.toUpperCase())) ] - }, [fullTokenList, userVisibleTokensInfo, nativeAsset]) + }, [fullTokenList, userVisibleTokensInfo, nativeAsset, assetsByNetwork]) const [buyAssetOptions, setBuyAssetOptions] = React.useState([nativeAsset]) @@ -58,8 +66,8 @@ export default function useAssets ( }).catch(e => console.error(e)) }, []) - const panelUserAssetList = React.useMemo(() => { - if (!userVisibleTokensInfo) { + const assetsByValueAndNetwork = React.useMemo(() => { + if (!assetsByNetwork) { return [] } @@ -67,7 +75,7 @@ export default function useAssets ( return [] } - return userVisibleTokensInfo.sort(function (a, b) { + return assetsByNetwork.sort(function (a, b) { const aBalance = getBalance(selectedAccount, a) const bBalance = getBalance(selectedAccount, b) @@ -76,12 +84,12 @@ export default function useAssets ( return bFiatBalance.toNumber() - aFiatBalance.toNumber() }) - }, [selectedAccount, userVisibleTokensInfo, getBalance, computeFiatAmount]) + }, [selectedAccount, assetsByNetwork, getBalance, computeFiatAmount]) return { swapAssetOptions, - sendAssetOptions: userVisibleTokensInfo, + sendAssetOptions: assetsByNetwork, buyAssetOptions, - panelUserAssetList + panelUserAssetList: assetsByValueAndNetwork } } diff --git a/components/brave_wallet_ui/common/hooks/balance.ts b/components/brave_wallet_ui/common/hooks/balance.ts index 49eede4b3c25..98947bd3018c 100644 --- a/components/brave_wallet_ui/common/hooks/balance.ts +++ b/components/brave_wallet_ui/common/hooks/balance.ts @@ -6,8 +6,9 @@ import * as React from 'react' import { BraveWallet, WalletAccountType } from '../../constants/types' +import { getTokensCoinType } from '../../utils/network-utils' -export default function useBalance (network: BraveWallet.NetworkInfo) { +export default function useBalance (networks: BraveWallet.NetworkInfo[]) { return React.useCallback((account?: WalletAccountType, token?: BraveWallet.BlockchainToken) => { if (!account || !token) { return '' @@ -17,11 +18,22 @@ export default function useBalance (network: BraveWallet.NetworkInfo) { return '' } + const tokensCoinType = getTokensCoinType(networks, token) // Return native asset balance - if (token.symbol.toLowerCase() === network.symbol.toLowerCase()) { - return account.balance + if ( + ( + token.contractAddress === '' || + // 0xeee... is used as a contractAddress + // of native assets in our Swap Widget and used for the 0x API. + token.contractAddress === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' + ) && + // Since all coinTypes share the same chainId for localHost networks, + // we want to make sure we return the right balance for that token. + account.coin === tokensCoinType + ) { + return (account.nativeBalanceRegistry || {})[token.chainId || ''] || '' } return (account.tokenBalanceRegistry || {})[token.contractAddress.toLowerCase()] || '' - }, [network]) + }, [networks]) } diff --git a/components/brave_wallet_ui/common/hooks/select-preset.test.ts b/components/brave_wallet_ui/common/hooks/select-preset.test.ts index 20abb1d7e9dd..7f1b03bf4b40 100644 --- a/components/brave_wallet_ui/common/hooks/select-preset.test.ts +++ b/components/brave_wallet_ui/common/hooks/select-preset.test.ts @@ -22,7 +22,7 @@ describe('usePreset hook', () => { [mockERC20Token.contractAddress.toLowerCase()]: balance } }, - mockNetwork, + [mockNetwork], mockFunc, jest.fn(), mockERC20Token, @@ -40,7 +40,7 @@ describe('usePreset hook', () => { const { result: { current: calcPresetAmount } } = renderHook(() => usePreset( mockAccount, - mockNetwork, + [mockNetwork], mockOnSetFromAmount, mockOnSetSendAmount, undefined, diff --git a/components/brave_wallet_ui/common/hooks/select-preset.ts b/components/brave_wallet_ui/common/hooks/select-preset.ts index 2435c5523fbe..6614166ec3cb 100644 --- a/components/brave_wallet_ui/common/hooks/select-preset.ts +++ b/components/brave_wallet_ui/common/hooks/select-preset.ts @@ -13,13 +13,13 @@ import Amount from '../../utils/amount' export default function usePreset ( selectedAccount: WalletAccountType, - selectedNetwork: BraveWallet.NetworkInfo, + networkList: BraveWallet.NetworkInfo[], onSetFromAmount: (value: string) => void, onSetSendAmount: (value: string) => void, swapAsset?: BraveWallet.BlockchainToken, sendAsset?: BraveWallet.BlockchainToken ) { - const getBalance = useBalance(selectedNetwork) + const getBalance = useBalance(networkList) return (sendOrSwap: 'send' | 'swap') => (percent: number) => { const selectedAsset = sendOrSwap === 'send' ? sendAsset : swapAsset diff --git a/components/brave_wallet_ui/common/hooks/swap.test.ts b/components/brave_wallet_ui/common/hooks/swap.test.ts index 4646f480a724..20f3f507d5e2 100644 --- a/components/brave_wallet_ui/common/hooks/swap.test.ts +++ b/components/brave_wallet_ui/common/hooks/swap.test.ts @@ -21,7 +21,7 @@ async function mockGetERC20Allowance (contractAddress: string, ownerAddress: str } const mockIsSwapSupportedFactory = (expected: boolean) => - async (network: BraveWallet.EthereumChain) => + async (network: BraveWallet.NetworkInfo) => expected const mockQuote = { @@ -49,6 +49,7 @@ describe('useSwap hook', () => { const { result, waitForNextUpdate } = renderHook(() => useSwap( mockAccount, mockNetwork, + [mockNetwork], AccountAssetOptions, WalletPageActions.fetchPageSwapQuote, mockGetERC20Allowance, @@ -66,6 +67,7 @@ describe('useSwap hook', () => { const { result, waitFor } = renderHook(() => useSwap( mockAccount, mockNetwork, + [mockNetwork], AccountAssetOptions, WalletPageActions.fetchPageSwapQuote, mockGetERC20Allowance, @@ -85,6 +87,7 @@ describe('useSwap hook', () => { const { waitForNextUpdate } = renderHook(() => useSwap( mockAccount, mockNetwork, + [mockNetwork], AccountAssetOptions, WalletPageActions.fetchPageSwapQuote, mockFn, @@ -107,6 +110,7 @@ describe('useSwap hook', () => { const { waitForNextUpdate } = renderHook(() => useSwap( mockAccount, mockNetwork, + [mockNetwork], swapAssets, WalletPageActions.fetchPageSwapQuote, mockFn, @@ -133,6 +137,7 @@ describe('useSwap hook', () => { const { waitForNextUpdate } = renderHook(() => useSwap( mockAccount, mockNetwork, + [mockNetwork], swapAssets, WalletPageActions.fetchPageSwapQuote, mockFn, @@ -157,6 +162,7 @@ describe('useSwap hook', () => { const { result, waitForValueToChange, waitFor } = renderHook(() => useSwap( mockAccount, mockNetwork, + [mockNetwork], AccountAssetOptions, WalletPageActions.fetchPageSwapQuote, mockGetERC20Allowance, @@ -185,9 +191,12 @@ describe('useSwap hook', () => { const { result, waitForValueToChange, waitFor } = renderHook(() => useSwap( { ...mockAccount, - balance: '1000000000000000000' // 1 ETH + nativeBalanceRegistry: { + '0x1': '1000000000000000000' + } }, mockNetwork, + [mockNetwork], AccountAssetOptions, // From asset is ETH WalletPageActions.fetchPageSwapQuote, mockGetERC20Allowance, @@ -233,6 +242,7 @@ describe('useSwap hook', () => { const { result, waitFor, waitForValueToChange } = renderHook(() => useSwap( mockAccount, mockNetwork, + [mockNetwork], AccountAssetOptions, // To asset is BAT WalletPageActions.fetchPageSwapQuote, mockGetERC20Allowance, @@ -279,6 +289,7 @@ describe('useSwap hook', () => { const { result, waitFor, waitForValueToChange } = renderHook(() => useSwap( mockAccount, // Balance: 123456 Wei mockNetwork, + [mockNetwork], AccountAssetOptions, // From asset is ETH WalletPageActions.fetchPageSwapQuote, mockGetERC20Allowance, @@ -330,6 +341,7 @@ describe('useSwap hook', () => { } }, mockNetwork, + [mockNetwork], AccountAssetOptions.slice(1), // From asset is BAT WalletPageActions.fetchPageSwapQuote, mockGetERC20Allowance, @@ -377,6 +389,7 @@ describe('useSwap hook', () => { const { result, waitFor, waitForValueToChange } = renderHook(() => useSwap( mockAccount, // Balance: 123456 Wei mockNetwork, + [mockNetwork], AccountAssetOptions, // From asset is ETH WalletPageActions.fetchPageSwapQuote, mockGetERC20Allowance, @@ -409,9 +422,12 @@ describe('useSwap hook', () => { const { result, waitFor, waitForValueToChange } = renderHook(() => useSwap( { ...mockAccount, - balance: '1234560' // 1234560 Wei + nativeBalanceRegistry: { + '0x1': '1234560' + } }, mockNetwork, + [mockNetwork], AccountAssetOptions, // From asset is ETH WalletPageActions.fetchPageSwapQuote, mockGetERC20Allowance, @@ -458,12 +474,15 @@ describe('useSwap hook', () => { const { result, waitFor, waitForValueToChange } = renderHook(() => useSwap( { ...mockAccount, - balance: '1000000000000000000', // 1 ETH + nativeBalanceRegistry: { + '0x1': '1000000000000000000' + }, tokenBalanceRegistry: { [AccountAssetOptions[1].contractAddress.toLowerCase()]: '20000000000000000000' // 20 BAT } }, mockNetwork, + [mockNetwork], AccountAssetOptions.slice(1), // From asset is BAT WalletPageActions.fetchPageSwapQuote, mockGetERC20Allowance, @@ -504,9 +523,12 @@ describe('useSwap hook', () => { const { result, waitFor, waitForValueToChange } = renderHook(() => useSwap( { ...mockAccount, - balance: '1000000000000000000' // 1 ETH + nativeBalanceRegistry: { + '0x1': '1000000000000000000' + } }, mockNetwork, + [mockNetwork], AccountAssetOptions, // From asset is ETH WalletPageActions.fetchPageSwapQuote, mockGetERC20Allowance, @@ -558,9 +580,12 @@ describe('useSwap hook', () => { const { result, waitFor, waitForValueToChange } = renderHook(() => useSwap( { ...mockAccount, - balance: '1000000000000000000' // 1 ETH + nativeBalanceRegistry: { + '0x1': '1000000000000000000' + } }, mockNetwork, + [mockNetwork], AccountAssetOptions, // From asset is ETH WalletPageActions.fetchPageSwapQuote, mockGetERC20Allowance, diff --git a/components/brave_wallet_ui/common/hooks/swap.ts b/components/brave_wallet_ui/common/hooks/swap.ts index 398f3ba69f37..caa1064a4a86 100644 --- a/components/brave_wallet_ui/common/hooks/swap.ts +++ b/components/brave_wallet_ui/common/hooks/swap.ts @@ -38,6 +38,7 @@ const SWAP_VALIDATION_ERROR_CODE = 100 export default function useSwap ( selectedAccount: WalletAccountType, selectedNetwork: BraveWallet.NetworkInfo, + networkList: BraveWallet.NetworkInfo[], swapAssetOptions: BraveWallet.BlockchainToken[], fetchSwapQuote: SimpleActionCreator, getERC20Allowance: (contractAddress: string, ownerAddress: string, spenderAddress: string) => Promise, @@ -99,7 +100,7 @@ export default function useSwap ( .catch(e => console.log(e)) }, [fromAsset, quote, selectedAccount]) - const getBalance = useBalance(selectedNetwork) + const getBalance = useBalance(networkList) const fromAssetBalance = getBalance(selectedAccount, fromAsset) const nativeAssetBalance = getBalance(selectedAccount, nativeAsset) diff --git a/components/brave_wallet_ui/common/hooks/transaction-parser.test.ts b/components/brave_wallet_ui/common/hooks/transaction-parser.test.ts index 48743e0e9f70..521ba4623597 100644 --- a/components/brave_wallet_ui/common/hooks/transaction-parser.test.ts +++ b/components/brave_wallet_ui/common/hooks/transaction-parser.test.ts @@ -236,7 +236,9 @@ describe('useTransactionParser hook', () => { [mockERC20Token.contractAddress.toLowerCase()]: '0' }, address: '0xdeadbeef', - balance: '1000000000000000' // 0.001 ETH + nativeBalanceRegistry: { + '0x1': '1000000000000000' // 0.001 ETH + } }], mockAssetPrices, [], [] )) @@ -284,7 +286,9 @@ describe('useTransactionParser hook', () => { [{ ...mockAccount, address: '0xdeadbeef', - balance: '1000000000000000000', // 1 ETH + nativeBalanceRegistry: { + '0x1': '1000000000000000000' // 1 ETH + }, tokenBalanceRegistry: { [mockTxData.baseData.to.toLowerCase()]: '0' } @@ -342,7 +346,9 @@ describe('useTransactionParser hook', () => { [{ ...mockAccount, address: '0xdeadbeef', - balance: '1000000000000000' // 0.001 ETH + nativeBalanceRegistry: { + '0x1': '1000000000000000' // 0.001 ETH + } }], mockAssetPrices, [], [] )) @@ -383,7 +389,9 @@ describe('useTransactionParser hook', () => { [{ ...mockAccount, address: '0xdeadbeef', - balance: '1003150000000000000' // 1.00315 ETH + nativeBalanceRegistry: { + '0x1': '1003150000000000000' // 1.00315 ETH + } }], mockAssetPrices, [], [] )) @@ -430,7 +438,9 @@ describe('useTransactionParser hook', () => { [mockERC20Token.contractAddress.toLowerCase()]: '1000000000000000' // 0.001 DOG }, address: '0xdeadbeef', - balance: '3150000000000000' // 0.00315 ETH + nativeBalanceRegistry: { + '0x1': '3150000000000000' // 0.00315 ETH + } }], mockAssetPrices, [], [] )) @@ -480,7 +490,9 @@ describe('useTransactionParser hook', () => { [mockERC20Token.contractAddress.toLowerCase()]: '1000000000000000000' // 1 DOG }, address: '0xdeadbeef', - balance: '3150000000000000' // 0.00315 ETH + nativeBalanceRegistry: { + '0x1': '3150000000000000' // 0.00315 ETH + } }], mockAssetPrices, [], diff --git a/components/brave_wallet_ui/common/hooks/transaction-parser.ts b/components/brave_wallet_ui/common/hooks/transaction-parser.ts index 52d4dcfc1a9c..25b683f7a5d6 100644 --- a/components/brave_wallet_ui/common/hooks/transaction-parser.ts +++ b/components/brave_wallet_ui/common/hooks/transaction-parser.ts @@ -128,7 +128,7 @@ export function useTransactionParser ( [selectedNetwork] ) const { findAssetPrice, computeFiatAmount } = usePricing(spotPrices) - const getBalance = useBalance(selectedNetwork) + const getBalance = useBalance([selectedNetwork]) const getAddressLabel = useAddressLabels(accounts) const networkSpotPrice = React.useMemo( diff --git a/components/brave_wallet_ui/common/reducers/wallet_reducer.ts b/components/brave_wallet_ui/common/reducers/wallet_reducer.ts index 88fa57d6cca9..0f483f5fe647 100644 --- a/components/brave_wallet_ui/common/reducers/wallet_reducer.ts +++ b/components/brave_wallet_ui/common/reducers/wallet_reducer.ts @@ -8,10 +8,8 @@ import { AccountInfo, AccountTransactions, BraveWallet, - GetAllNetworksList, - GetAllTokensReturnInfo, GetBlockchainTokenBalanceReturnInfo, - GetNativeAssetBalancesReturnInfo, + GetNativeAssetBalancesPayload, GetPriceHistoryReturnInfo, PortfolioTokenHistoryAndInfo, WalletAccountType, @@ -41,6 +39,7 @@ const defaultState: WalletState = { hasInitialized: false, isFilecoinEnabled: false, isSolanaEnabled: false, + isTestNetworksEnabled: true, isWalletCreated: false, isWalletLocked: true, favoriteApps: [], @@ -58,9 +57,9 @@ const defaultState: WalletState = { decimals: 18, coin: BraveWallet.CoinType.ETH, data: { - ethData: { + ethData: { isEip1559: true - } + } } } as BraveWallet.NetworkInfo, accounts: [], @@ -81,11 +80,13 @@ const defaultState: WalletState = { gasEstimates: undefined, connectedAccounts: [], isMetaMaskInstalled: false, + selectedCoin: BraveWallet.CoinType.ETH, defaultCurrencies: { fiat: '', crypto: '' }, - transactionProviderErrorRegistry: {} + transactionProviderErrorRegistry: {}, + defaultNetworks: [] as BraveWallet.NetworkInfo[] } const reducer = createReducer({}, defaultState) @@ -103,10 +104,10 @@ reducer.on(WalletActions.initialized, (state: any, payload: WalletInfo) => { id: `${idx + 1}`, name: info.name, address: info.address, - balance: '', accountType: getAccountType(info), deviceId: info.hardware ? info.hardware.deviceId : '', tokenBalanceRegistry: {}, + nativeBalanceRegistry: {}, coin: info.coin } as WalletAccountType }) @@ -144,47 +145,40 @@ reducer.on(WalletActions.setSelectedAccount, (state: any, payload: WalletAccount reducer.on(WalletActions.setNetwork, (state: any, payload: BraveWallet.NetworkInfo) => { return { ...state, - isFetchingPortfolioPriceHistory: true, selectedNetwork: payload } }) reducer.on(WalletActions.setVisibleTokensInfo, (state: WalletState, payload: BraveWallet.BlockchainToken[]) => { - const userVisibleTokensInfo = payload.map((token) => ({ - ...token, - logo: `chrome://erc-token-images/${token.logo}` - })) as BraveWallet.BlockchainToken[] - return { ...state, - userVisibleTokensInfo + userVisibleTokensInfo: payload } }) -reducer.on(WalletActions.setAllNetworks, (state: any, payload: GetAllNetworksList) => { +reducer.on(WalletActions.setAllNetworks, (state: WalletState, payload: BraveWallet.NetworkInfo[]) => { return { ...state, - networkList: payload.networks + networkList: payload } }) -reducer.on(WalletActions.setAllTokensList, (state: WalletState, payload: GetAllTokensReturnInfo) => { +reducer.on(WalletActions.setAllTokensList, (state: WalletState, payload: BraveWallet.BlockchainToken[]) => { return { ...state, - fullTokenList: payload.tokens.map(token => ({ - ...token, - logo: `chrome://erc-token-images/${token.logo}` - })) + fullTokenList: payload } }) -reducer.on(WalletActions.nativeAssetBalancesUpdated, (state: WalletState, payload: GetNativeAssetBalancesReturnInfo) => { +reducer.on(WalletActions.nativeAssetBalancesUpdated, (state: WalletState, payload: GetNativeAssetBalancesPayload) => { let accounts: WalletAccountType[] = [...state.accounts] - accounts.forEach((account, index) => { - if (payload.balances[index].error === BraveWallet.ProviderError.kSuccess) { - accounts[index].balance = Amount.normalize(payload.balances[index].balance) - } + accounts.forEach((account, accountIndex) => { + payload.balances[accountIndex].forEach((info, tokenIndex) => { + if (info.error === BraveWallet.ProviderError.kSuccess) { + accounts[accountIndex].nativeBalanceRegistry[info.chainId] = Amount.normalize(info.balance) + } + }) }) // Refresh selectedAccount object @@ -462,4 +456,18 @@ reducer.on(WalletActions.setTransactionProviderError, (state: WalletState, paylo } }) +reducer.on(WalletActions.setSelectedCoin, (state: WalletState, payload: BraveWallet.CoinType) => { + return { + ...state, + selectedCoin: payload + } +}) + +reducer.on(WalletActions.setDefaultNetworks, (state: WalletState, payload: BraveWallet.NetworkInfo[]) => { + return { + ...state, + defaultNetworks: payload + } +}) + export default reducer diff --git a/components/brave_wallet_ui/components/buy-send-swap/accounts-assets-networks/index.tsx b/components/brave_wallet_ui/components/buy-send-swap/accounts-assets-networks/index.tsx index fdf1dc2234b7..16f023ec7578 100644 --- a/components/brave_wallet_ui/components/buy-send-swap/accounts-assets-networks/index.tsx +++ b/components/brave_wallet_ui/components/buy-send-swap/accounts-assets-networks/index.tsx @@ -2,7 +2,8 @@ import * as React from 'react' import { BraveWallet, BuySendSwapViewTypes, - UserAccountType + UserAccountType, + WalletAccountType } from '../../../constants/types' import { @@ -18,7 +19,7 @@ import { export interface Props { selectedView: BuySendSwapViewTypes - accounts: UserAccountType[] + accounts: WalletAccountType[] networkList: BraveWallet.NetworkInfo[] assetOptions: BraveWallet.BlockchainToken[] selectedNetwork: BraveWallet.NetworkInfo diff --git a/components/brave_wallet_ui/components/buy-send-swap/select-account-with-header/index.tsx b/components/brave_wallet_ui/components/buy-send-swap/select-account-with-header/index.tsx index 94d6616fc4c5..2c7c3436a239 100644 --- a/components/brave_wallet_ui/components/buy-send-swap/select-account-with-header/index.tsx +++ b/components/brave_wallet_ui/components/buy-send-swap/select-account-with-header/index.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { UserAccountType } from '../../../constants/types' +import { UserAccountType, WalletAccountType } from '../../../constants/types' import { SearchBar, SelectAccount } from '../../shared' import Header from '../../buy-send-swap/select-header' import { getLocale } from '../../../../common/locale' @@ -10,7 +10,7 @@ import { } from '../shared-styles' export interface Props { - accounts: UserAccountType[] + accounts: WalletAccountType[] selectedAccount: UserAccountType onSelectAccount: (account: UserAccountType) => () => void onAddAccount?: () => void @@ -19,8 +19,15 @@ export interface Props { } function SelectAccountWithHeader (props: Props) { - const { accounts, selectedAccount, onSelectAccount, onBack, onAddAccount, hasAddButton } = props - const [filteredAccountList, setFilteredAccountList] = React.useState(accounts) + const { + accounts, + selectedAccount, + onSelectAccount, + onBack, + onAddAccount, + hasAddButton + } = props + const [filteredAccountList, setFilteredAccountList] = React.useState(accounts) const filterAccountList = (event: any) => { const search = event.target.value diff --git a/components/brave_wallet_ui/components/buy-send-swap/tabs/buy-tab.tsx b/components/brave_wallet_ui/components/buy-send-swap/tabs/buy-tab.tsx index 50906f84530c..b23ef27581ab 100644 --- a/components/brave_wallet_ui/components/buy-send-swap/tabs/buy-tab.tsx +++ b/components/brave_wallet_ui/components/buy-send-swap/tabs/buy-tab.tsx @@ -3,7 +3,8 @@ import { UserAccountType, BuySendSwapViewTypes, BraveWallet, - DefaultCurrencies + DefaultCurrencies, + WalletAccountType } from '../../../constants/types' import { AccountsAssetsNetworks, @@ -12,7 +13,7 @@ import { } from '..' export interface Props { - accounts: UserAccountType[] + accounts: WalletAccountType[] networkList: BraveWallet.NetworkInfo[] selectedNetwork: BraveWallet.NetworkInfo selectedAccount: UserAccountType diff --git a/components/brave_wallet_ui/components/buy-send-swap/tabs/send-tab.tsx b/components/brave_wallet_ui/components/buy-send-swap/tabs/send-tab.tsx index 954f5aa9c238..1c5420366865 100644 --- a/components/brave_wallet_ui/components/buy-send-swap/tabs/send-tab.tsx +++ b/components/brave_wallet_ui/components/buy-send-swap/tabs/send-tab.tsx @@ -4,7 +4,8 @@ import { BuySendSwapViewTypes, ToOrFromType, BraveWallet, - AmountValidationErrorType + AmountValidationErrorType, + WalletAccountType } from '../../../constants/types' import { AccountsAssetsNetworks, @@ -13,7 +14,7 @@ import { } from '..' export interface Props { - accounts: UserAccountType[] + accounts: WalletAccountType[] selectedAsset: BraveWallet.BlockchainToken | undefined selectedNetwork: BraveWallet.NetworkInfo selectedAccount: UserAccountType diff --git a/components/brave_wallet_ui/components/buy-send-swap/tabs/swap-tab.tsx b/components/brave_wallet_ui/components/buy-send-swap/tabs/swap-tab.tsx index a724e56dd761..489da1d0740b 100644 --- a/components/brave_wallet_ui/components/buy-send-swap/tabs/swap-tab.tsx +++ b/components/brave_wallet_ui/components/buy-send-swap/tabs/swap-tab.tsx @@ -7,7 +7,8 @@ import { ExpirationPresetObjectType, ToOrFromType, BraveWallet, - SwapValidationErrorType + SwapValidationErrorType, + WalletAccountType } from '../../../constants/types' import { AccountsAssetsNetworks, @@ -16,7 +17,7 @@ import { } from '..' export interface Props { - accounts: UserAccountType[] + accounts: WalletAccountType[] networkList: BraveWallet.NetworkInfo[] orderType: OrderTypes swapToAsset: BraveWallet.BlockchainToken | undefined diff --git a/components/brave_wallet_ui/components/desktop/asset-watchlist-item/index.tsx b/components/brave_wallet_ui/components/desktop/asset-watchlist-item/index.tsx index a367bb78a02d..25ad02a19427 100644 --- a/components/brave_wallet_ui/components/desktop/asset-watchlist-item/index.tsx +++ b/components/brave_wallet_ui/components/desktop/asset-watchlist-item/index.tsx @@ -1,11 +1,17 @@ import * as React from 'react' -import { Checkbox } from 'brave-ui' + +// Types import { BraveWallet } from '../../../constants/types' -import { withPlaceholderIcon } from '../../shared' // Utils +import { getTokensNetwork } from '../../../utils/network-utils' +import { getLocale } from '../../../../common/locale' import Amount from '../../../utils/amount' +// Components +import { withPlaceholderIcon } from '../../shared' +import { Checkbox } from 'brave-ui' + // Styled Components import { StyledWrapper, @@ -25,7 +31,7 @@ export interface Props { isCustom: boolean isSelected: boolean token: BraveWallet.BlockchainToken - selectedNetwork: BraveWallet.NetworkInfo + networkList: BraveWallet.NetworkInfo[] } const AssetWatchlistItem = (props: Props) => { @@ -35,7 +41,7 @@ const AssetWatchlistItem = (props: Props) => { isCustom, token, isSelected, - selectedNetwork + networkList } = props const onCheck = (key: string, selected: boolean) => { @@ -54,10 +60,23 @@ const AssetWatchlistItem = (props: Props) => { return withPlaceholderIcon(AssetIcon, { size: 'big', marginLeft: 0, marginRight: 8 }) }, []) + const tokensNetwork = React.useMemo(() => { + if (!token) { + return + } + return getTokensNetwork(networkList, token) + }, [token, networkList]) + + const networkDescription = React.useMemo(() => { + return getLocale('braveWalletPortfolioAssetNetworkDescription') + .replace('$1', token.symbol) + .replace('$2', tokensNetwork?.chainName ?? '') + }, [tokensNetwork, token]) + return ( - + {token.name} { @@ -66,7 +85,7 @@ const AssetWatchlistItem = (props: Props) => { : '' } - {token.symbol} + {networkDescription} diff --git a/components/brave_wallet_ui/components/desktop/popup-modals/edit-visible-assets-modal/index.tsx b/components/brave_wallet_ui/components/desktop/popup-modals/edit-visible-assets-modal/index.tsx index ac1f9453836a..698d1d369d5c 100644 --- a/components/brave_wallet_ui/components/desktop/popup-modals/edit-visible-assets-modal/index.tsx +++ b/components/brave_wallet_ui/components/desktop/popup-modals/edit-visible-assets-modal/index.tsx @@ -42,6 +42,7 @@ export interface Props { fullAssetList: BraveWallet.BlockchainToken[] userVisibleTokensInfo: BraveWallet.BlockchainToken[] selectedNetwork: BraveWallet.NetworkInfo + networkList: BraveWallet.NetworkInfo[] onFindTokenInfoByContractAddress: (contractAddress: string) => void foundTokenInfoByContractAddress?: BraveWallet.BlockchainToken } @@ -52,6 +53,7 @@ const EditVisibleAssetsModal = (props: Props) => { userVisibleTokensInfo, addUserAssetError, selectedNetwork, + networkList, onClose, onAddCustomAsset, onFindTokenInfoByContractAddress, @@ -172,7 +174,8 @@ const EditVisibleAssetsModal = (props: Props) => { symbol: selectedNetwork.symbol, visible: true, tokenId: '', - coingeckoId: '' + coingeckoId: '', + chainId: '' } const tokenList = React.useMemo(() => { @@ -232,6 +235,7 @@ const EditVisibleAssetsModal = (props: Props) => { let token = foundTokenInfoByContractAddress token.tokenId = tokenID ? new Amount(tokenID).toHex() : '' token.logo = iconURL + token.chainId = selectedNetwork.chainId setIsLoading(true) onAddCustomAsset(token) return @@ -239,6 +243,7 @@ const EditVisibleAssetsModal = (props: Props) => { let foundToken = foundTokenInfoByContractAddress foundToken.coingeckoId = coingeckoID !== '' ? coingeckoID : foundTokenInfoByContractAddress.coingeckoId foundToken.logo = iconURL + foundToken.chainId = selectedNetwork.chainId onAddCustomAsset(foundToken) } else { const newToken: BraveWallet.BlockchainToken = { @@ -251,7 +256,8 @@ const EditVisibleAssetsModal = (props: Props) => { tokenId: tokenID ? new Amount(tokenID).toHex() : '', logo: iconURL, visible: true, - coingeckoId: coingeckoID + coingeckoId: coingeckoID, + chainId: selectedNetwork.chainId } onAddCustomAsset(newToken) } @@ -490,10 +496,10 @@ const EditVisibleAssetsModal = (props: Props) => { <> {filteredTokenList.map((token) => { token, defaultCurrencies, hideBalances, - selectedNetwork, - isPanel + isPanel, + networks } = props const [assetNameSkeletonWidth, setAssetNameSkeletonWidth] = React.useState(0) const [assetNetworkSkeletonWidth, setAssetNetworkSkeletonWidth] = React.useState(0) @@ -78,14 +80,18 @@ const PortfolioAssetItem = (props: Props) => { return formattedAssetBalance === '' }, [formattedAssetBalance]) + const tokensNetwork = React.useMemo(() => { + return getTokensNetwork(networks, token) + }, [token, networks]) + const NetworkDescription = React.useMemo(() => { - if (selectedNetwork && token.contractAddress !== '' && !isPanel) { + if (tokensNetwork && !isPanel) { return getLocale('braveWalletPortfolioAssetNetworkDescription') .replace('$1', token.symbol) - .replace('$2', selectedNetwork?.chainName ?? '') + .replace('$2', tokensNetwork.chainName ?? '') } return token.symbol - }, [selectedNetwork, token]) + }, [tokensNetwork, token]) React.useEffect(() => { // Randow value between 100 & 250 @@ -109,37 +115,37 @@ const PortfolioAssetItem = (props: Props) => { {isLoading ? + circle={true} + width={40} + height={40} + /> : <> - - {selectedNetwork && token.contractAddress !== '' && !isPanel && - - - - } - + + {tokensNetwork && token.contractAddress !== '' && !isPanel && + + + + } + } {isLoading ? <> - - - - + + + + : <> - - {token.name} { + + {token.name} { token.isErc721 && token.tokenId ? '#' + new Amount(token.tokenId).toNumber() : '' - } - - {NetworkDescription} - + } + + {NetworkDescription} + } @@ -150,14 +156,14 @@ const PortfolioAssetItem = (props: Props) => { > {isLoading ? <> - - + + : <> - {!token.isErc721 && - {formattedFiatBalance} - } - {formattedAssetBalance} - + {!token.isErc721 && + {formattedFiatBalance} + } + {formattedAssetBalance} + } diff --git a/components/brave_wallet_ui/components/desktop/views/accounts/index.tsx b/components/brave_wallet_ui/components/desktop/views/accounts/index.tsx index fada4ef404b8..c0d181114af0 100644 --- a/components/brave_wallet_ui/components/desktop/views/accounts/index.tsx +++ b/components/brave_wallet_ui/components/desktop/views/accounts/index.tsx @@ -7,7 +7,8 @@ import { AccountTransactions, BraveWallet, DefaultCurrencies, - AddAccountNavTypes + AddAccountNavTypes, + CoinTypesMap } from '../../../../constants/types' import { reduceAddress } from '../../../../utils/reduce-address' import { copyToClipboard } from '../../../../utils/copy-to-clipboard' @@ -61,6 +62,7 @@ export interface Props { transactions: AccountTransactions privateKey: string selectedNetwork: BraveWallet.NetworkInfo + networkList: BraveWallet.NetworkInfo[] userVisibleTokensInfo: BraveWallet.BlockchainToken[] transactionSpotPrices: BraveWallet.AssetPrice[] selectedAccount: WalletAccountType | undefined @@ -89,6 +91,7 @@ function Accounts (props: Props) { userVisibleTokensInfo, selectedAccount, defaultCurrencies, + networkList, goBack, onSelectAccount, onSelectAsset, @@ -103,7 +106,7 @@ function Accounts (props: Props) { onCancelTransaction } = props - const getBalance = useBalance(selectedNetwork) + const getBalance = useBalance(networkList) const groupById = (accounts: WalletAccountType[], key: string) => { return accounts.reduce((result, obj) => { @@ -180,9 +183,29 @@ function Accounts (props: Props) { } }, [selectedAccount, transactions]) + const accountsTokensList = React.useMemo(() => { + if (!selectedAccount) { + return [] + } + // Since LOCALHOST's chainId is shared between coinType's + // this check will make sure we are returning the correct + // LOCALHOST asset for each account. + const coinName = CoinTypesMap[selectedAccount?.coin ?? 0] + const localHostCoins = userVisibleTokensInfo.filter((token) => token.chainId === BraveWallet.LOCALHOST_CHAIN_ID) + const accountsLocalHost = localHostCoins.find((token) => token.symbol.toUpperCase() === coinName) + const chainList = networkList.filter((network) => network.coin === selectedAccount?.coin).map((network) => network.chainId) ?? [] + const list = + userVisibleTokensInfo.filter((token) => chainList.includes(token?.chainId ?? '') && + token.chainId !== BraveWallet.LOCALHOST_CHAIN_ID) ?? [] + if (accountsLocalHost) { + return [...list, accountsLocalHost] + } + return list + }, [userVisibleTokensInfo, selectedAccount, networkList]) + const erc271Tokens = React.useMemo(() => - userVisibleTokensInfo.filter((token) => token.isErc721), - [userVisibleTokensInfo] + accountsTokensList.filter((token) => token.isErc721), + [accountsTokensList] ) return ( @@ -283,13 +306,13 @@ function Accounts (props: Props) { {getLocale('braveWalletAccountsAssets')} - {userVisibleTokensInfo.filter((token) => !token.isErc721).map((item) => + {accountsTokensList.filter((token) => !token.isErc721).map((item) => )} @@ -301,10 +324,10 @@ function Accounts (props: Props) { {erc271Tokens?.map((item) => diff --git a/components/brave_wallet_ui/components/desktop/views/crypto/index.tsx b/components/brave_wallet_ui/components/desktop/views/crypto/index.tsx index 2dae36ea8e1c..e2ab442e9304 100644 --- a/components/brave_wallet_ui/components/desktop/views/crypto/index.tsx +++ b/components/brave_wallet_ui/components/desktop/views/crypto/index.tsx @@ -40,7 +40,6 @@ export interface Props { onUpdateAccountName: (payload: UpdateAccountNamePayloadType) => { success: boolean } onShowAddModal: () => void onHideAddModal: () => void - onSelectNetwork: (network: BraveWallet.NetworkInfo) => void onRemoveAccount: (address: string, hardware: boolean, coin: BraveWallet.CoinType) => void onViewPrivateKey: (address: string, isDefault: boolean, coin: BraveWallet.CoinType) => void onDoneViewingPrivateKey: () => void @@ -101,7 +100,6 @@ const CryptoView = (props: Props) => { onImportAccount, onImportFilecoinAccount, onUpdateAccountName, - onSelectNetwork, onRemoveAccount, onViewPrivateKey, onDoneViewingPrivateKey, @@ -167,7 +165,9 @@ const CryptoView = (props: Props) => { if (id === 'add-asset') { onShowVisibleAssetsModal(true) } else { - const asset = userVisibleTokensInfo.find((token) => token.symbol.toLowerCase() === id?.toLowerCase()) + const asset = id?.toLowerCase().startsWith('0x') + ? userVisibleTokensInfo.find((token) => token.contractAddress === id) + : userVisibleTokensInfo.find((token) => token.symbol.toLowerCase() === id?.toLowerCase()) onSelectAsset(asset) setHideNav(true) } @@ -218,7 +218,11 @@ const CryptoView = (props: Props) => { const selectAsset = (asset: BraveWallet.BlockchainToken | undefined) => { if (asset) { - history.push(`${WalletRoutes.Portfolio}/${asset.symbol}`) + if (asset.contractAddress === '') { + history.push(`${WalletRoutes.Portfolio}/${asset.symbol}`) + return + } + history.push(`${WalletRoutes.Portfolio}/${asset.contractAddress}`) } else { onSelectAsset(asset) history.push(WalletRoutes.Portfolio) @@ -306,7 +310,6 @@ const CryptoView = (props: Props) => { onSelectAsset={selectAsset} onSelectAccount={onSelectAccount} onClickAddAccount={onClickAddAccount} - onSelectNetwork={onSelectNetwork} onAddCustomAsset={onAddCustomAsset} selectedAsset={selectedAsset} portfolioBalance={portfolioBalance} @@ -354,6 +357,7 @@ const CryptoView = (props: Props) => { onRetryTransaction={onRetryTransaction} onSpeedupTransaction={onSpeedupTransaction} onCancelTransaction={onCancelTransaction} + networkList={networkList} /> diff --git a/components/brave_wallet_ui/components/desktop/views/portfolio/components/accounts-and-transctions-list/index.tsx b/components/brave_wallet_ui/components/desktop/views/portfolio/components/accounts-and-transctions-list/index.tsx index 4cb230cef679..5ae044d17c78 100644 --- a/components/brave_wallet_ui/components/desktop/views/portfolio/components/accounts-and-transctions-list/index.tsx +++ b/components/brave_wallet_ui/components/desktop/views/portfolio/components/accounts-and-transctions-list/index.tsx @@ -41,6 +41,7 @@ export interface Props { selectedAsset: BraveWallet.BlockchainToken | undefined accounts: WalletAccountType[] selectedNetwork: BraveWallet.NetworkInfo + networkList: BraveWallet.NetworkInfo[] fullAssetFiatBalance: Amount formattedFullAssetBalance: string selectedAssetTransactions: BraveWallet.TransactionInfo[] @@ -66,6 +67,7 @@ const AccountsAndTransactionsList = (props: Props) => { selectedAssetTransactions, userVisibleTokensInfo, hideBalances, + networkList, onSelectAccount, onClickAddAccount, onSelectAsset, @@ -74,7 +76,7 @@ const AccountsAndTransactionsList = (props: Props) => { onSpeedupTransaction } = props - const getBalance = useBalance(selectedNetwork) + const getBalance = useBalance(networkList) const findAccount = (address: string): WalletAccountType | undefined => { return accounts.find((account) => address === account.address) @@ -82,7 +84,7 @@ const AccountsAndTransactionsList = (props: Props) => { const accountsList = React.useMemo(() => { if (selectedAsset?.isErc721) { - return accounts.filter((account) => Number(account.balance) !== 0) + return accounts.filter((account) => Number(account.nativeBalanceRegistry[selectedNetwork.chainId] ?? 0) !== 0) } return accounts }, [selectedAsset, accounts]) diff --git a/components/brave_wallet_ui/components/desktop/views/portfolio/components/token-lists/index.tsx b/components/brave_wallet_ui/components/desktop/views/portfolio/components/token-lists/index.tsx index ee2ecf945903..e595393e1187 100644 --- a/components/brave_wallet_ui/components/desktop/views/portfolio/components/token-lists/index.tsx +++ b/components/brave_wallet_ui/components/desktop/views/portfolio/components/token-lists/index.tsx @@ -31,7 +31,7 @@ export interface Props { defaultCurrencies: DefaultCurrencies userAssetList: UserAssetInfoType[] hideBalances: boolean - selectedNetwork: BraveWallet.NetworkInfo + networks: BraveWallet.NetworkInfo[] onSetFilteredAssetList: (filteredList: UserAssetInfoType[]) => void onSelectAsset: (asset: BraveWallet.BlockchainToken | undefined) => () => void onShowAssetModal: () => void @@ -44,7 +44,7 @@ const TokenLists = (props: Props) => { defaultCurrencies, userAssetList, hideBalances, - selectedNetwork, + networks, onSelectAsset, onShowAssetModal, onSetFilteredAssetList @@ -78,11 +78,11 @@ const TokenLists = (props: Props) => { spotPrices={tokenPrices} defaultCurrencies={defaultCurrencies} action={onSelectAsset(item.asset)} - key={item.asset.contractAddress} + key={`${item.asset.contractAddress}-${item.asset.symbol}-${item.asset.chainId}`} assetBalance={item.assetBalance} token={item.asset} hideBalances={hideBalances} - selectedNetwork={selectedNetwork} + networks={networks} /> )} {erc721TokenList.length !== 0 && @@ -95,11 +95,11 @@ const TokenLists = (props: Props) => { spotPrices={tokenPrices} defaultCurrencies={defaultCurrencies} action={onSelectAsset(item.asset)} - key={item.asset.contractAddress} + key={`${item.asset.contractAddress}-${item.asset.tokenId}-${item.asset.chainId}`} assetBalance={item.assetBalance} token={item.asset} hideBalances={hideBalances} - selectedNetwork={selectedNetwork} + networks={networks} /> )} diff --git a/components/brave_wallet_ui/components/desktop/views/portfolio/index.tsx b/components/brave_wallet_ui/components/desktop/views/portfolio/index.tsx index 7bc3c41d901c..e7ae7d78af21 100644 --- a/components/brave_wallet_ui/components/desktop/views/portfolio/index.tsx +++ b/components/brave_wallet_ui/components/desktop/views/portfolio/index.tsx @@ -8,13 +8,15 @@ import { WalletAccountType, UserAssetInfoType, DefaultCurrencies, - AddAccountNavTypes + AddAccountNavTypes, + SupportedTestNetworks } from '../../../../constants/types' import { getLocale } from '../../../../../common/locale' import { CurrencySymbols } from '../../../../utils/currency-symbols' // Utils import { sortTransactionByDate } from '../../../../utils/tx-utils' +import { getTokensNetwork, getTokensCoinType } from '../../../../utils/network-utils' import Amount from '../../../../utils/amount' // Options @@ -25,7 +27,6 @@ import { BackButton, LoadingSkeleton, withPlaceholderIcon } from '../../../share import { ChartControlBar, LineChart, - SelectNetworkDropdown, EditVisibleAssetsModal, WithHideBalancePlaceholder } from '../../' @@ -45,6 +46,7 @@ import { BalanceText, AssetIcon, AssetRow, + AssetColumn, PriceRow, AssetNameText, DetailText, @@ -54,7 +56,8 @@ import { PercentText, ArrowIcon, BalanceRow, - ShowBalanceButton + ShowBalanceButton, + NetworkDescription } from './style' export interface Props { @@ -63,7 +66,6 @@ export interface Props { onSelectAsset: (asset: BraveWallet.BlockchainToken | undefined) => void onSelectAccount: (account: WalletAccountType) => void onClickAddAccount: (tabId: AddAccountNavTypes) => () => void - onSelectNetwork: (network: BraveWallet.NetworkInfo) => void onAddCustomAsset: (token: BraveWallet.BlockchainToken) => void onShowVisibleAssetsModal: (showModal: boolean) => void onUpdateVisibleAssets: (updatedTokensList: BraveWallet.BlockchainToken[]) => void @@ -102,7 +104,6 @@ const Portfolio = (props: Props) => { onSelectAsset, onSelectAccount, onClickAddAccount, - onSelectNetwork, onAddCustomAsset, onShowVisibleAssetsModal, onUpdateVisibleAssets, @@ -134,7 +135,13 @@ const Portfolio = (props: Props) => { foundTokenInfoByContractAddress } = props - const [filteredAssetList, setfilteredAssetList] = React.useState(userAssetList) + // This will be removed once Network Filtering is integrated here + // https://github.com/brave/brave-browser/issues/20780 + const mainnetAssets = React.useMemo(() => { + return userAssetList.filter((asset) => !SupportedTestNetworks.includes(asset.asset.chainId)) + }, [userAssetList]) + + const [filteredAssetList, setfilteredAssetList] = React.useState(mainnetAssets) const [fullPortfolioFiatBalance, setFullPortfolioFiatBalance] = React.useState(portfolioBalance) const [hoverBalance, setHoverBalance] = React.useState() const [hoverPrice, setHoverPrice] = React.useState() @@ -142,10 +149,6 @@ const Portfolio = (props: Props) => { const [hideBalances, setHideBalances] = React.useState(false) const parseTransaction = useTransactionParser(selectedNetwork, accounts, transactionSpotPrices, userVisibleTokensInfo) - const toggleShowNetworkDropdown = () => { - setShowNetworkDropdown(!showNetworkDropdown) - } - const onSetFilteredAssetList = (filteredList: UserAssetInfoType[]) => { setfilteredAssetList(filteredList) } @@ -157,8 +160,8 @@ const Portfolio = (props: Props) => { }, [portfolioBalance]) React.useEffect(() => { - setfilteredAssetList(userAssetList) - }, [userAssetList]) + setfilteredAssetList(mainnetAssets) + }, [mainnetAssets]) const portfolioHistory = React.useMemo(() => { return portfolioPriceHistory @@ -171,7 +174,7 @@ const Portfolio = (props: Props) => { const goBack = () => { onSelectAsset(undefined) - setfilteredAssetList(userAssetList) + setfilteredAssetList(mainnetAssets) toggleNav() } @@ -191,11 +194,6 @@ const Portfolio = (props: Props) => { } } - const onClickSelectNetwork = (network: BraveWallet.NetworkInfo) => () => { - onSelectNetwork(network) - toggleShowNetworkDropdown() - } - const onHideNetworkDropdown = () => { if (showNetworkDropdown) { setShowNetworkDropdown(false) @@ -224,7 +222,18 @@ const Portfolio = (props: Props) => { }, [selectedAsset, transactions]) const fullAssetBalances = React.useMemo(() => { - return filteredAssetList.find((asset) => asset.asset.contractAddress.toLowerCase() === selectedAsset?.contractAddress.toLowerCase()) + if (selectedAsset?.contractAddress === '') { + return filteredAssetList.find( + (asset) => + asset.asset.symbol.toLowerCase() === selectedAsset?.symbol.toLowerCase() && + asset.asset.chainId === selectedAsset?.chainId + ) + } + return filteredAssetList.find( + (asset) => + asset.asset.contractAddress.toLowerCase() === selectedAsset?.contractAddress.toLowerCase() && + asset.asset.chainId === selectedAsset?.chainId + ) }, [filteredAssetList, selectedAsset]) const AssetIconWithPlaceholder = React.useMemo(() => { @@ -250,6 +259,21 @@ const Portfolio = (props: Props) => { setHideBalances(!hideBalances) } + const filteredAccountsByCoinType = React.useMemo(() => { + if (!selectedAsset) { + return [] + } + const coinType = getTokensCoinType(networkList, selectedAsset) + return accounts.filter((account) => account.coin === coinType) + }, [networkList, accounts, selectedAsset]) + + const selectedAssetsNetwork = React.useMemo(() => { + if (!selectedAsset) { + return + } + return getTokensNetwork(networkList, selectedAsset) + }, [selectedAsset, networkList]) + return ( @@ -259,15 +283,6 @@ const Portfolio = (props: Props) => { ) : ( )} - {!selectedAsset?.isErc721 && - - } {!selectedAsset?.isErc721 && @@ -300,10 +315,13 @@ const Portfolio = (props: Props) => { {!selectedAsset.isErc721 && - - {selectedAsset.name} + + + {selectedAsset.name} + {selectedAsset.symbol} on {selectedAssetsNetwork?.chainName ?? ''} + - {selectedAsset.name} {getLocale('braveWalletPrice')} ({selectedAsset.symbol}) + {/* {selectedAsset.name} {getLocale('braveWalletPrice')} ({selectedAsset.symbol}) */} {CurrencySymbols[defaultCurrencies.fiat]}{hoverPrice || (selectedAssetFiatPrice ? new Amount(selectedAssetFiatPrice.price).formatAsFiat() : 0.00)} @@ -345,13 +363,13 @@ const Portfolio = (props: Props) => { } */} { onRetryTransaction={onRetryTransaction} onSpeedupTransaction={onSpeedupTransaction} hideBalances={hideBalances} + networkList={networkList} /> {!selectedAsset && { onClose={toggleShowVisibleAssetModal} onAddCustomAsset={onAddCustomAsset} selectedNetwork={selectedNetwork} + networkList={networkList} onFindTokenInfoByContractAddress={onFindTokenInfoByContractAddress} foundTokenInfoByContractAddress={foundTokenInfoByContractAddress} onUpdateVisibleAssets={onUpdateVisibleAssets} diff --git a/components/brave_wallet_ui/components/desktop/views/portfolio/style.ts b/components/brave_wallet_ui/components/desktop/views/portfolio/style.ts index 39c769de3aab..95f03c89b881 100644 --- a/components/brave_wallet_ui/components/desktop/views/portfolio/style.ts +++ b/components/brave_wallet_ui/components/desktop/views/portfolio/style.ts @@ -81,7 +81,14 @@ export const AssetRow = styled.div` flex-direction: row; align-items: center; justify-content: center; - margin-bottom: 9px; + margin-bottom: 20px; +` + +export const AssetColumn = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: center; ` export const PriceRow = styled.div` @@ -100,6 +107,14 @@ export const AssetNameText = styled.span` color: ${(p) => p.theme.color.text01}; ` +export const NetworkDescription = styled.span` + font-family: Poppins; + font-size: 14px; + line-height: 16px; + letter-spacing: 0.02em; + color: ${(p) => p.theme.color.text02}; +` + export const DetailText = styled.span` font-family: Poppins; font-size: 13px; diff --git a/components/brave_wallet_ui/components/extension/assets-panel/index.tsx b/components/brave_wallet_ui/components/extension/assets-panel/index.tsx index a39b95120d80..364346d2fd25 100644 --- a/components/brave_wallet_ui/components/extension/assets-panel/index.tsx +++ b/components/brave_wallet_ui/components/extension/assets-panel/index.tsx @@ -21,7 +21,7 @@ export interface Props { spotPrices: BraveWallet.AssetPrice[] userAssetList: BraveWallet.BlockchainToken[] defaultCurrencies: DefaultCurrencies - selectedNetwork: BraveWallet.NetworkInfo + networkList: BraveWallet.NetworkInfo[] selectedAccount: WalletAccountType onAddAsset: () => void } @@ -31,12 +31,12 @@ const AssetsPanel = (props: Props) => { userAssetList, spotPrices, defaultCurrencies, - selectedNetwork, selectedAccount, + networkList, onAddAsset } = props - const getBalance = useBalance(selectedNetwork) + const getBalance = useBalance(networkList) const onClickAsset = (symbol: string) => () => { const url = `brave://wallet${WalletRoutes.Portfolio}/${symbol}` @@ -62,7 +62,7 @@ const AssetsPanel = (props: Props) => { key={asset.contractAddress} assetBalance={getBalance(selectedAccount, asset)} token={asset} - selectedNetwork={selectedNetwork} + networks={networkList} isPanel={true} /> )} diff --git a/components/brave_wallet_ui/components/extension/connected-panel/index.tsx b/components/brave_wallet_ui/components/extension/connected-panel/index.tsx index 02e4b943ac5e..92a9ea045bab 100644 --- a/components/brave_wallet_ui/components/extension/connected-panel/index.tsx +++ b/components/brave_wallet_ui/components/extension/connected-panel/index.tsx @@ -110,14 +110,14 @@ const ConnectedPanel = (props: Props) => { return !BuySupportedChains.includes(selectedNetwork.chainId) }, [BuySupportedChains, selectedNetwork]) - const formattedAssetBalance = new Amount(selectedAccount.balance) + const formattedAssetBalance = new Amount(selectedAccount.nativeBalanceRegistry[selectedNetwork.chainId] ?? '') .divideByDecimals(selectedNetwork.decimals) .formatAsAsset(6, selectedNetwork.symbol) const { computeFiatAmount } = usePricing(spotPrices) const selectedAccountFiatBalance = React.useMemo(() => computeFiatAmount( - selectedAccount.balance, selectedNetwork.symbol, selectedNetwork.decimals + selectedAccount.nativeBalanceRegistry[selectedNetwork.chainId], selectedNetwork.symbol, selectedNetwork.decimals ), [computeFiatAmount, selectedNetwork, selectedAccount]) const onClickViewOnBlockExplorer = useExplorer(selectedNetwork) diff --git a/components/brave_wallet_ui/components/shared/create-network-icon/index.tsx b/components/brave_wallet_ui/components/shared/create-network-icon/index.tsx index 73a38702ded1..a5a069d43cac 100644 --- a/components/brave_wallet_ui/components/shared/create-network-icon/index.tsx +++ b/components/brave_wallet_ui/components/shared/create-network-icon/index.tsx @@ -2,7 +2,7 @@ import * as React from 'react' import { create } from 'ethereum-blockies' // Constants -import { BraveWallet } from '../../../constants/types' +import { BraveWallet, SupportedTestNetworks } from '../../../constants/types' // Utils import { stripERC20TokenImageURL, isRemoteImageURL, isValidIconExtension } from '../../../utils/string-utils' @@ -46,16 +46,6 @@ function CreateNetworkIcon (props: Props) { return false }, [isRemoteURL, isDataURL, networkImageURL]) - // Will remove these hardcoded chainId's. - // We need to return a normal Ethereum Icon URL for Ethereum Mainnet - // and return a grayed out Ethereum Icon Url for Ethereum Test Networks from the backend. - const testNetworkList = [ - BraveWallet.RINKEBY_CHAIN_ID, - BraveWallet.ROPSTEN_CHAIN_ID, - BraveWallet.GOERLI_CHAIN_ID, - BraveWallet.KOVAN_CHAIN_ID, - BraveWallet.LOCALHOST_CHAIN_ID - ] const needsPlaceholder = nativeAsset.logo === '' && (networkImageURL === '' || !isValidIcon) const orb = React.useMemo(() => { @@ -86,7 +76,7 @@ function CreateNetworkIcon (props: Props) { return ( () => void } function SelectAccount (props: Props) { const { accounts, selectedAccount, onSelectAccount } = props + + // MULTICHAIN: Remove me once we support SOL and FIL transaction creation. + // Will be implemented in these 2 issues + // https://github.com/brave/brave-browser/issues/20698 + // https://github.com/brave/brave-browser/issues/20893 + const accountsList = React.useMemo(() => { + return accounts.filter((account) => + account.coin !== BraveWallet.CoinType.SOL && + account.coin !== BraveWallet.CoinType.FIL) + }, [accounts]) + return ( <> - {accounts.map((account) => + {accountsList.map((account) => { + return networks.filter( + (network) => + network.coin !== BraveWallet.CoinType.SOL && + network.coin !== BraveWallet.CoinType.FIL + ) + }, [networks]) + return ( <> - {networks.map((network) => + {networkList.map((network) => diff --git a/components/brave_wallet_ui/constants/types.ts b/components/brave_wallet_ui/constants/types.ts index 089edcb646f8..ea34ed9ccb26 100644 --- a/components/brave_wallet_ui/constants/types.ts +++ b/components/brave_wallet_ui/constants/types.ts @@ -24,7 +24,7 @@ export interface WalletAccountType { name: string address: string tokenBalanceRegistry: TokenBalanceRegistry - balance: string + nativeBalanceRegistry: TokenBalanceRegistry accountType: 'Primary' | 'Secondary' | 'Ledger' | 'Trezor' deviceId?: string coin: BraveWallet.CoinType @@ -189,6 +189,7 @@ export interface WalletState { hasInitialized: boolean isFilecoinEnabled: boolean isSolanaEnabled: boolean + isTestNetworksEnabled: boolean isWalletCreated: boolean isWalletLocked: boolean favoriteApps: BraveWallet.AppItem[] @@ -214,8 +215,10 @@ export interface WalletState { gasEstimates?: BraveWallet.GasEstimation1559 connectedAccounts: WalletAccountType[] isMetaMaskInstalled: boolean + selectedCoin: BraveWallet.CoinType defaultCurrencies: DefaultCurrencies transactionProviderErrorRegistry: TransactionProviderErrorRegistry + defaultNetworks: BraveWallet.NetworkInfo[] } export interface PanelState { @@ -335,7 +338,18 @@ export interface GetAllTokensReturnInfo { } export interface GetNativeAssetBalancesReturnInfo { - balances: BraveWallet.JsonRpcService_GetBalance_ResponseParams[] + balances: BraveWallet.JsonRpcService_GetBalance_ResponseParams[][] +} + +export interface BalancePayload { + balance: string + error: number + errorMessage: string + chainId: string +} + +export interface GetNativeAssetBalancesPayload { + balances: BalancePayload[][] } export interface GetBlockchainTokenBalanceReturnInfo { @@ -573,3 +587,26 @@ export interface TransactionProviderError { export interface TransactionProviderErrorRegistry { [transactionId: string]: TransactionProviderError } + +export const SupportedCoinTypes = [ + BraveWallet.CoinType.ETH, + BraveWallet.CoinType.SOL, + BraveWallet.CoinType.FIL +] + +export const SupportedTestNetworks = [ + BraveWallet.RINKEBY_CHAIN_ID, + BraveWallet.ROPSTEN_CHAIN_ID, + BraveWallet.GOERLI_CHAIN_ID, + BraveWallet.KOVAN_CHAIN_ID, + BraveWallet.LOCALHOST_CHAIN_ID, + BraveWallet.SOLANA_DEVNET, + BraveWallet.SOLANA_TESTNET, + BraveWallet.FILECOIN_TESTNET +] + +export enum CoinTypesMap { + ETH = BraveWallet.CoinType.ETH, + FIL = BraveWallet.CoinType.FIL, + SOL = BraveWallet.CoinType.SOL +} diff --git a/components/brave_wallet_ui/options/asset-options.ts b/components/brave_wallet_ui/options/asset-options.ts index 5b2d0dde4c52..be0ad891e317 100644 --- a/components/brave_wallet_ui/options/asset-options.ts +++ b/components/brave_wallet_ui/options/asset-options.ts @@ -10,7 +10,9 @@ import { BNBIconUrl, BTCIconUrl, ETHIconUrl, - ZRXIconUrl + ZRXIconUrl, + SOLIconUrl, + FILECOINIconUrl } from '../assets/asset-icons' import { CeloIcon, @@ -46,6 +48,14 @@ export function makeNetworkAsset (network: BraveWallet.NetworkInfo) { logo = CeloIcon break + case network.symbol.toUpperCase() === 'SOL': + logo = SOLIconUrl + break + + case network.symbol.toUpperCase() === 'FIL': + logo = FILECOINIconUrl + break + case network.symbol.toUpperCase() === 'ETH': logo = 'chrome://erc-token-images/eth.png' break @@ -64,7 +74,8 @@ export function makeNetworkAsset (network: BraveWallet.NetworkInfo) { decimals: network.decimals, visible: true, tokenId: '', - coingeckoId: '' + coingeckoId: '', + chainId: network.chainId } as BraveWallet.BlockchainToken } @@ -106,7 +117,8 @@ export const NewAssetOptions: BraveWallet.BlockchainToken[] = [ decimals: 18, visible: true, tokenId: '', - coingeckoId: '' + coingeckoId: '', + chainId: '0x1' }, { contractAddress: '2', @@ -118,7 +130,8 @@ export const NewAssetOptions: BraveWallet.BlockchainToken[] = [ decimals: 18, visible: true, tokenId: '', - coingeckoId: '' + coingeckoId: '', + chainId: '0x1' }, { contractAddress: '3', @@ -130,7 +143,8 @@ export const NewAssetOptions: BraveWallet.BlockchainToken[] = [ decimals: 18, visible: true, tokenId: '', - coingeckoId: '' + coingeckoId: '', + chainId: '0x3' }, { contractAddress: '4', @@ -142,7 +156,8 @@ export const NewAssetOptions: BraveWallet.BlockchainToken[] = [ decimals: 18, visible: true, tokenId: '', - coingeckoId: '' + coingeckoId: '', + chainId: '0x1' }, { contractAddress: '5', @@ -154,7 +169,8 @@ export const NewAssetOptions: BraveWallet.BlockchainToken[] = [ decimals: 18, visible: true, tokenId: '', - coingeckoId: '' + coingeckoId: '', + chainId: '0x1' }, { contractAddress: '6', @@ -166,7 +182,8 @@ export const NewAssetOptions: BraveWallet.BlockchainToken[] = [ decimals: 18, visible: true, tokenId: '', - coingeckoId: '' + coingeckoId: '', + chainId: '0x1' }, { contractAddress: '7', @@ -178,13 +195,17 @@ export const NewAssetOptions: BraveWallet.BlockchainToken[] = [ decimals: 0, visible: true, tokenId: '0x42a5', - coingeckoId: '' + coingeckoId: '', + chainId: '0x1' } ] // Use only with storybook as dummy data. export const AccountAssetOptions: BraveWallet.BlockchainToken[] = [ - ETH, + { + ...ETH, + chainId: '0x1' + }, { contractAddress: '0x0D8775F648430679A709E98d2b0Cb6250d2887EF', name: 'Basic Attention Token', @@ -195,7 +216,8 @@ export const AccountAssetOptions: BraveWallet.BlockchainToken[] = [ decimals: 18, visible: true, tokenId: '', - coingeckoId: '' + coingeckoId: '', + chainId: '0x1' }, { contractAddress: '3', @@ -207,7 +229,8 @@ export const AccountAssetOptions: BraveWallet.BlockchainToken[] = [ decimals: 8, visible: true, tokenId: '', - coingeckoId: '' + coingeckoId: '', + chainId: '0x1' }, { contractAddress: '4', @@ -219,7 +242,8 @@ export const AccountAssetOptions: BraveWallet.BlockchainToken[] = [ decimals: 8, visible: true, tokenId: '', - coingeckoId: '' + coingeckoId: '', + chainId: '0x1' }, { contractAddress: '5', @@ -231,7 +255,8 @@ export const AccountAssetOptions: BraveWallet.BlockchainToken[] = [ decimals: 8, visible: true, tokenId: '', - coingeckoId: '' + coingeckoId: '', + chainId: '0x1' }, { contractAddress: '0xE41d2489571d322189246DaFA5ebDe1F4699F498', @@ -243,6 +268,7 @@ export const AccountAssetOptions: BraveWallet.BlockchainToken[] = [ decimals: 18, visible: true, tokenId: '', - coingeckoId: '' + coingeckoId: '', + chainId: '0x1' } ] diff --git a/components/brave_wallet_ui/page/async/wallet_page_async_handler.ts b/components/brave_wallet_ui/page/async/wallet_page_async_handler.ts index f6f2dca131e9..95d5ec019fd6 100644 --- a/components/brave_wallet_ui/page/async/wallet_page_async_handler.ts +++ b/components/brave_wallet_ui/page/async/wallet_page_async_handler.ts @@ -31,7 +31,7 @@ import { import { NewUnapprovedTxAdded } from '../../common/constants/action_types' import { fetchSwapQuoteFactory } from '../../common/async/handlers' import { Store } from '../../common/async/types' -import { GetTokenParam } from '../../utils/api-utils' +import { getTokenParam } from '../../utils/api-utils' const handler = new AsyncActionHandler() @@ -87,11 +87,10 @@ handler.on(WalletPageActions.selectAsset.getType(), async (store: Store, payload const walletState = getWalletState(store) const defaultFiat = walletState.defaultCurrencies.fiat.toLowerCase() const defaultCrypto = walletState.defaultCurrencies.crypto.toLowerCase() - const selectedNetwork = walletState.selectedNetwork if (payload.asset) { const selectedAsset = payload.asset - const defaultPrices = await assetRatioService.getPrice([GetTokenParam(selectedNetwork, selectedAsset)], [defaultFiat, defaultCrypto], payload.timeFrame) - const priceHistory = await assetRatioService.getPriceHistory(GetTokenParam(selectedNetwork, selectedAsset), defaultFiat, payload.timeFrame) + const defaultPrices = await assetRatioService.getPrice([getTokenParam(selectedAsset)], [defaultFiat, defaultCrypto], payload.timeFrame) + const priceHistory = await assetRatioService.getPriceHistory(getTokenParam(selectedAsset), defaultFiat, payload.timeFrame) store.dispatch(WalletPageActions.updatePriceInfo({ priceHistory: priceHistory, defaultFiatPrice: defaultPrices.values[0], defaultCryptoPrice: defaultPrices.values[1], timeFrame: payload.timeFrame })) } else { store.dispatch(WalletPageActions.updatePriceInfo({ priceHistory: undefined, defaultFiatPrice: undefined, defaultCryptoPrice: undefined, timeFrame: payload.timeFrame })) diff --git a/components/brave_wallet_ui/page/container.tsx b/components/brave_wallet_ui/page/container.tsx index 66572cd38e3a..9da0d3c9e50c 100644 --- a/components/brave_wallet_ui/page/container.tsx +++ b/components/brave_wallet_ui/page/container.tsx @@ -27,7 +27,8 @@ import { WalletAccountType, WalletPageState, WalletRoutes, - WalletState + WalletState, + SupportedTestNetworks } from '../constants/types' // import { NavOptions } from '../options/side-nav-options' import BuySendSwap from '../stories/screens/buy-send-swap' @@ -38,6 +39,7 @@ import BackupWallet from '../stories/screens/backup-wallet' import Amount from '../utils/amount' import { GetBuyOrFaucetUrl } from '../utils/buy-asset-url' import { mojoTimeDeltaToJSDate } from '../../common/mojomUtils' +import { getTokensCoinType } from '../utils/network-utils' import { findENSAddress, @@ -134,8 +136,8 @@ function Container (props: Props) { sendAssetOptions, buyAssetOptions } = useAssets( - accounts, selectedAccount, + networkList, selectedNetwork, fullTokenList, userVisibleTokensInfo, @@ -176,6 +178,7 @@ function Container (props: Props) { } = useSwap( selectedAccount, selectedNetwork, + networkList, swapAssetOptions, props.walletPageActions.fetchPageSwapQuote, getERC20Allowance, @@ -217,20 +220,19 @@ function Container (props: Props) { props.walletActions.setUserAssetVisible, props.walletActions.removeUserAsset, props.walletActions.refreshBalancesAndPriceHistory, - selectedNetwork, fullTokenList, userVisibleTokensInfo ) const { computeFiatAmount } = usePricing(transactionSpotPrices) - const getAccountBalance = useBalance(selectedNetwork) + const getAccountBalance = useBalance(networkList) const sendAssetBalance = getAccountBalance(selectedAccount, selectedSendAsset) const fromAssetBalance = getAccountBalance(selectedAccount, fromAsset) const toAssetBalance = getAccountBalance(selectedAccount, toAsset) const onSelectPresetAmountFactory = usePreset( selectedAccount, - selectedNetwork, + networkList, onSetFromAmount, onSetSendAmount, fromAsset, @@ -327,15 +329,22 @@ function Container (props: Props) { // This will scrape all the user's accounts and combine the asset balances for a single asset const fullAssetBalance = React.useCallback((asset: BraveWallet.BlockchainToken) => { - const amounts = accounts.map((account) => + const tokensCoinType = getTokensCoinType(networkList, asset) + const amounts = accounts.filter((account) => account.coin === tokensCoinType).map((account) => getAccountBalance(account, asset)) + // If a user has not yet created a FIL or SOL account, + // we return 0 until they create an account + if (amounts.length === 0) { + return '0' + } + return amounts.reduce(function (a, b) { return a !== '' && b !== '' ? new Amount(a).plus(b).format() : '' }) - }, [accounts, getAccountBalance]) + }, [accounts, networkList, getAccountBalance]) // This looks at the users asset list and returns the full balance for each asset const userAssetList = React.useMemo(() => { @@ -351,8 +360,14 @@ function Container (props: Props) { // This will scrape all of the user's accounts and combine the fiat value for every asset const fullPortfolioBalance = React.useMemo(() => { + // Will remove testnetwork filter when this is implemented + // https://github.com/brave/brave-browser/issues/20780 const visibleAssetOptions = userAssetList - .filter((token) => token.asset.visible && !token.asset.isErc721) + .filter((token) => + token.asset.visible && + !token.asset.isErc721 && + !SupportedTestNetworks.includes(token.asset.chainId) + ) if (visibleAssetOptions.length === 0) { return '' @@ -506,7 +521,10 @@ function Container (props: Props) { React.useEffect(() => { // Creates a list of Accepted Portfolio Routes const acceptedPortfolioRoutes = userVisibleTokensInfo.map((token) => { - return `${WalletRoutes.Portfolio}/${token.symbol}` + if (token.contractAddress === '') { + return `${WalletRoutes.Portfolio}/${token.symbol}` + } + return `${WalletRoutes.Portfolio}/${token.contractAddress}` }) // Creates a list of Accepted Account Routes const acceptedAccountRoutes = accounts.map((account) => { @@ -664,7 +682,6 @@ function Container (props: Props) { onHideAddModal={onHideAddModal} onUpdateAccountName={onUpdateAccountName} selectedNetwork={selectedNetwork} - onSelectNetwork={onSelectNetwork} isFetchingPortfolioPriceHistory={isFetchingPortfolioPriceHistory} onRemoveAccount={onRemoveAccount} privateKey={privateKey ?? ''} diff --git a/components/brave_wallet_ui/panel/container.tsx b/components/brave_wallet_ui/panel/container.tsx index 5b69ec47c884..b737e93ad666 100644 --- a/components/brave_wallet_ui/panel/container.tsx +++ b/components/brave_wallet_ui/panel/container.tsx @@ -62,7 +62,7 @@ import { import { AppsList } from '../options/apps-list-options' import LockPanel from '../components/extension/lock-panel' import { GetBuyOrFaucetUrl } from '../utils/buy-asset-url' -import { GetNetworkInfo } from '../utils/network-utils' +import { getNetworkInfo } from '../utils/network-utils' import { findENSAddress, findUnstoppableDomainAddress, @@ -146,8 +146,8 @@ function Container (props: Props) { buyAssetOptions, panelUserAssetList } = useAssets( - accounts, selectedAccount, + networkList, selectedNetwork, props.wallet.fullTokenList, userVisibleTokensInfo, @@ -188,6 +188,7 @@ function Container (props: Props) { } = useSwap( selectedAccount, selectedNetwork, + networkList, swapAssetOptions, props.walletPanelActions.fetchPanelSwapQuote, getERC20Allowance, @@ -232,14 +233,14 @@ function Container (props: Props) { } }, [hasIncorrectPassword]) - const getSelectedAccountBalance = useBalance(selectedNetwork) + const getSelectedAccountBalance = useBalance(networkList) const sendAssetBalance = getSelectedAccountBalance(selectedAccount, selectedSendAsset) const fromAssetBalance = getSelectedAccountBalance(selectedAccount, fromAsset) const toAssetBalance = getSelectedAccountBalance(selectedAccount, toAsset) const onSelectPresetAmountFactory = usePreset( selectedAccount, - selectedNetwork, + networkList, onSetFromAmount, onSetSendAmount, fromAsset, @@ -652,7 +653,7 @@ function Container (props: Props) { transactionQueueNumber={pendingTransactions.findIndex(tx => tx.id === selectedPendingTransaction.id) + 1} transactionsQueueLength={pendingTransactions.length} accounts={accounts} - selectedNetwork={GetNetworkInfo(selectedNetwork.chainId, networkList)} + selectedNetwork={getNetworkInfo(selectedNetwork.chainId, networkList)} transactionInfo={selectedPendingTransaction} transactionSpotPrices={transactionSpotPrices} visibleTokens={userVisibleTokensInfo} @@ -712,7 +713,7 @@ function Container (props: Props) { onApproveChangeNetwork={onApproveChangeNetwork} onCancel={onCancelChangeNetwork} onLearnMore={onNetworkLearnMore} - networkPayload={GetNetworkInfo(switchChainRequest.chainId, networkList)} + networkPayload={getNetworkInfo(switchChainRequest.chainId, networkList)} panelType='change' /> @@ -729,7 +730,7 @@ function Container (props: Props) { accounts={accounts} onCancel={onCancelSigning} onSign={onSignData} - selectedNetwork={GetNetworkInfo(selectedNetwork.chainId, networkList)} + selectedNetwork={getNetworkInfo(selectedNetwork.chainId, networkList)} // Pass a boolean here if the signing method is risky showWarning={false} /> @@ -924,7 +925,7 @@ function Container (props: Props) { onSubmit={onSubmitBuy} selectedAsset={selectedWyreAsset} buyAmount={buyAmount} - selectedNetwork={GetNetworkInfo(selectedNetwork.chainId, networkList)} + selectedNetwork={getNetworkInfo(selectedNetwork.chainId, networkList)} networkList={networkList} /> @@ -1039,10 +1040,10 @@ function Container (props: Props) { @@ -1083,7 +1084,7 @@ function Container (props: Props) { defaultCurrencies={defaultCurrencies} spotPrices={transactionSpotPrices} selectedAccount={selectedAccount} - selectedNetwork={GetNetworkInfo(selectedNetwork.chainId, networkList)} + selectedNetwork={getNetworkInfo(selectedNetwork.chainId, networkList)} isConnected={isConnectedToSite} navAction={navigateTo} onLockWallet={onLockWallet} diff --git a/components/brave_wallet_ui/stories/screens/buy-send-swap.tsx b/components/brave_wallet_ui/stories/screens/buy-send-swap.tsx index 835741850918..02362626291c 100644 --- a/components/brave_wallet_ui/stories/screens/buy-send-swap.tsx +++ b/components/brave_wallet_ui/stories/screens/buy-send-swap.tsx @@ -10,7 +10,8 @@ import { BuySupportedChains, SwapValidationErrorType, DefaultCurrencies, - AmountValidationErrorType + AmountValidationErrorType, + WalletAccountType } from '../../constants/types' import Swap from '../../components/buy-send-swap/tabs/swap-tab' import Send from '../../components/buy-send-swap/tabs/send-tab' @@ -20,7 +21,7 @@ import { } from '../../components/buy-send-swap' export interface Props { - accounts: UserAccountType[] + accounts: WalletAccountType[] networkList: BraveWallet.NetworkInfo[] orderType: OrderTypes selectedSendAsset: BraveWallet.BlockchainToken | undefined diff --git a/components/brave_wallet_ui/stories/screens/crypto-story-view.tsx b/components/brave_wallet_ui/stories/screens/crypto-story-view.tsx index bcb0ca935c4f..5d2889f3bc61 100644 --- a/components/brave_wallet_ui/stories/screens/crypto-story-view.tsx +++ b/components/brave_wallet_ui/stories/screens/crypto-story-view.tsx @@ -63,7 +63,6 @@ export interface Props { onDoneViewingPrivateKey: () => void onViewPrivateKey: (address: string, isDefault: boolean, coin: BraveWallet.CoinType) => void onRemoveAccount: (address: string, hardware: boolean, coin: BraveWallet.CoinType) => void - onSelectNetwork: (network: BraveWallet.NetworkInfo) => void onToggleAddModal: () => void onUpdateAccountName: (payload: UpdateAccountNamePayloadType) => { success: boolean } getBalance: (address: string) => Promise @@ -121,7 +120,6 @@ const CryptoStoryView = (props: Props) => { onImportAccount, onImportFilecoinAccount, onUpdateAccountName, - onSelectNetwork, onToggleAddModal, onRemoveAccount, onViewPrivateKey, @@ -293,7 +291,6 @@ const CryptoStoryView = (props: Props) => { onSelectAsset={onSelectAsset} onSelectAccount={onSelectAccount} onClickAddAccount={onClickAddAccount} - onSelectNetwork={onSelectNetwork} addUserAssetError={false} selectedAsset={selectedAsset} portfolioBalance={portfolioBalance} @@ -341,6 +338,7 @@ const CryptoStoryView = (props: Props) => { onRetryTransaction={onClickRetryTransaction} onSpeedupTransaction={onClickSpeedupTransaction} onCancelTransaction={onClickCancelTransaction} + networkList={networkList} /> } {showAddModal && diff --git a/components/brave_wallet_ui/stories/wallet-concept.tsx b/components/brave_wallet_ui/stories/wallet-concept.tsx index d2cfa540c6dd..dc03d4e07287 100644 --- a/components/brave_wallet_ui/stories/wallet-concept.tsx +++ b/components/brave_wallet_ui/stories/wallet-concept.tsx @@ -405,7 +405,9 @@ export const _DesktopWalletConcept = (args: { onboarding: boolean, locked: boole id: id, name: name, address: wallet.address, - balance: singleAccountBalance(wallet), + nativeBalanceRegistry: { + '0x1': singleAccountBalance(wallet) + }, asset: selectedAsset ? selectedAsset.symbol : '', accountType: 'Primary', tokenBalanceRegistry: {}, @@ -786,7 +788,6 @@ export const _DesktopWalletConcept = (args: { onboarding: boolean, locked: boole onToggleAddModal={onToggleAddModal} onUpdateAccountName={onUpdateAccountName} selectedNetwork={selectedNetwork} - onSelectNetwork={onSelectNetwork} isFetchingPortfolioPriceHistory={false} selectedPortfolioTimeline={selectedTimeline} onRemoveAccount={onRemoveAccount} @@ -831,7 +832,7 @@ export const _DesktopWalletConcept = (args: { onboarding: boolean, locked: boole orderExpiration={orderExpiration} slippageTolerance={slippageTolerance} swapFromAsset={fromAsset} - accounts={mockUserAccounts} + accounts={accounts} selectedNetwork={selectedNetwork} selectedAccount={selectedAccount} selectedTab={selectedWidgetTab} diff --git a/components/brave_wallet_ui/stories/wallet-extension-panels.tsx b/components/brave_wallet_ui/stories/wallet-extension-panels.tsx index 027f55f7a808..85638b6272e9 100644 --- a/components/brave_wallet_ui/stories/wallet-extension-panels.tsx +++ b/components/brave_wallet_ui/stories/wallet-extension-panels.tsx @@ -67,7 +67,9 @@ const accounts: WalletAccountType[] = [ id: '1', name: 'Account 1', address: '0x7d66c9ddAED3115d93Bd1790332f3Cd06Cf52B14', - balance: '311780000000000000', + nativeBalanceRegistry: { + '0x1': '311780000000000000' + }, accountType: 'Primary', tokenBalanceRegistry: {}, coin: BraveWallet.CoinType.ETH @@ -76,7 +78,9 @@ const accounts: WalletAccountType[] = [ id: '2', name: 'Account 2', address: '0x73A29A1da97149722eB09c526E4eAd698895bDCf', - balance: '311780000000000000', + nativeBalanceRegistry: { + '0x1': '311780000000000000' + }, accountType: 'Primary', tokenBalanceRegistry: {}, coin: BraveWallet.CoinType.ETH @@ -85,7 +89,9 @@ const accounts: WalletAccountType[] = [ id: '3', name: 'Account 3', address: '0x3f29A1da97149722eB09c526E4eAd698895b426', - balance: '311780000000000000', + nativeBalanceRegistry: { + '0x1': '311780000000000000' + }, accountType: 'Primary', tokenBalanceRegistry: {}, coin: BraveWallet.CoinType.ETH @@ -398,7 +404,9 @@ export const _ConnectedPanel = (args: { locked: boolean }) => { id: '1', name: 'Account 1', address: '1', - balance: '0.31178', + nativeBalanceRegistry: { + '0x1': '311780000000000000' + }, accountType: 'Primary', tokenBalanceRegistry: {}, coin: BraveWallet.CoinType.ETH @@ -528,7 +536,7 @@ export const _ConnectedPanel = (args: { locked: boolean }) => { } const onSelectPresetAmount = (percent: number) => { - const amount = Number(selectedAccount.balance) * percent + const amount = Number(selectedAccount.nativeBalanceRegistry[0]) * percent setFromAmount(amount.toString()) } @@ -697,7 +705,7 @@ export const _ConnectedPanel = (args: { locked: boolean }) => { addressWarning='' selectedAsset={selectedAsset} selectedAssetAmount={fromAmount} - selectedAssetBalance={selectedAccount.balance.toString()} + selectedAssetBalance={(selectedAccount.nativeBalanceRegistry)[selectedAsset.chainId]} toAddressOrUrl={toAddress} toAddress={toAddress} amountValidationError={undefined} @@ -744,11 +752,11 @@ export const _ConnectedPanel = (args: { locked: boolean }) => { {selectedPanel === 'assets' && } diff --git a/components/brave_wallet_ui/utils/api-utils.test.ts b/components/brave_wallet_ui/utils/api-utils.test.ts index 4542b375a9f5..765a388f86f9 100644 --- a/components/brave_wallet_ui/utils/api-utils.test.ts +++ b/components/brave_wallet_ui/utils/api-utils.test.ts @@ -1,42 +1,47 @@ -import { GetTokenParam, GetFlattenedAccountBalances } from './api-utils' -import { mockNetworks } from '../stories/mock-data/mock-networks' +import { getTokenParam, getFlattenedAccountBalances } from './api-utils' import { AccountAssetOptions } from '../options/asset-options' import { mockAccount } from '../common/constants/mocks' +const ethToken = AccountAssetOptions[0] +const batToken = AccountAssetOptions[1] + describe('Check token param', () => { test('Value should return contract address', () => { - expect(GetTokenParam(mockNetworks[0], AccountAssetOptions[1])).toEqual('0x0D8775F648430679A709E98d2b0Cb6250d2887EF') + expect(getTokenParam(batToken)).toEqual('0x0D8775F648430679A709E98d2b0Cb6250d2887EF') }) test('Value should return symbol', () => { - expect(GetTokenParam(mockNetworks[1], AccountAssetOptions[1])).toEqual('bat') + expect(getTokenParam({ + ...batToken, + contractAddress: '' + })).toEqual('bat') }) test('Value should return coingeckoId', () => { - expect(GetTokenParam(mockNetworks[1], { - ...AccountAssetOptions[1], + expect(getTokenParam({ + ...batToken, coingeckoId: 'mockCoingeckoId' })).toEqual('mockCoingeckoId') }) }) const mockUserVisibleTokenOptions = [ - AccountAssetOptions[0], - AccountAssetOptions[1] + ethToken, + batToken ] const mockAccounts = [ { ...mockAccount, tokenBalanceRegistry: { - [AccountAssetOptions[0].contractAddress.toLowerCase()]: '238699740940532526', - [AccountAssetOptions[1].contractAddress.toLowerCase()]: '0' + [ethToken.contractAddress.toLowerCase()]: '238699740940532526', + [batToken.contractAddress.toLowerCase()]: '0' } }, { ...mockAccount, tokenBalanceRegistry: { - [AccountAssetOptions[0].contractAddress.toLowerCase()]: '80573000000000000', - [AccountAssetOptions[1].contractAddress.toLowerCase()]: '0' + [ethToken.contractAddress.toLowerCase()]: '80573000000000000', + [batToken.contractAddress.toLowerCase()]: '0' } } ] @@ -44,19 +49,19 @@ const mockAccounts = [ const expectedResult = [ { balance: 319272740940532500, - token: AccountAssetOptions[0] + token: ethToken }, { balance: 0, - token: AccountAssetOptions[1] + token: batToken } ] describe('Check Flattened Account Balances', () => { test('Value should return an Empty Array', () => { - expect(GetFlattenedAccountBalances([], mockUserVisibleTokenOptions)).toEqual([]) + expect(getFlattenedAccountBalances([], mockUserVisibleTokenOptions)).toEqual([]) }) test('Value should return a Flattened Account Balance List', () => { - expect(GetFlattenedAccountBalances(mockAccounts, mockUserVisibleTokenOptions)).toEqual(expectedResult) + expect(getFlattenedAccountBalances(mockAccounts, mockUserVisibleTokenOptions)).toEqual(expectedResult) }) }) diff --git a/components/brave_wallet_ui/utils/api-utils.ts b/components/brave_wallet_ui/utils/api-utils.ts index 562ab2dfb979..bc22fded9870 100644 --- a/components/brave_wallet_ui/utils/api-utils.ts +++ b/components/brave_wallet_ui/utils/api-utils.ts @@ -1,23 +1,25 @@ import { BraveWallet, WalletAccountType, GetFlattenedAccountBalancesReturnInfo } from '../constants/types' -export const GetTokenParam = (selectedNetwork: BraveWallet.NetworkInfo, token: BraveWallet.BlockchainToken): string => { +export const getTokenParam = (token: BraveWallet.BlockchainToken): string => { if (token.coingeckoId) { return token.coingeckoId } - const isEthereumNetwork = selectedNetwork.chainId === BraveWallet.MAINNET_CHAIN_ID + const isEthereumNetwork = token.chainId === BraveWallet.MAINNET_CHAIN_ID if (!isEthereumNetwork) { return token.symbol.toLowerCase() } - return token.symbol.toLowerCase() === selectedNetwork.symbol.toLowerCase() - ? token.symbol.toLowerCase() - : token.contractAddress + if (token.contractAddress === '') { + return token.symbol.toLowerCase() + } + + return token.contractAddress } // This will get the sum balance for each token between all accounts -export const GetFlattenedAccountBalances = (accounts: WalletAccountType[], userVisibleTokensInfo: BraveWallet.BlockchainToken[]): GetFlattenedAccountBalancesReturnInfo[] => { +export const getFlattenedAccountBalances = (accounts: WalletAccountType[], userVisibleTokensInfo: BraveWallet.BlockchainToken[]): GetFlattenedAccountBalancesReturnInfo[] => { if (accounts.length === 0) { return [] } @@ -29,7 +31,7 @@ export const GetFlattenedAccountBalances = (accounts: WalletAccountType[], userV .map(account => { const balance = token.contractAddress ? account.tokenBalanceRegistry[token.contractAddress.toLowerCase()] - : account.balance + : account.nativeBalanceRegistry[token.chainId] return balance || '0' }) diff --git a/components/brave_wallet_ui/utils/network-utils.test.ts b/components/brave_wallet_ui/utils/network-utils.test.ts index dff7e6651e6f..95a12c7c9270 100644 --- a/components/brave_wallet_ui/utils/network-utils.test.ts +++ b/components/brave_wallet_ui/utils/network-utils.test.ts @@ -1,15 +1,28 @@ -import { GetNetworkInfo, emptyNetwork, reduceNetworkDisplayName } from './network-utils' +import { + getNetworkInfo, + emptyNetwork, + reduceNetworkDisplayName, + getNetworksByCoinType, + getTokensNetwork, + getTokensCoinType +} from './network-utils' import { mockNetworks } from '../stories/mock-data/mock-networks' +import { NewAssetOptions } from '../options/asset-options' +import { BraveWallet } from '../constants/types' + +const ethToken = NewAssetOptions[0] +const bnbToken = NewAssetOptions[2] +const ethMainNetwork = mockNetworks[0] describe('getNetworkInfo', () => { it('should return network info', () => { - const chainId = mockNetworks[0].chainId - expect(GetNetworkInfo(chainId, mockNetworks)).toEqual(mockNetworks[0]) + const chainId = ethMainNetwork.chainId + expect(getNetworkInfo(chainId, mockNetworks)).toEqual(ethMainNetwork) }) it('should return network object with default values if network with chainId is not found', () => { const chainId = 'fakeChainId' - expect(GetNetworkInfo(chainId, mockNetworks)).toEqual(emptyNetwork) + expect(getNetworkInfo(chainId, mockNetworks)).toEqual(emptyNetwork) }) }) @@ -29,3 +42,33 @@ describe('reduceNetworkDisplayName', () => { expect(reduceNetworkDisplayName(networkName)).toBe(expected) }) }) + +describe('getNetworksByCoinType', () => { + it('CoinType ETH, should return all ETH networks', () => { + expect(getNetworksByCoinType(mockNetworks, BraveWallet.CoinType.ETH)).toEqual(mockNetworks) + }) + it('CoinType random number, should return an empty array', () => { + expect(getNetworksByCoinType(mockNetworks, 3000)).toEqual([]) + }) +}) + +describe('getTokensNetwork', () => { + it('Ethereum with chainId 0x1, should return ETH Mainnet info', () => { + expect(getTokensNetwork(mockNetworks, ethToken)).toEqual(ethMainNetwork) + }) + it('Binance Coin with chainId 0x3, should return ETH Ropsten Testnetwork info', () => { + expect(getTokensNetwork(mockNetworks, bnbToken)).toEqual(mockNetworks[1]) + }) +}) + +describe('getTokensCoinType', () => { + it('Ethereum with chainId 0x1, should return CoinType ETH', () => { + expect(getTokensCoinType(mockNetworks, ethToken)).toEqual(BraveWallet.CoinType.ETH) + }) + it('Binance Coin with chainId 0x3, should return ETH CoinType ETH', () => { + expect(getTokensCoinType(mockNetworks, bnbToken)).toEqual(BraveWallet.CoinType.ETH) + }) + it('Binance Coin with chainId 0x3333333458, should default to CoinType ETH', () => { + expect(getTokensCoinType(mockNetworks, { ...bnbToken, chainId: '0x3333333458' })).toEqual(BraveWallet.CoinType.ETH) + }) +}) diff --git a/components/brave_wallet_ui/utils/network-utils.ts b/components/brave_wallet_ui/utils/network-utils.ts index d49876597b77..9c9ec2f8220e 100644 --- a/components/brave_wallet_ui/utils/network-utils.ts +++ b/components/brave_wallet_ui/utils/network-utils.ts @@ -10,13 +10,13 @@ export const emptyNetwork = { decimals: 0, coin: BraveWallet.CoinType.ETH, data: { - ethData: { + ethData: { isEip1559: true - } + } } } -export const GetNetworkInfo = (chainId: string, list: BraveWallet.NetworkInfo[]) => { +export const getNetworkInfo = (chainId: string, list: BraveWallet.NetworkInfo[]) => { for (let it of list) { if (it.chainId === chainId) { return it @@ -39,3 +39,30 @@ export const reduceNetworkDisplayName = (name: string) => { } } } + +export const getNetworksByCoinType = (networks: BraveWallet.NetworkInfo[], coin: BraveWallet.CoinType): BraveWallet.NetworkInfo[] => { + if (!networks) { + return [] + } + return networks.filter((network) => network.coin === coin) +} + +export const getTokensNetwork = (networks: BraveWallet.NetworkInfo[], token: BraveWallet.BlockchainToken): BraveWallet.NetworkInfo => { + if (!networks) { + return emptyNetwork + } + + const network = networks.filter((n) => n.chainId === token.chainId) + if (network.length > 1) { + return network?.find((n) => n.symbol.toLowerCase() === token.symbol.toLowerCase()) ?? emptyNetwork + } + + return network[0] ?? emptyNetwork +} + +export const getTokensCoinType = (networks: BraveWallet.NetworkInfo[], token: BraveWallet.BlockchainToken) => { + if (!networks) { + return '' + } + return getTokensNetwork(networks, token).coin || '' +}