diff --git a/browser/brave_wallet/asset_discovery_manager_unittest.cc b/browser/brave_wallet/asset_discovery_manager_unittest.cc index 7499d8f046d8..0180869e162e 100644 --- a/browser/brave_wallet/asset_discovery_manager_unittest.cc +++ b/browser/brave_wallet/asset_discovery_manager_unittest.cc @@ -204,7 +204,7 @@ TEST_F(AssetDiscoveryManagerUnitTest, GetFungibleSupportedChains) { TEST_F(AssetDiscoveryManagerUnitTest, GetNonFungibleSupportedChains) { // Gnosis chain ID should not be included if it's not a custom network auto chains = asset_discovery_manager_->GetNonFungibleSupportedChains(); - EXPECT_EQ(chains.size(), 7UL); + EXPECT_EQ(chains.size(), 8UL); EXPECT_TRUE(base::Contains( chains, mojom::ChainId::New(mojom::CoinType::ETH, mojom::kMainnetChainId))); @@ -222,7 +222,7 @@ TEST_F(AssetDiscoveryManagerUnitTest, GetNonFungibleSupportedChains) { network_manager_->AddCustomNetwork(gnosis_network); chains = asset_discovery_manager_->GetNonFungibleSupportedChains(); - EXPECT_EQ(chains.size(), 8UL); + EXPECT_EQ(chains.size(), 9UL); // Verify one of the chain IDs is mojom::kGnosisChainId EXPECT_TRUE(base::Contains( diff --git a/browser/brave_wallet/asset_discovery_task_unittest.cc b/browser/brave_wallet/asset_discovery_task_unittest.cc index 5c851b22d76e..bd218d8ebc48 100644 --- a/browser/brave_wallet/asset_discovery_task_unittest.cc +++ b/browser/brave_wallet/asset_discovery_task_unittest.cc @@ -556,17 +556,17 @@ TEST_F(AssetDiscoveryTaskUnitTest, DiscoverERC20sFromRegistry) { auto* blockchain_registry = BlockchainRegistry::GetInstance(); TokenListMap token_list_map; std::string token_list_json = R"({ - "0x6B175474E89094C44Da98b954EedeAC495271d0F": { - "name": "Dai Stablecoin", - "logo": "dai.svg", - "erc20": true, - "symbol": "DAI", - "chainId": "0x1", - "decimals": 18 - } - })"; - ASSERT_TRUE( - ParseTokenList(token_list_json, &token_list_map, mojom::CoinType::ETH)); + "0x1": { + "0x6B175474E89094C44Da98b954EedeAC495271d0F": { + "name": "Dai Stablecoin", + "logo": "dai.svg", + "erc20": true, + "symbol": "DAI", + "decimals": 18 + } + } + })"; + ASSERT_TRUE(ParseTokenList(token_list_json, &token_list_map)); blockchain_registry->UpdateTokenList(std::move(token_list_map)); // One account, no balances, yields empty token_contract_addresses @@ -649,17 +649,17 @@ TEST_F(AssetDiscoveryTaskUnitTest, DiscoverERC20sFromRegistry) { // Reset token list with a fresh token not in user assets token_list_json = R"({ - "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2": { - "name": "Wrapped Eth", - "logo": "weth.svg", - "erc20": true, - "symbol": "WETH", - "decimals": 18, - "chainId": "0x1" + "0x1": { + "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2": { + "name": "Wrapped Eth", + "logo": "weth.svg", + "erc20": true, + "symbol": "WETH", + "decimals": 18 + } } })"; - ASSERT_TRUE( - ParseTokenList(token_list_json, &token_list_map, mojom::CoinType::ETH)); + ASSERT_TRUE(ParseTokenList(token_list_json, &token_list_map)); blockchain_registry->UpdateTokenList(std::move(token_list_map)); // Two accounts, each with the same balance, yields just one discovered @@ -684,25 +684,26 @@ TEST_F(AssetDiscoveryTaskUnitTest, DiscoverERC20sFromRegistry) { chain_ids.push_back(mojom::kMainnetChainId); chain_ids.push_back(mojom::kPolygonMainnetChainId); token_list_json = R"({ + "0x1": { "0x1111111111111111111111111111111111111111": { "name": "1111", "logo": "111.svg", "erc20": true, "symbol": "111", - "decimals": 18, - "chainId": "0x1" - }, + "decimals": 18 + } + }, + "0x89": { "0x2222222222222222222222222222222222222222": { "name": "22222222222", "logo": "2222.svg", "erc20": true, "symbol": "2222", - "decimals": 18, - "chainId": "0x89" + "decimals": 18 } - })"; - ASSERT_TRUE( - ParseTokenList(token_list_json, &token_list_map, mojom::CoinType::ETH)); + } + })"; + ASSERT_TRUE(ParseTokenList(token_list_json, &token_list_map)); blockchain_registry->UpdateTokenList(std::move(token_list_map)); requests = { {GetNetwork(mojom::kMainnetChainId, mojom::CoinType::ETH), @@ -725,25 +726,26 @@ TEST_F(AssetDiscoveryTaskUnitTest, DiscoverERC20sFromRegistry) { // Multiple accounts with different balances, yields multiple discovered // contract addresses Reset token list with a fresh token not in user assets token_list_json = R"({ + "0x1": { "0x3333333333333333333333333333333333333333": { "name": "3333", "logo": "333.svg", "erc20": true, "symbol": "333", - "decimals": 18, - "chainId": "0x1" - }, + "decimals": 18 + } + }, + "0x89": { "0x4444444444444444444444444444444444444444": { "name": "44444444444", "logo": "4444.svg", "erc20": true, "symbol": "4444", - "decimals": 18, - "chainId": "0x89" + "decimals": 18 } - })"; - ASSERT_TRUE( - ParseTokenList(token_list_json, &token_list_map, mojom::CoinType::ETH)); + } + })"; + ASSERT_TRUE(ParseTokenList(token_list_json, &token_list_map)); blockchain_registry->UpdateTokenList(std::move(token_list_map)); requests = { {GetNetwork(mojom::kMainnetChainId, mojom::CoinType::ETH), @@ -795,27 +797,26 @@ TEST_F(AssetDiscoveryTaskUnitTest, DiscoverSPLTokensFromRegistry) { auto* blockchain_registry = BlockchainRegistry::GetInstance(); TokenListMap token_list_map; std::string token_list_json = R"({ - "88j24JNwWLmJCjn2tZQ5jJzyaFtnusS2qsKup9NeDnd8": { - "name": "Wrapped SOL", - "logo": "So11111111111111111111111111111111111111112.png", - "erc20": false, - "symbol": "SOL", - "decimals": 9, - "chainId": "0x65", - "coingeckoId": "solana" - }, - "EybFzCH4nBYEr7FD4wLWBvNZbEGgjy4kh584bGQntr1b": { - "name": "USD Coin", - "logo": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v.png", - "erc20": false, - "symbol": "USDC", - "decimals": 6, - "chainId": "0x65", - "coingeckoId": "usd-coin" + "0x65": { + "88j24JNwWLmJCjn2tZQ5jJzyaFtnusS2qsKup9NeDnd8": { + "name": "Wrapped SOL", + "logo": "So11111111111111111111111111111111111111112.png", + "erc20": false, + "symbol": "SOL", + "decimals": 9, + "coingeckoId": "solana" + }, + "EybFzCH4nBYEr7FD4wLWBvNZbEGgjy4kh584bGQntr1b": { + "name": "USD Coin", + "logo": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v.png", + "erc20": false, + "symbol": "USDC", + "decimals": 6, + "coingeckoId": "usd-coin" + } } })"; - ASSERT_TRUE( - ParseTokenList(token_list_json, &token_list_map, mojom::CoinType::SOL)); + ASSERT_TRUE(ParseTokenList(token_list_json, &token_list_map)); blockchain_registry->UpdateTokenList(std::move(token_list_map)); // Empy account address @@ -1030,41 +1031,38 @@ TEST_F(AssetDiscoveryTaskUnitTest, DiscoverSPLTokensFromRegistry) { // 7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs, and // 4zLh7YPr8NfrNP4bzTXaYaE72QQc3A8mptbtqUspRz5g to token list token_list_json = R"({ - "BEARs6toGY6fRGsmz2Se8NDuR2NVPRmJuLPpeF8YxCq2": { - "name": "Tesla Inc.", - "logo": "2inRoG4DuMRRzZxAt913CCdNZCu2eGsDD9kZTrsj2DAZ.png", - "erc20": false, - "symbol": "TSLA", - "decimals": 8, - "chainId": "0x65" - }, - "ADJqxHJRfFBpyxVQ2YS8nBhfW6dumdDYGU21B4AmYLZJ": { - "name": "Apple Inc.", - "logo": "8bpRdBGPt354VfABL5xugP3pmYZ2tQjzRcqjg2kmwfbF.png", - "erc20": false, - "symbol": "AAPL", - "decimals": 8, - "chainId": "0x65" - }, - "7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs": { - "name": "Microsoft Corporation", - "logo": "3vhcrQfEn8ashuBfE82F3MtEDFcBCEFfFw1ZgM3xj1s8.png", - "erc20": false, - "symbol": "MSFT", - "decimals": 8, - "chainId": "0x65" - }, - "4zLh7YPr8NfrNP4bzTXaYaE72QQc3A8mptbtqUspRz5g": { - "name": "MicroStrategy Incorporated.", - "logo": "ASwYCbLedk85mRdPnkzrUXbbYbwe26m71af9rzrhC2Qz.png", - "erc20": false, - "symbol": "MSTR", - "decimals": 8, - "chainId": "0x65" + "0x65": { + "BEARs6toGY6fRGsmz2Se8NDuR2NVPRmJuLPpeF8YxCq2": { + "name": "Tesla Inc.", + "logo": "2inRoG4DuMRRzZxAt913CCdNZCu2eGsDD9kZTrsj2DAZ.png", + "erc20": false, + "symbol": "TSLA", + "decimals": 8 + }, + "ADJqxHJRfFBpyxVQ2YS8nBhfW6dumdDYGU21B4AmYLZJ": { + "name": "Apple Inc.", + "logo": "8bpRdBGPt354VfABL5xugP3pmYZ2tQjzRcqjg2kmwfbF.png", + "erc20": false, + "symbol": "AAPL", + "decimals": 8 + }, + "7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs": { + "name": "Microsoft Corporation", + "logo": "3vhcrQfEn8ashuBfE82F3MtEDFcBCEFfFw1ZgM3xj1s8.png", + "erc20": false, + "symbol": "MSFT", + "decimals": 8 + }, + "4zLh7YPr8NfrNP4bzTXaYaE72QQc3A8mptbtqUspRz5g": { + "name": "MicroStrategy Incorporated.", + "logo": "ASwYCbLedk85mRdPnkzrUXbbYbwe26m71af9rzrhC2Qz.png", + "erc20": false, + "symbol": "MSTR", + "decimals": 8 + } } })"; - ASSERT_TRUE( - ParseTokenList(token_list_json, &token_list_map, mojom::CoinType::SOL)); + ASSERT_TRUE(ParseTokenList(token_list_json, &token_list_map)); blockchain_registry->UpdateTokenList(std::move(token_list_map)); TestDiscoverSolAssets({"4fzcQKyGFuk55uJaBZtvTHh42RBxbrZMuXzsGQvBJbwF", "8RFACUfst117ARQLezvK4cKVR8ZHvW2xUfdUoqWnTuEB"}, diff --git a/browser/brave_wallet/brave_wallet_service_unittest.cc b/browser/brave_wallet/brave_wallet_service_unittest.cc index 277d566d541b..d3de0063b3c1 100644 --- a/browser/brave_wallet/brave_wallet_service_unittest.cc +++ b/browser/brave_wallet/brave_wallet_service_unittest.cc @@ -74,91 +74,80 @@ namespace { const char token_list_json[] = R"( { - "0x6B175474E89094C44Da98b954EedeAC495271d0F": { - "name": "USD Coin", - "logo": "usdc.png", - "erc20": true, - "erc721": false, - "symbol": "USDC", - "decimals": 6, - "chainId": "0x1" - }, - "0x06012c8cf97BEaD5deAe237070F9587f8E7A266d": { - "name": "Crypto Kitties", - "logo": "CryptoKitties-Kitty-13733.svg", - "erc20": false, - "erc721": true, - "symbol": "CK", - "decimals": 0, - "chainId": "0x1" - }, - "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984": { - "name": "Uniswap", - "logo": "uni.svg", - "erc20": true, - "symbol": "UNI", - "decimals": 18, - "chainId": "0x1" - } - })"; - -const char sepolia_list_json[] = R"( - { - "0x6B175474E89094C44Da98b954EedeAC495271d0F": { - "name": "USD Coin", - "logo": "usdc.png", - "erc20": true, - "erc721": false, - "symbol": "USDC", - "decimals": 6, - "chainId": "0xaa36a7" - }, - "0x06012c8cf97BEaD5deAe237070F9587f8E7A266d": { - "name": "Crypto Kitties", - "logo": "CryptoKitties-Kitty-13733.svg", - "erc20": false, - "erc721": true, - "symbol": "CK", - "decimals": 0, - "chainId": "0xaa36a7" - }, - "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984": { - "name": "Uniswap", - "logo": "uni.svg", - "erc20": true, - "symbol": "UNI", - "decimals": 18, - "chainId": "0xaa36a7" - } - })"; - -const char solana_token_list_json[] = R"( - { - "So11111111111111111111111111111111111111112": { - "name": "Wrapped SOL", - "logo": "So11111111111111111111111111111111111111112.png", - "erc20": false, - "symbol": "SOL", - "decimals": 9, - "chainId": "0x65", - "coingeckoId": "solana" + "0x1": { + "0x6B175474E89094C44Da98b954EedeAC495271d0F": { + "name": "USD Coin", + "logo": "usdc.png", + "erc20": true, + "erc721": false, + "symbol": "USDC", + "decimals": 6 + }, + "0x06012c8cf97BEaD5deAe237070F9587f8E7A266d": { + "name": "Crypto Kitties", + "logo": "CryptoKitties-Kitty-13733.svg", + "erc20": false, + "erc721": true, + "symbol": "CK", + "decimals": 0 + }, + "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984": { + "name": "Uniswap", + "logo": "uni.svg", + "erc20": true, + "symbol": "UNI", + "decimals": 18 + } }, - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v": { - "name": "USD Coin", - "logo": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v.png", - "erc20": false, - "symbol": "USDC", - "decimals": 6, - "chainId": "0x65", - "coingeckoId": "usd-coin" + "0xaa36a7": { + "0x6B175474E89094C44Da98b954EedeAC495271d0F": { + "name": "USD Coin", + "logo": "usdc.png", + "erc20": true, + "erc721": false, + "symbol": "USDC", + "decimals": 6 + }, + "0x06012c8cf97BEaD5deAe237070F9587f8E7A266d": { + "name": "Crypto Kitties", + "logo": "CryptoKitties-Kitty-13733.svg", + "erc20": false, + "erc721": true, + "symbol": "CK", + "decimals": 0 + }, + "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984": { + "name": "Uniswap", + "logo": "uni.svg", + "erc20": true, + "symbol": "UNI", + "decimals": 18 + } }, - "2inRoG4DuMRRzZxAt913CCdNZCu2eGsDD9kZTrsj2DAZ": { - "name": "Tesla Inc.", - "logo": "2inRoG4DuMRRzZxAt913CCdNZCu2eGsDD9kZTrsj2DAZ.png", - "erc20": false, - "symbol": "TSLA", - "decimals": 8, - "chainId": "0x65" + "0x65": { + "So11111111111111111111111111111111111111112": { + "name": "Wrapped SOL", + "logo": "So11111111111111111111111111111111111111112.png", + "erc20": false, + "symbol": "SOL", + "decimals": 9, + "coingeckoId": "solana" + }, + "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v": { + "name": "USD Coin", + "logo": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v.png", + "erc20": false, + "symbol": "USDC", + "decimals": 6, + "coingeckoId": "usd-coin" + }, + "2inRoG4DuMRRzZxAt913CCdNZCu2eGsDD9kZTrsj2DAZ": { + "name": "Tesla Inc.", + "logo": "2inRoG4DuMRRzZxAt913CCdNZCu2eGsDD9kZTrsj2DAZ.png", + "erc20": false, + "symbol": "TSLA", + "decimals": 8 + } } })"; @@ -364,12 +353,7 @@ class BraveWalletServiceUnitTest : public testing::Test { auto* registry = BlockchainRegistry::GetInstance(); TokenListMap token_list_map; - ASSERT_TRUE( - ParseTokenList(token_list_json, &token_list_map, mojom::CoinType::ETH)); - ASSERT_TRUE(ParseTokenList(sepolia_list_json, &token_list_map, - mojom::CoinType::ETH)); - ASSERT_TRUE(ParseTokenList(solana_token_list_json, &token_list_map, - mojom::CoinType::SOL)); + ASSERT_TRUE(ParseTokenList(token_list_json, &token_list_map)); registry->UpdateTokenList(std::move(token_list_map)); token1_ = GetRegistry()->GetTokenByAddress( @@ -2605,13 +2589,15 @@ TEST_F(BraveWalletServiceUnitTest, RecordGeneralUsageMetrics) { TEST_F(BraveWalletServiceUnitTest, GetBalanceScannerSupportedChains) { service_->GetBalanceScannerSupportedChains( base::BindLambdaForTesting([](const std::vector& chains) { - std::vector expected_chains = { - mojom::kMainnetChainId, mojom::kBnbSmartChainMainnetChainId, - mojom::kPolygonMainnetChainId, mojom::kOptimismMainnetChainId, + base::flat_set expected_chains_set = { mojom::kArbitrumMainnetChainId, mojom::kAvalancheMainnetChainId, + mojom::kBaseMainnetChainId, mojom::kBnbSmartChainMainnetChainId, + mojom::kMainnetChainId, mojom::kOptimismMainnetChainId, + mojom::kPolygonMainnetChainId, }; - ASSERT_EQ(chains.size(), expected_chains.size()); - EXPECT_EQ(chains, expected_chains); + ASSERT_EQ(chains.size(), expected_chains_set.size()); + base::flat_set chains_set(chains.begin(), chains.end()); + EXPECT_EQ(chains_set, expected_chains_set); })); } diff --git a/browser/brave_wallet/eth_allowance_manager_unittest.cc b/browser/brave_wallet/eth_allowance_manager_unittest.cc index 54a24eaae0fa..82d99f25faa9 100644 --- a/browser/brave_wallet/eth_allowance_manager_unittest.cc +++ b/browser/brave_wallet/eth_allowance_manager_unittest.cc @@ -81,13 +81,14 @@ constexpr std::string_view kEthAllowanceErrorResponse = R"({ })"; constexpr char kTokenListJson[] = R"({ - "0x3333333333333333333333333333333333333333": { - "name": "3333", - "logo": "333.svg", - "erc20": true, - "symbol": "333", - "decimals": 18, - "chainId": "0x1" + "0x1": { + "0x3333333333333333333333333333333333333333": { + "name": "3333", + "logo": "333.svg", + "erc20": true, + "symbol": "333", + "decimals": 18 + } } })"; @@ -266,8 +267,7 @@ class EthAllowanceManagerUnitTest : public testing::Test { auto* blockchain_registry = BlockchainRegistry::GetInstance(); TokenListMap token_list_map; - ASSERT_TRUE(ParseTokenList(current_token_list_json, &token_list_map, - mojom::CoinType::ETH)); + ASSERT_TRUE(ParseTokenList(current_token_list_json, &token_list_map)); std::vector contract_addresses; for (auto const& [contract_addr, token_info] : token_list_map) { diff --git a/components/brave_wallet/browser/android_page_appearing_browsertest.cc b/components/brave_wallet/browser/android_page_appearing_browsertest.cc index 4b04cca35747..8e145650fff8 100644 --- a/components/brave_wallet/browser/android_page_appearing_browsertest.cc +++ b/components/brave_wallet/browser/android_page_appearing_browsertest.cc @@ -94,15 +94,19 @@ class ConsoleObserver : public WebContentsObserver { namespace brave_wallet { namespace { -constexpr char kTokenList[] = R"({ - "": { - "name": "Ethereum", - "symbol": "ETH", - "logo": "333.svg", - "erc20": true, - "decimals": 18, - "chainId": "0x1" - }, +constexpr char kTokenList[] = R"( + { + "0x1": { + "0xdac17f958d2ee523a2206206994597c13d831ec7": { + "name": "Tether", + "symbol": "usdt", + "coingeckoId": "tether", + "decimals": 6, + "logo": + "https://coin-images.coingecko.com/coins/images/325/large/Tether.png" + } + }, + "0x89": { "0x4444444444444444444444444444444444444444": { "name": "44444444444", "logo": "4444.svg", @@ -111,7 +115,9 @@ constexpr char kTokenList[] = R"({ "decimals": 18, "chainId": "0x89" } - })"; + } + } +)"; constexpr char kGetBalanceResp[] = R"({ "jsonrpc": "2.0", @@ -240,8 +246,7 @@ class AndroidPageAppearingBrowserTest : public PlatformBrowserTest { kPasswordBrave, false)); TokenListMap token_list_map; - ASSERT_TRUE( - ParseTokenList(kTokenList, &token_list_map, mojom::CoinType::ETH)); + ASSERT_TRUE(ParseTokenList(kTokenList, &token_list_map)); BlockchainRegistry::GetInstance()->UpdateTokenList( std::move(token_list_map)); diff --git a/components/brave_wallet/browser/blockchain_list_parser.cc b/components/brave_wallet/browser/blockchain_list_parser.cc index f4d2d77ce5b6..b0d54693bb7f 100644 --- a/components/brave_wallet/browser/blockchain_list_parser.cc +++ b/components/brave_wallet/browser/blockchain_list_parser.cc @@ -16,6 +16,7 @@ #include "base/strings/stringprintf.h" #include "brave/components/brave_wallet/browser/blockchain_list_schemas.h" #include "brave/components/brave_wallet/browser/brave_wallet_utils.h" +#include "brave/components/brave_wallet/common/brave_wallet.mojom.h" #include "brave/components/brave_wallet/common/common_utils.h" #include "brave/components/brave_wallet/common/solana_utils.h" #include "brave/components/brave_wallet/common/value_conversion_utils.h" @@ -35,6 +36,11 @@ bool ParseResultFromDict(const base::Value::Dict* response_dict, return true; } +bool ParseOptionalBoolFromDict(const base::Value::Dict* response_dict, + const std::string& key) { + return response_dict->FindBool(key).value_or(false); +} + std::optional ParseNullableStringAsDouble(const base::Value& value) { if (value.is_none()) { return std::nullopt; @@ -205,35 +211,36 @@ void AddTokenToMaps(const blockchain_lists::Token& token, } // namespace -bool ParseTokenList(const std::string& json, - TokenListMap* token_list_map, - mojom::CoinType coin) { +bool ParseTokenList(const std::string& json, TokenListMap* token_list_map) { DCHECK(token_list_map); // { - // "0x0D8775F648430679A709E98d2b0Cb6250d2887EF": { - // "name": "Basic Attention Token", - // "logo": "bat.svg", - // "erc20": true, - // "symbol": "BAT", - // "decimals": 18 - // }, - // "0x06012c8cf97BEaD5deAe237070F9587f8E7A266d": { - // "name": "Crypto Kitties", - // "logo": "CryptoKitties-Kitty-13733.svg", - // "erc20": false, - // "erc721": true, - // "symbol": "CK", - // "decimals": 0 - // }, - // "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984": { - // "name": "Uniswap", - // "logo": "uni.svg", - // "erc20": true, - // "symbol": "UNI", - // "decimals": 18, - // "chainId": "0x1" - // } + // "0x1": { + // "0xdac17f958d2ee523a2206206994597c13d831ec7": { + // "name": "Tether", + // "symbol": "usdt", + // "coingeckoId": "tether", + // "decimals": 6, + // "logo": + // "https://coin-images.coingecko.com/coins/images/325/large/Tether.png" + // }, + // "0xb8c77482e45f1f44de1745f52c74426c631bdd52": { + // "name": "BNB", + // "symbol": "bnb", + // "coingeckoId": "binancecoin", + // "decimals": 18, + // "logo": + // "https://coin-images.coingecko.com/coins/images/825/large/bnb-icon2_2x.png" + // }, + // "0x06012c8cf97BEaD5deAe237070F9587f8E7A266d": { + // "name": "Crypto Kitties", + // "logo": "CryptoKitties-Kitty-13733.svg", + // "erc20": false, + // "erc721": true, + // "symbol": "CK", + // "decimals": 0 + // } + // } // } std::optional records_v = @@ -244,70 +251,69 @@ bool ParseTokenList(const std::string& json, return false; } - const auto& response_dict = records_v->GetDict(); - for (const auto blockchain_token_value_pair : response_dict) { - auto blockchain_token = mojom::BlockchainToken::New(); - blockchain_token->contract_address = blockchain_token_value_pair.first; - const auto* blockchain_token_value = - blockchain_token_value_pair.second.GetIfDict(); - if (!blockchain_token_value) { - return false; + const auto& chain_dict = records_v->GetDict(); + // Iterate through chain IDs + for (const auto chain_pair : chain_dict) { + const std::string& chain_id = chain_pair.first; + const auto* tokens_dict = chain_pair.second.GetIfDict(); + if (!tokens_dict) { + continue; } - blockchain_token->is_erc20 = - blockchain_token_value->FindBool("erc20").value_or(false); + // Determine coin type based on chain_id + mojom::CoinType coin = chain_id == mojom::kSolanaMainnet + ? mojom::CoinType::SOL + : mojom::CoinType::ETH; + + // Iterate through tokens in this chain + for (const auto token_pair : *tokens_dict) { + auto blockchain_token = mojom::BlockchainToken::New(); + blockchain_token->contract_address = token_pair.first; + const auto* token_info = token_pair.second.GetIfDict(); + if (!token_info) { + continue; + } - blockchain_token->is_erc721 = - blockchain_token_value->FindBool("erc721").value_or(false); + // Parse required fields + if (!ParseResultFromDict(token_info, "symbol", + &blockchain_token->symbol) || + !ParseResultFromDict(token_info, "name", &blockchain_token->name)) { + continue; + } - blockchain_token->is_nft = blockchain_token->is_erc721; + // Parse optional fields + ParseResultFromDict(token_info, "logo", &blockchain_token->logo); + ParseResultFromDict(token_info, "coingeckoId", + &blockchain_token->coingecko_id); + blockchain_token->is_erc721 = + ParseOptionalBoolFromDict(token_info, "erc721"); - if (!ParseResultFromDict(blockchain_token_value, "symbol", - &blockchain_token->symbol)) { - continue; - } - if (!ParseResultFromDict(blockchain_token_value, "name", - &blockchain_token->name)) { - return false; - } - ParseResultFromDict(blockchain_token_value, "logo", - &blockchain_token->logo); + // Determining is_nft from blockchain list is not supported for Solana + blockchain_token->is_nft = blockchain_token->is_erc721; + bool is_token2022 = ParseOptionalBoolFromDict(token_info, "token2022"); - std::optional decimals_opt = - blockchain_token_value->FindInt("decimals"); - if (decimals_opt) { + std::optional decimals_opt = token_info->FindInt("decimals"); + if (!decimals_opt) { + continue; + } blockchain_token->decimals = *decimals_opt; - } else { - continue; - } - - // chain_id is only optional for ETH mainnet token lists. - blockchain_token->chain_id = "0x1"; - if (!ParseResultFromDict(blockchain_token_value, "chainId", - &blockchain_token->chain_id) && - coin != mojom::CoinType::ETH) { - continue; - } - ParseResultFromDict(blockchain_token_value, "coingeckoId", - &blockchain_token->coingecko_id); - - blockchain_token->coin = coin; - blockchain_token->visible = true; - - if (IsSPLToken(blockchain_token)) { - bool is_token2022 = - blockchain_token_value->FindBool("token2022").value_or(false); - blockchain_token->spl_token_program = - is_token2022 ? mojom::SPLTokenProgram::kToken2022 - : mojom::SPLTokenProgram::kToken; - } else { + // Set default values + blockchain_token->chain_id = chain_id; + blockchain_token->coin = coin; + blockchain_token->visible = true; + blockchain_token->is_erc20 = + coin == mojom::CoinType::ETH && !blockchain_token->is_nft; + blockchain_token->is_erc1155 = false; blockchain_token->spl_token_program = - mojom::SPLTokenProgram::kUnsupported; - } + IsSPLToken(blockchain_token) + ? (is_token2022 ? mojom::SPLTokenProgram::kToken2022 + : mojom::SPLTokenProgram::kToken) + : mojom::SPLTokenProgram::kUnsupported; - (*token_list_map)[GetTokenListKey(coin, blockchain_token->chain_id)] - .push_back(std::move(blockchain_token)); + (*token_list_map)[GetTokenListKey(coin, chain_id)].push_back( + std::move(blockchain_token)); + } } return true; diff --git a/components/brave_wallet/browser/blockchain_list_parser.h b/components/brave_wallet/browser/blockchain_list_parser.h index 719a2187db00..0da50c0532de 100644 --- a/components/brave_wallet/browser/blockchain_list_parser.h +++ b/components/brave_wallet/browser/blockchain_list_parser.h @@ -32,9 +32,7 @@ using OffRampTokensListMap = std::vector>; using RampTokenListMaps = std::pair; -bool ParseTokenList(const std::string& json, - TokenListMap* token_list, - mojom::CoinType coin); +bool ParseTokenList(const std::string& json, TokenListMap* token_list_map); std::optional ParseRampTokenListMaps( const std::string& json); std::optional> ParseOnRampCurrencyLists( diff --git a/components/brave_wallet/browser/blockchain_list_parser_unittest.cc b/components/brave_wallet/browser/blockchain_list_parser_unittest.cc index a71ef3cf7c4c..a8d6bf2f325b 100644 --- a/components/brave_wallet/browser/blockchain_list_parser_unittest.cc +++ b/components/brave_wallet/browser/blockchain_list_parser_unittest.cc @@ -20,65 +20,94 @@ namespace brave_wallet { TEST(BlockchainListParseUnitTest, ParseTokenList) { std::string json(R"( { - "0x06012c8cf97BEaD5deAe237070F9587f8E7A266d": { - "name": "Crypto Kitties", - "logo": "CryptoKitties-Kitty-13733.svg", - "erc20": false, - "erc721": true, - "symbol": "CK", - "decimals": 0 - }, - "0x0D8775F648430679A709E98d2b0Cb6250d2887EF": { - "name": "Basic Attention Token", - "logo": "bat.svg", - "erc20": true, - "symbol": "BAT", - "decimals": 18, - "coingeckoId": "basic-attention-token" - }, - "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984": { - "name": "Uniswap", - "logo": "uni.svg", - "erc20": true, - "symbol": "UNI", - "decimals": 18, - "chainId": "0xaa36a7" - } + "0x1": { + "0x06012c8cf97BEaD5deAe237070F9587f8E7A266d": { + "name": "Crypto Kitties", + "logo": "CryptoKitties-Kitty-13733.svg", + "erc20": false, + "erc721": true, + "symbol": "CK", + "decimals": 0 + }, + "0x0D8775F648430679A709E98d2b0Cb6250d2887EF": { + "name": "Basic Attention Token", + "logo": "bat.svg", + "erc20": true, + "symbol": "BAT", + "decimals": 18, + "coingeckoId": "basic-attention-token" + } + }, + "0xaa36a7": { + "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984": { + "name": "Uniswap", + "logo": "uni.svg", + "erc20": true, + "symbol": "UNI", + "decimals": 18 + } + }, + "0x65": { + "So11111111111111111111111111111111111111112": { + "name": "Wrapped SOL", + "logo": "So11111111111111111111111111111111111111112.png", + "erc20": false, + "symbol": "SOL", + "decimals": 9, + "coingeckoId": "solana" + }, + "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v": { + "name": "USD Coin", + "logo": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v.png", + "erc20": false, + "symbol": "USDC", + "decimals": 6, + "coingeckoId": "usd-coin" + }, + "2inRoG4DuMRRzZxAt913CCdNZCu2eGsDD9kZTrsj2DAZ": { + "name": "Tesla Inc.", + "logo": "2inRoG4DuMRRzZxAt913CCdNZCu2eGsDD9kZTrsj2DAZ.png", + "erc20": false, + "token2022": true, + "symbol": "TSLA", + "decimals": 8 + } + } } )"); TokenListMap token_list_map; - ASSERT_TRUE(ParseTokenList(json, &token_list_map, mojom::CoinType::ETH)); + ASSERT_TRUE(ParseTokenList(json, &token_list_map)); ASSERT_EQ(token_list_map["ethereum.0x1"].size(), 2UL); EXPECT_EQ(token_list_map["ethereum.0x2"].size(), 0UL); ASSERT_EQ(token_list_map["ethereum.0xaa36a7"].size(), 1UL); - const auto& mainnet_token_list = token_list_map["ethereum.0x1"]; - EXPECT_EQ(mainnet_token_list[0]->name, "Crypto Kitties"); - EXPECT_EQ(mainnet_token_list[0]->contract_address, + const auto& ethereum_token_list = token_list_map["ethereum.0x1"]; + EXPECT_EQ(ethereum_token_list[0]->name, "Crypto Kitties"); + EXPECT_EQ(ethereum_token_list[0]->contract_address, "0x06012c8cf97BEaD5deAe237070F9587f8E7A266d"); - EXPECT_FALSE(mainnet_token_list[0]->is_erc20); - EXPECT_TRUE(mainnet_token_list[0]->is_erc721); - EXPECT_TRUE(mainnet_token_list[0]->is_nft); - EXPECT_EQ(mainnet_token_list[0]->symbol, "CK"); - EXPECT_EQ(mainnet_token_list[0]->logo, "CryptoKitties-Kitty-13733.svg"); - EXPECT_EQ(mainnet_token_list[0]->decimals, 0); - EXPECT_TRUE(mainnet_token_list[0]->coingecko_id.empty()); - EXPECT_EQ(mainnet_token_list[0]->spl_token_program, + EXPECT_FALSE(ethereum_token_list[0]->is_erc20); + EXPECT_TRUE(ethereum_token_list[0]->is_erc721); + EXPECT_TRUE(ethereum_token_list[0]->is_nft); + EXPECT_EQ(ethereum_token_list[0]->symbol, "CK"); + EXPECT_EQ(ethereum_token_list[0]->logo, "CryptoKitties-Kitty-13733.svg"); + EXPECT_EQ(ethereum_token_list[0]->decimals, 0); + EXPECT_TRUE(ethereum_token_list[0]->coingecko_id.empty()); + EXPECT_EQ(ethereum_token_list[0]->spl_token_program, mojom::SPLTokenProgram::kUnsupported); - EXPECT_EQ(mainnet_token_list[1]->name, "Basic Attention Token"); - EXPECT_EQ(mainnet_token_list[1]->contract_address, + EXPECT_EQ(ethereum_token_list[1]->name, "Basic Attention Token"); + EXPECT_EQ(ethereum_token_list[1]->contract_address, "0x0D8775F648430679A709E98d2b0Cb6250d2887EF"); - EXPECT_TRUE(mainnet_token_list[1]->is_erc20); - EXPECT_FALSE(mainnet_token_list[1]->is_erc721); - EXPECT_FALSE(mainnet_token_list[1]->is_erc1155); - EXPECT_FALSE(mainnet_token_list[1]->is_nft); - EXPECT_EQ(mainnet_token_list[1]->symbol, "BAT"); - EXPECT_EQ(mainnet_token_list[1]->logo, "bat.svg"); - EXPECT_EQ(mainnet_token_list[1]->decimals, 18); - EXPECT_EQ(mainnet_token_list[1]->coingecko_id, "basic-attention-token"); - EXPECT_EQ(mainnet_token_list[1]->spl_token_program, + EXPECT_TRUE(ethereum_token_list[1]->is_erc20); + EXPECT_FALSE(ethereum_token_list[1]->is_erc721); + EXPECT_FALSE(ethereum_token_list[1]->is_erc1155); + EXPECT_FALSE(ethereum_token_list[1]->is_nft); + EXPECT_EQ(ethereum_token_list[1]->symbol, "BAT"); + EXPECT_EQ(ethereum_token_list[1]->logo, "bat.svg"); + EXPECT_EQ(ethereum_token_list[1]->decimals, 18); + EXPECT_EQ(ethereum_token_list[1]->coingecko_id, "basic-attention-token"); + EXPECT_EQ(ethereum_token_list[1]->spl_token_program, mojom::SPLTokenProgram::kUnsupported); const auto& sepolia_token_list = token_list_map["ethereum.0xaa36a7"]; @@ -96,39 +125,7 @@ TEST(BlockchainListParseUnitTest, ParseTokenList) { EXPECT_EQ(sepolia_token_list[0]->spl_token_program, mojom::SPLTokenProgram::kUnsupported); - std::string solana_json(R"( - { - "So11111111111111111111111111111111111111112": { - "name": "Wrapped SOL", - "logo": "So11111111111111111111111111111111111111112.png", - "erc20": false, - "symbol": "SOL", - "decimals": 9, - "chainId": "0x65", - "coingeckoId": "solana" - }, - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v": { - "name": "USD Coin", - "logo": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v.png", - "erc20": false, - "symbol": "USDC", - "decimals": 6, - "chainId": "0x65", - "coingeckoId": "usd-coin" - }, - "2inRoG4DuMRRzZxAt913CCdNZCu2eGsDD9kZTrsj2DAZ": { - "name": "Tesla Inc.", - "logo": "2inRoG4DuMRRzZxAt913CCdNZCu2eGsDD9kZTrsj2DAZ.png", - "erc20": false, - "token2022": true, - "symbol": "TSLA", - "decimals": 8, - "chainId": "0x65" - } - } - )"); - EXPECT_TRUE( - ParseTokenList(solana_json, &token_list_map, mojom::CoinType::SOL)); + ASSERT_EQ(token_list_map["solana.0x65"].size(), 3UL); auto wrapped_sol = mojom::BlockchainToken::New( "So11111111111111111111111111111111111111112", "Wrapped SOL", "So11111111111111111111111111111111111111112.png", false, false, false, @@ -152,19 +149,20 @@ TEST(BlockchainListParseUnitTest, ParseTokenList) { token_list_map.clear(); json = R"({})"; - EXPECT_TRUE(ParseTokenList(json, &token_list_map, mojom::CoinType::ETH)); + EXPECT_TRUE(ParseTokenList(json, &token_list_map)); + EXPECT_TRUE(token_list_map.empty()); + json = R"({"0x1": { "0x0D8775F648430679A709E98d2b0Cb6250d2887EF": 3}})"; + EXPECT_TRUE(ParseTokenList(json, &token_list_map)); EXPECT_TRUE(token_list_map.empty()); - json = R"({"0x0D8775F648430679A709E98d2b0Cb6250d2887EF": 3})"; - EXPECT_FALSE(ParseTokenList(json, &token_list_map, mojom::CoinType::ETH)); - json = R"({"0x0D8775F648430679A709E98d2b0Cb6250d2887EF": {}})"; + json = R"({"0x1": {}})"; + EXPECT_TRUE(ParseTokenList(json, &token_list_map)); EXPECT_TRUE(token_list_map.empty()); - EXPECT_TRUE(ParseTokenList(json, &token_list_map, mojom::CoinType::ETH)); json = "3"; - EXPECT_FALSE(ParseTokenList(json, &token_list_map, mojom::CoinType::ETH)); + EXPECT_FALSE(ParseTokenList(json, &token_list_map)); json = "[3]"; - EXPECT_FALSE(ParseTokenList(json, &token_list_map, mojom::CoinType::ETH)); + EXPECT_FALSE(ParseTokenList(json, &token_list_map)); json = ""; - EXPECT_FALSE(ParseTokenList(json, &token_list_map, mojom::CoinType::ETH)); + EXPECT_FALSE(ParseTokenList(json, &token_list_map)); } TEST(ParseTokenListUnitTest, GetTokenListKey) { diff --git a/components/brave_wallet/browser/blockchain_registry.cc b/components/brave_wallet/browser/blockchain_registry.cc index d54a6b98de44..5ae5f24ab4f7 100644 --- a/components/brave_wallet/browser/blockchain_registry.cc +++ b/components/brave_wallet/browser/blockchain_registry.cc @@ -122,17 +122,14 @@ void DoParseCoingeckoIdsMap(const base::FilePath& dir, ParseListsResult& out) { out.coingecko_ids_map = std::move(*coingecko_ids_map); } -void HandleParseTokenList(const base::FilePath& dir, - const std::string& filename, - mojom::CoinType coin_type, - ParseListsResult& out) { - auto result = ParseJsonFile(dir, filename); +void DoParseTokenList(const base::FilePath& dir, ParseListsResult& out) { + auto result = ParseJsonFile(dir, "coingecko.json"); if (!result) { return; } TokenListMap lists; - if (!ParseTokenList(*result, &lists, coin_type)) { + if (!ParseTokenList(*result, &lists)) { VLOG(1) << "Can't parse token list."; return; } @@ -142,13 +139,6 @@ void HandleParseTokenList(const base::FilePath& dir, } } -void DoParseTokenList(const base::FilePath& dir, ParseListsResult& out) { - HandleParseTokenList(dir, "contract-map.json", mojom::CoinType::ETH, out); - HandleParseTokenList(dir, "evm-contract-map.json", mojom::CoinType::ETH, out); - HandleParseTokenList(dir, "solana-contract-map.json", mojom::CoinType::SOL, - out); -} - void DoParseChainList(const base::FilePath& dir, ParseListsResult& out) { auto result = ParseJsonFile(dir, "chainlist.json"); if (!result) { diff --git a/components/brave_wallet/browser/blockchain_registry_unittest.cc b/components/brave_wallet/browser/blockchain_registry_unittest.cc index 0706c7283b88..9809fb43bdc3 100644 --- a/components/brave_wallet/browser/blockchain_registry_unittest.cc +++ b/components/brave_wallet/browser/blockchain_registry_unittest.cc @@ -31,71 +31,78 @@ namespace { const char token_list_json[] = R"( { - "0x06012c8cf97BEaD5deAe237070F9587f8E7A266d": { - "name": "Crypto Kitties", - "logo": "CryptoKitties-Kitty-13733.svg", - "erc20": false, - "erc721": true, - "symbol": "CK", - "decimals": 0 - }, - "0x0D8775F648430679A709E98d2b0Cb6250d2887EF": { - "name": "Basic Attention Token", - "logo": "bat.svg", - "erc20": true, - "symbol": "BAT", - "decimals": 18 - }, - "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984": { - "name": "Uniswap", - "logo": "uni.svg", - "erc20": true, - "symbol": "UNI", - "decimals": 18, - "chainId": "0xaa36a7" - }, - "0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef": { - "name": "ENS Registrar", - "logo": "ens.svg" - } - })"; - -const char solana_token_list_json[] = R"( - { - "So11111111111111111111111111111111111111112": { - "name": "Wrapped SOL", - "logo": "So11111111111111111111111111111111111111112.png", - "erc20": false, - "symbol": "SOL", - "decimals": 9, - "chainId": "0x65", - "coingeckoId": "solana" + "0x1": { + "0x06012c8cf97BEaD5deAe237070F9587f8E7A266d": { + "name": "Crypto Kitties", + "logo": "CryptoKitties-Kitty-13733.svg", + "erc20": false, + "erc721": true, + "symbol": "CK", + "decimals": 0 + }, + "0x0D8775F648430679A709E98d2b0Cb6250d2887EF": { + "name": "Basic Attention Token", + "logo": "bat.svg", + "erc20": true, + "symbol": "BAT", + "decimals": 18 + }, + "0x6090A6e47849629b7245Dfa1Ca21D94cd15878Ef": { + "name": "ENS Registrar", + "logo": "ens.svg" + } }, - "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v": { - "name": "USD Coin", - "logo": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v.png", - "erc20": false, - "symbol": "USDC", - "decimals": 6, - "chainId": "0x65", - "coingeckoId": "usd-coin" + "0xaa36a7": { + "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984": { + "name": "Uniswap", + "logo": "uni.svg", + "erc20": true, + "symbol": "UNI", + "decimals": 18 + } }, - "2inRoG4DuMRRzZxAt913CCdNZCu2eGsDD9kZTrsj2DAZ": { - "name": "Tesla Inc.", - "logo": "2inRoG4DuMRRzZxAt913CCdNZCu2eGsDD9kZTrsj2DAZ.png", - "erc20": false, - "symbol": "TSLA", - "decimals": 8, - "chainId": "0x65" + "0x89": { + "0xc2132D05D31c914a87C6611C10748AEb04B58e8F": { + "name": "Tether USD - PoS", + "logo": "usdt.png", + "erc20": true, + "symbol": "USDT", + "decimals": 6, + "coingeckoId": "tether" + } }, - "2kMpEJCZL8vEDZe7YPLMCS9Y3WKSAMedXBn7xHPvsWvi": { - "name": "SolarMoon", - "logo": "2kMpEJCZL8vEDZe7YPLMCS9Y3WKSAMedXBn7xHPvsWvi.png", - "erc20": false, - "symbol": "MOON", - "decimals": 5, - "chainId": "0x65", - "token2022": true + "0x65": { + "So11111111111111111111111111111111111111112": { + "name": "Wrapped SOL", + "logo": "So11111111111111111111111111111111111111112.png", + "erc20": false, + "symbol": "SOL", + "decimals": 9, + "coingeckoId": "solana" + }, + "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v": { + "name": "USD Coin", + "logo": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v.png", + "erc20": false, + "symbol": "USDC", + "decimals": 6, + "coingeckoId": "usd-coin" + }, + "2inRoG4DuMRRzZxAt913CCdNZCu2eGsDD9kZTrsj2DAZ": { + "name": "Tesla Inc.", + "logo": "2inRoG4DuMRRzZxAt913CCdNZCu2eGsDD9kZTrsj2DAZ.png", + "erc20": false, + "symbol": "TSLA", + "decimals": 8 + }, + "2kMpEJCZL8vEDZe7YPLMCS9Y3WKSAMedXBn7xHPvsWvi": { + "name": "SolarMoon", + "logo": "2kMpEJCZL8vEDZe7YPLMCS9Y3WKSAMedXBn7xHPvsWvi.png", + "erc20": false, + "symbol": "MOON", + "decimals": 5, + "token2022": true + } } })"; @@ -488,10 +495,7 @@ TEST(BlockchainRegistryUnitTest, GetAllTokens) { base::test::TaskEnvironment task_environment; auto* registry = BlockchainRegistry::GetInstance(); TokenListMap token_list_map; - ASSERT_TRUE( - ParseTokenList(token_list_json, &token_list_map, mojom::CoinType::ETH)); - ASSERT_TRUE(ParseTokenList(solana_token_list_json, &token_list_map, - mojom::CoinType::SOL)); + ASSERT_TRUE(ParseTokenList(token_list_json, &token_list_map)); registry->UpdateTokenList(std::move(token_list_map)); // Loop twice to make sure getting the same list twice works @@ -583,10 +587,7 @@ TEST(BlockchainRegistryUnitTest, GetTokenByAddress) { base::test::TaskEnvironment task_environment; auto* registry = BlockchainRegistry::GetInstance(); TokenListMap token_list_map; - ASSERT_TRUE( - ParseTokenList(token_list_json, &token_list_map, mojom::CoinType::ETH)); - ASSERT_TRUE(ParseTokenList(solana_token_list_json, &token_list_map, - mojom::CoinType::SOL)); + ASSERT_TRUE(ParseTokenList(token_list_json, &token_list_map)); registry->UpdateTokenList(std::move(token_list_map)); base::RunLoop run_loop; registry->GetTokenByAddress( @@ -647,10 +648,7 @@ TEST(BlockchainRegistryUnitTest, GetTokenBySymbol) { base::test::TaskEnvironment task_environment; auto* registry = BlockchainRegistry::GetInstance(); TokenListMap token_list_map; - ASSERT_TRUE( - ParseTokenList(token_list_json, &token_list_map, mojom::CoinType::ETH)); - ASSERT_TRUE(ParseTokenList(solana_token_list_json, &token_list_map, - mojom::CoinType::SOL)); + ASSERT_TRUE(ParseTokenList(token_list_json, &token_list_map)); registry->UpdateTokenList(std::move(token_list_map)); base::RunLoop run_loop; registry->GetTokenBySymbol( @@ -1008,8 +1006,7 @@ TEST(BlockchainRegistryUnitTest, GetEthTokenListMap) { base::test::TaskEnvironment task_environment; auto* registry = BlockchainRegistry::GetInstance(); TokenListMap token_list_map; - ASSERT_TRUE( - ParseTokenList(token_list_json, &token_list_map, mojom::CoinType::ETH)); + ASSERT_TRUE(ParseTokenList(token_list_json, &token_list_map)); registry->UpdateTokenList(std::move(token_list_map)); // Loop twice to make sure getting the same list twice works @@ -1097,20 +1094,7 @@ TEST(BlockchainRegistryUnitTest, ParseLists) { ASSERT_TRUE(base::WriteFile(path.Append(FPL("coingecko-ids.json")), coingecko_ids_map_json)); ASSERT_TRUE( - base::WriteFile(path.Append(FPL("contract-map.json")), token_list_json)); - ASSERT_TRUE(base::WriteFile(path.Append(FPL("evm-contract-map.json")), R"({ - "0xc2132D05D31c914a87C6611C10748AEb04B58e8F": { - "name": "Tether USD - PoS", - "logo": "usdt.png", - "erc20": true, - "symbol": "USDT", - "decimals": 6, - "coingeckoId": "tether", - "chainId": "0x89" - } - })")); - ASSERT_TRUE(base::WriteFile(path.Append(FPL("solana-contract-map.json")), - solana_token_list_json)); + base::WriteFile(path.Append(FPL("coingecko.json")), token_list_json)); ASSERT_TRUE( base::WriteFile(path.Append(FPL("chainlist.json")), chain_list_json)); ASSERT_TRUE( @@ -1135,7 +1119,7 @@ TEST(BlockchainRegistryUnitTest, ParseLists) { "0x7f5c764cbc14f9669b88837ca1490cca17c31607"), "usd-coin"); - // contract-map.json + // coingecko.json EXPECT_EQ( registry ->GetTokenByAddress(mojom::kMainnetChainId, mojom::CoinType::ETH, @@ -1143,7 +1127,6 @@ TEST(BlockchainRegistryUnitTest, ParseLists) { ->symbol, "BAT"); - // evm-contract-map.json EXPECT_EQ(registry ->GetTokenByAddress( mojom::kPolygonMainnetChainId, mojom::CoinType::ETH, @@ -1151,7 +1134,6 @@ TEST(BlockchainRegistryUnitTest, ParseLists) { ->symbol, "USDT"); - // solana-contract-map.json EXPECT_EQ( registry ->GetTokenByAddress(mojom::kSolanaMainnet, mojom::CoinType::SOL, diff --git a/components/brave_wallet/browser/brave_wallet_constants.cc b/components/brave_wallet/browser/brave_wallet_constants.cc index de46375488f9..14d0af1648b1 100644 --- a/components/brave_wallet/browser/brave_wallet_constants.cc +++ b/components/brave_wallet/browser/brave_wallet_constants.cc @@ -38,22 +38,21 @@ const base::flat_map& GetEthBalanceScannerContractAddresses() { static base::NoDestructor> contract_addresses( - // Mainnet, Polygon, and Avalanche conctract addresses pulled from - // https://github.com/MyCryptoHQ/eth-scan - {{mojom::kMainnetChainId, - "0x08A8fDBddc160A7d5b957256b903dCAb1aE512C5"}, - {mojom::kPolygonMainnetChainId, - "0x08A8fDBddc160A7d5b957256b903dCAb1aE512C5"}, + // Ref: https://github.com/brave/evm-scanner + {{mojom::kArbitrumMainnetChainId, + "0xfA542DD20c1997D6e8b24387D64CB8336197df3d"}, {mojom::kAvalancheMainnetChainId, - "0x08A8fDBddc160A7d5b957256b903dCAb1aE512C5"}, - // BSC, Optimism, and Arbitrum contract addresses pulled from - // https://github.com/onyb/x/blob/75800edce88688dcfe59dd6b4a664087862369bb/core/evm/scanner/balances/EVMScanner.ts + "0x827aa7e7C0C665df227Fae6dd155c0048fec6978"}, + {mojom::kBaseMainnetChainId, + "0xF9164898C08f40DfB0999F94Bf9b9F73d66dfFeb"}, {mojom::kBnbSmartChainMainnetChainId, - "0x53242a975aa7c607e17138b0e0231162e3e68593"}, + "0x578E2574dDD2e609dDA7f6C8B2a90C540794B75e"}, + {mojom::kMainnetChainId, + "0x667e61DB0997B59681C15E07376185aE24f754Db"}, {mojom::kOptimismMainnetChainId, - "0x9e5076DF494FC949aBc4461F4E57592B81517D81"}, - {mojom::kArbitrumMainnetChainId, - "0xa3e7eb35e779f261ca604138d41d0258e995e97b"}}); + "0x2D1AacdEcd43Be64d82c14E9a6072A29dc804cAe"}, + {mojom::kPolygonMainnetChainId, + "0x0B7Dd2c628a6Ee40153D89ce68bdA82d4840CD34"}}); return *contract_addresses; } diff --git a/components/brave_wallet/browser/brave_wallet_constants.h b/components/brave_wallet/browser/brave_wallet_constants.h index c3ab4c2e1fa1..ac345840337e 100644 --- a/components/brave_wallet/browser/brave_wallet_constants.h +++ b/components/brave_wallet/browser/brave_wallet_constants.h @@ -26,6 +26,7 @@ inline constexpr int32_t kAutoLockMinutesMin = 1; inline constexpr int32_t kAutoLockMinutesMax = 10080; inline constexpr int32_t kAssetDiscoveryMinutesPerRequest = 1; +inline constexpr size_t kBalanceScannerBatchSize = 4000; inline constexpr char kWalletBaseDirectory[] = "BraveWallet"; inline constexpr char kImageSourceHost[] = "erc-token-images"; diff --git a/components/brave_wallet/browser/json_rpc_service.cc b/components/brave_wallet/browser/json_rpc_service.cc index fe3b5e9587ee..b37e0326c62f 100644 --- a/components/brave_wallet/browser/json_rpc_service.cc +++ b/components/brave_wallet/browser/json_rpc_service.cc @@ -15,6 +15,7 @@ #include "base/base64.h" #include "base/check.h" #include "base/check_is_test.h" +#include "base/containers/extend.h" #include "base/containers/flat_set.h" #include "base/functional/bind.h" #include "base/no_destructor.h" @@ -305,6 +306,34 @@ bool HasDuplicateNftIds( .size() != nft_identifiers.size(); } +std::optional GetEthBalanceScannerContractAddressForChain( + const std::string& chain_id) { + if (auto it = GetEthBalanceScannerContractAddresses().find(chain_id); + it != GetEthBalanceScannerContractAddresses().end()) { + return it->second; + } + return std::nullopt; +} + +std::vector ShrinkToBalanceScannerBatchSize( + std::vector& addresses) { + // `addresses` fit batch size, don't need `remaining_batch`. + if (addresses.size() <= kBalanceScannerBatchSize) { + return {}; + } + + // Move excess to `remaining_batch` and return it. + auto remaining_span = base::span(addresses).subspan(kBalanceScannerBatchSize); + + std::vector remaining_batch; + remaining_batch.reserve(remaining_span.size()); + for (auto& address : remaining_span) { + remaining_batch.push_back(std::move(address)); + } + addresses.resize(kBalanceScannerBatchSize); + return remaining_batch; +} + } // namespace JsonRpcService::JsonRpcService( @@ -925,6 +954,7 @@ void JsonRpcService::OnFilGetBalance(GetBalanceCallback callback, std::move(callback).Run(*balance, mojom::ProviderError::kSuccess, ""); } + void JsonRpcService::GetFilStateSearchMsgLimited( const std::string& chain_id, const std::string& cid, @@ -1268,56 +1298,109 @@ void JsonRpcService::OnGetERC20TokenAllowance( } void JsonRpcService::GetERC20TokenBalances( - const std::vector& token_contract_addresses, + const std::vector& token_addresses, const std::string& user_address, const std::string& chain_id, GetERC20TokenBalancesCallback callback) { - const auto& balance_scanner_contract_addresses = - GetEthBalanceScannerContractAddresses(); - if (!base::Contains(balance_scanner_contract_addresses, chain_id)) { + // Validate inputs + auto balance_scanner_contract_address = + GetEthBalanceScannerContractAddressForChain(chain_id); + if (!balance_scanner_contract_address) { std::move(callback).Run( {}, mojom::ProviderError::kInvalidParams, l10n_util::GetStringUTF8(IDS_WALLET_INVALID_PARAMETERS)); return; } - const auto& balance_scanner_contract_address = - balance_scanner_contract_addresses.at(chain_id); - if (token_contract_addresses.empty() || user_address.empty()) { + if (token_addresses.empty() || user_address.empty()) { std::move(callback).Run( {}, mojom::ProviderError::kInvalidParams, l10n_util::GetStringUTF8(IDS_WALLET_INVALID_PARAMETERS)); return; } - std::optional calldata = - balance_scanner::TokensBalance(user_address, token_contract_addresses); - if (!calldata) { + auto network_url = GetNetworkURL(chain_id, mojom::CoinType::ETH); + if (!network_url.is_valid()) { std::move(callback).Run( {}, mojom::ProviderError::kInvalidParams, l10n_util::GetStringUTF8(IDS_WALLET_INVALID_PARAMETERS)); return; } - auto network_url = GetNetworkURL(chain_id, mojom::CoinType::ETH); - if (!network_url.is_valid()) { + ProcessNextERC20Batch(token_addresses, user_address, + *balance_scanner_contract_address, network_url, + std::vector(), + std::move(callback)); +} + +void JsonRpcService::ProcessNextERC20Batch( + std::vector token_addresses, + const std::string& user_address, + const std::string& scanner_address, + const GURL& network_url, + std::vector accumulated_results, + GetERC20TokenBalancesCallback callback) { + DCHECK(!token_addresses.empty()); + + auto remaining = ShrinkToBalanceScannerBatchSize(token_addresses); + + // Process current batch + std::optional calldata = + balance_scanner::TokensBalance(user_address, token_addresses); + if (!calldata) { std::move(callback).Run( {}, mojom::ProviderError::kInvalidParams, l10n_util::GetStringUTF8(IDS_WALLET_INVALID_PARAMETERS)); return; } - // Makes the eth_call request to the balance scanner contract. + auto batch_callback = base::BindOnce( + &JsonRpcService::OnBatchERC20TokenBalances, + weak_ptr_factory_.GetWeakPtr(), user_address, scanner_address, + network_url, std::move(accumulated_results), std::move(remaining), + std::move(callback)); + auto internal_callback = base::BindOnce( &JsonRpcService::OnGetERC20TokenBalances, weak_ptr_factory_.GetWeakPtr(), - token_contract_addresses, std::move(callback)); - RequestInternal( - eth::eth_call(balance_scanner_contract_address, calldata.value()), true, - network_url, std::move(internal_callback)); + std::move(token_addresses), std::move(batch_callback)); + + RequestInternal(eth::eth_call(scanner_address, calldata.value()), true, + network_url, std::move(internal_callback)); +} + +void JsonRpcService::OnBatchERC20TokenBalances( + const std::string& user_address, + const std::string& scanner_address, + const GURL& network_url, + std::vector accumulated_results, + std::vector token_addresses, + GetERC20TokenBalancesCallback callback, + std::vector batch_results, + mojom::ProviderError error, + const std::string& error_message) { + if (error != mojom::ProviderError::kSuccess) { + std::move(callback).Run({}, error, error_message); + return; + } + + // Add batch results to accumulated results + base::Extend(accumulated_results, std::move(batch_results)); + + // All batches processed. Nothing to do. + if (token_addresses.empty()) { + std::move(callback).Run(std::move(accumulated_results), + mojom::ProviderError::kSuccess, ""); + return; + } + + // Process next batch + ProcessNextERC20Batch(std::move(token_addresses), user_address, + scanner_address, network_url, + std::move(accumulated_results), std::move(callback)); } void JsonRpcService::OnGetERC20TokenBalances( - const std::vector& token_contract_addresses, + const std::vector& token_addresses, GetERC20TokenBalancesCallback callback, APIRequestResult api_request_result) { if (!api_request_result.Is2XXResponseCode()) { @@ -1344,20 +1427,20 @@ void JsonRpcService::OnGetERC20TokenBalances( return; } - // The number of contract addresses supplied to the BalanceScanner + // The number of addresses supplied to the BalanceScanner // should match the number of balances it returns - if (token_contract_addresses.size() != results->size()) { + if (token_addresses.size() != results->size()) { std::move(callback).Run( {}, mojom::ProviderError::kInternalError, l10n_util::GetStringUTF8(IDS_WALLET_INTERNAL_ERROR)); return; } - // Match up the balances with the contract addresses + // Match up the balances with the addresses std::vector erc20_balance_results; - for (size_t i = 0; i < token_contract_addresses.size(); i++) { + for (size_t i = 0; i < token_addresses.size(); i++) { auto erc20_balance_result = mojom::ERC20BalanceResult::New(); - erc20_balance_result->contract_address = token_contract_addresses[i]; + erc20_balance_result->contract_address = token_addresses[i]; erc20_balance_result->balance = results->at(i); erc20_balance_results.push_back(std::move(erc20_balance_result)); } @@ -3431,7 +3514,6 @@ void JsonRpcService::OnAnkrGetAccountBalances( l10n_util::GetStringUTF8(IDS_WALLET_INTERNAL_ERROR)); return; } - auto result = ankr::ParseGetAccountBalanceResponse(api_request_result.value_body()); if (!result) { diff --git a/components/brave_wallet/browser/json_rpc_service.h b/components/brave_wallet/browser/json_rpc_service.h index e396f5d027da..7544d71e125b 100644 --- a/components/brave_wallet/browser/json_rpc_service.h +++ b/components/brave_wallet/browser/json_rpc_service.h @@ -187,11 +187,10 @@ class JsonRpcService : public mojom::JsonRpcService { const std::string& chain_id, GetERC20TokenAllowanceCallback callback) override; - void GetERC20TokenBalances( - const std::vector& token_contract_addresses, - const std::string& user_address, - const std::string& chain_id, - GetERC20TokenBalancesCallback callback) override; + void GetERC20TokenBalances(const std::vector& token_addresses, + const std::string& user_address, + const std::string& chain_id, + GetERC20TokenBalancesCallback callback) override; void UnstoppableDomainsResolveDns( const std::string& domain, @@ -612,10 +611,26 @@ class JsonRpcService : public mojom::JsonRpcService { APIRequestResult api_request_result); void OnGetERC20TokenAllowance(GetERC20TokenAllowanceCallback callback, APIRequestResult api_request_result); - void OnGetERC20TokenBalances( - const std::vector& token_contract_addresses, + void ProcessNextERC20Batch( + std::vector token_addresses, + const std::string& user_address, + const std::string& scanner_address, + const GURL& network_url, + std::vector accumulated_results, + GetERC20TokenBalancesCallback callback); + void OnBatchERC20TokenBalances( + const std::string& user_address, + const std::string& scanner_address, + const GURL& network_url, + std::vector accumulated_results, + std::vector token_addresses, GetERC20TokenBalancesCallback callback, - APIRequestResult api_request_result); + std::vector batch_results, + mojom::ProviderError error, + const std::string& error_message); + void OnGetERC20TokenBalances(const std::vector& token_addresses, + GetERC20TokenBalancesCallback callback, + APIRequestResult api_request_result); void OnUnstoppableDomainsResolveDns(const std::string& domain, const std::string& chain_id, APIRequestResult api_request_result); diff --git a/components/brave_wallet/browser/json_rpc_service_unittest.cc b/components/brave_wallet/browser/json_rpc_service_unittest.cc index 75d3355742fd..69f8c48ea9c6 100644 --- a/components/brave_wallet/browser/json_rpc_service_unittest.cc +++ b/components/brave_wallet/browser/json_rpc_service_unittest.cc @@ -7896,29 +7896,28 @@ TEST_F(JsonRpcServiceUnitTest, GetSPLTokenProgramByMint) { // Setup registry with two assets. const char token_list_json[] = R"( { - "2inRoG4DuMRRzZxAt913CCdNZCu2eGsDD9kZTrsj2DAZ": { - "name": "Tesla Inc.", - "logo": "2inRoG4DuMRRzZxAt913CCdNZCu2eGsDD9kZTrsj2DAZ.png", - "erc20": false, - "symbol": "TSLA", - "decimals": 8, - "chainId": "0x65" - }, - "2kMpEJCZL8vEDZe7YPLMCS9Y3WKSAMedXBn7xHPvsWvi": { - "name": "SolarMoon", - "logo": "2kMpEJCZL8vEDZe7YPLMCS9Y3WKSAMedXBn7xHPvsWvi.png", - "erc20": false, - "symbol": "MOON", - "decimals": 5, - "chainId": "0x65", - "token2022": true + "0x65": { + "2inRoG4DuMRRzZxAt913CCdNZCu2eGsDD9kZTrsj2DAZ": { + "name": "Tesla Inc.", + "logo": "2inRoG4DuMRRzZxAt913CCdNZCu2eGsDD9kZTrsj2DAZ.png", + "erc20": false, + "symbol": "TSLA", + "decimals": 8 + }, + "2kMpEJCZL8vEDZe7YPLMCS9Y3WKSAMedXBn7xHPvsWvi": { + "name": "SolarMoon", + "logo": "2kMpEJCZL8vEDZe7YPLMCS9Y3WKSAMedXBn7xHPvsWvi.png", + "erc20": false, + "symbol": "MOON", + "decimals": 5, + "token2022": true + } } })"; auto* registry = BlockchainRegistry::GetInstance(); TokenListMap token_list_map; - ASSERT_TRUE( - ParseTokenList(token_list_json, &token_list_map, mojom::CoinType::SOL)); + ASSERT_TRUE(ParseTokenList(token_list_json, &token_list_map)); registry->UpdateTokenList(std::move(token_list_map)); // Setup two user assets. diff --git a/components/brave_wallet_ui/common/constants/magics.ts b/components/brave_wallet_ui/common/constants/magics.ts index 804ab8786d6c..2c5a52245bc9 100644 --- a/components/brave_wallet_ui/common/constants/magics.ts +++ b/components/brave_wallet_ui/common/constants/magics.ts @@ -19,6 +19,9 @@ export const NATIVE_EVM_ASSET_CONTRACT_ADDRESS = export const WRAPPED_SOL_CONTRACT_ADDRESS = 'So11111111111111111111111111111111111111112' +export const POLYGON_NATIVE_ASSET_CONTRACT_ADDRESS = + '0x0000000000000000000000000000000000001010' + /** * Set this as a token's coingecko id to flag the token as "unknown" * This will allow for fallback UI when we do have all the required token info diff --git a/components/brave_wallet_ui/common/slices/endpoints/token_balances.endpoints.ts b/components/brave_wallet_ui/common/slices/endpoints/token_balances.endpoints.ts index 2af2858fbe1e..7abc09673158 100644 --- a/components/brave_wallet_ui/common/slices/endpoints/token_balances.endpoints.ts +++ b/components/brave_wallet_ui/common/slices/endpoints/token_balances.endpoints.ts @@ -14,6 +14,10 @@ import { WalletStatus } from '../../../constants/types' import { coinTypesMapping } from '../constants' +import { + NATIVE_EVM_ASSET_CONTRACT_ADDRESS, + POLYGON_NATIVE_ASSET_CONTRACT_ADDRESS +} from '../../../common/constants/magics' // types import { WalletApiEndpointBuilderParams } from '../api-base.slice' @@ -885,61 +889,43 @@ async function fetchAccountTokenBalanceRegistryForChainId({ onBalance: (arg: BalanceResult) => void | Promise cache: BaseQueryCache }): Promise { - // Construct arg to query native token for use in case the - // optimized balance fetcher kicks in. const nativeTokenArgs = arg.tokens.filter((token) => isNativeAsset(token)) - const nonNativeTokens = arg.tokens.filter((token) => !isNativeAsset(token)) - - await eachLimit( - nativeTokenArgs, - 2, - async (token: BraveWallet.BlockchainToken) => { - const balance = await fetchAccountTokenCurrentBalance({ - arg: { - accountId: arg.accountId, - token: token - }, - bitcoinWalletService, - jsonRpcService, - zcashWalletService - }) - - if (balance) { - onBalance({ - accountId: arg.accountId, - chainId: arg.chainId, - contractAddress: token.contractAddress, - balance, - coinType: token.coin, - tokenId: '', - isShielded: token.isShielded - }) - } - } - ) if (arg.coin === CoinTypes.ETH) { - // jsonRpcService.getERC20TokenBalances cannot handle - // native assets - if (nonNativeTokens.length === 0) { - return - } - const supportedChainIds = cache.balanceScannerSupportedChains || (await cache.getBalanceScannerSupportedChains()) - // ERC20 Tokens if (supportedChainIds.includes(arg.chainId)) { - const erc20Tokens = nonNativeTokens.filter((token) => !token.isNft) + // Filter for ERC20 tokens, including MATIC token on Polygon + // + // POL balance is fetched using the native asset contract, and not the + // proxy ERC20 contract (0x0000000000000000000000000000000000001010) to + // avoid double counting the balance. + const erc20Tokens = arg.tokens + .filter((token) => + !token.isNft && + !isNativeAsset(token) && + !(token.chainId === BraveWallet.POLYGON_MAINNET_CHAIN_ID && + token.contractAddress === POLYGON_NATIVE_ASSET_CONTRACT_ADDRESS) + ) + + const tokens = [ + ...nativeTokenArgs + .map(token => ({ + ...token, + contractAddress: NATIVE_EVM_ASSET_CONTRACT_ADDRESS + })), + ...erc20Tokens + ] // nothing to fetch - if (!erc20Tokens.length) { + if (!tokens.length) { return } const result = await jsonRpcService.getERC20TokenBalances( - erc20Tokens.map((token) => token.contractAddress), + tokens.map((token) => token.contractAddress), arg.accountId.address, arg.chainId ) @@ -966,7 +952,10 @@ async function fetchAccountTokenBalanceRegistryForChainId({ onBalance({ accountId: arg.accountId, chainId: arg.chainId, - contractAddress, + contractAddress: + contractAddress === NATIVE_EVM_ASSET_CONTRACT_ADDRESS + ? '' + : contractAddress, balance: new Amount(balance).format(), coinType: arg.coin, tokenId: '', // these are ERC20 tokens, @@ -979,7 +968,35 @@ async function fetchAccountTokenBalanceRegistryForChainId({ } } - if (arg.coin === CoinTypes.SOL && !arg.tokens) { + await eachLimit( + nativeTokenArgs, + 2, + async (token: BraveWallet.BlockchainToken) => { + const balance = await fetchAccountTokenCurrentBalance({ + arg: { + accountId: arg.accountId, + token: token + }, + bitcoinWalletService, + jsonRpcService, + zcashWalletService + }) + + if (balance) { + onBalance({ + accountId: arg.accountId, + chainId: arg.chainId, + contractAddress: token.contractAddress, + balance, + coinType: token.coin, + tokenId: '', + isShielded: token.isShielded + }) + } + } + ) + + if (arg.coin === CoinTypes.SOL) { const result = await jsonRpcService.getSPLTokenBalances( arg.accountId.address, arg.chainId @@ -994,7 +1011,7 @@ async function fetchAccountTokenBalanceRegistryForChainId({ } for (const { mint, amount } of result.balances) { - if (amount) { + if (amount && new Amount(amount).gt(0)) { onBalance({ accountId: arg.accountId, chainId: arg.chainId, @@ -1011,6 +1028,7 @@ async function fetchAccountTokenBalanceRegistryForChainId({ } // Fallback to fetching individual balances + const nonNativeTokens = arg.tokens.filter((token) => !isNativeAsset(token)) await eachLimit( nonNativeTokens, 10,