diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/activities/BraveWalletActivity.java b/android/java/org/chromium/chrome/browser/crypto_wallet/activities/BraveWalletActivity.java index 0c103e2065e..6e307fe370a 100644 --- a/android/java/org/chromium/chrome/browser/crypto_wallet/activities/BraveWalletActivity.java +++ b/android/java/org/chromium/chrome/browser/crypto_wallet/activities/BraveWalletActivity.java @@ -216,13 +216,6 @@ public class BraveWalletActivity extends BraveWalletBaseActivity implements OnNe } }); } - // Delay active wallet P3A report to avoid too many RPC calls at once - new Handler().postDelayed(new Runnable() { - @Override - public void run() { - Utils.reportActiveWalletsForP3A(BraveWalletActivity.this); - } - }, 10000); } @Override diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/modal/BraveWalletPanel.java b/android/java/org/chromium/chrome/browser/crypto_wallet/modal/BraveWalletPanel.java index a1e9bd6e4d2..a7e99d5a4ea 100644 --- a/android/java/org/chromium/chrome/browser/crypto_wallet/modal/BraveWalletPanel.java +++ b/android/java/org/chromium/chrome/browser/crypto_wallet/modal/BraveWalletPanel.java @@ -342,7 +342,7 @@ public class BraveWalletPanel implements DialogInterface { BalanceHelper.getNativeAssetsBalances( mBraveWalletPanelServices.getJsonRpcService(), selectedNetwork, new AccountInfo[] {selectedAccount}, - nativeAssetsBalances -> { + (coinType, nativeAssetsBalances) -> { double price = Utils.getOrDefault(assetPrices, asset.symbol.toLowerCase(Locale.getDefault()), 0.0d); diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/util/AsyncUtils.java b/android/java/org/chromium/chrome/browser/crypto_wallet/util/AsyncUtils.java index f6fe23c7900..80a19ef1edf 100644 --- a/android/java/org/chromium/chrome/browser/crypto_wallet/util/AsyncUtils.java +++ b/android/java/org/chromium/chrome/browser/crypto_wallet/util/AsyncUtils.java @@ -22,6 +22,7 @@ import org.chromium.brave_wallet.mojom.TxService; import org.chromium.mojo.bindings.Callbacks; import java.util.HashMap; +import java.util.HashSet; public class AsyncUtils { private final static String TAG = "AsyncUtils"; @@ -279,7 +280,8 @@ public class AsyncUtils { } public static class GetNativeAssetsBalancesResponseContext extends SingleResponseBaseContext - implements Callbacks.Callback1> { + implements Callbacks.Callback2> { + public int coinType; public HashMap nativeAssetsBalances; public GetNativeAssetsBalancesResponseContext(Runnable responseCompleteCallback) { @@ -287,22 +289,26 @@ public class AsyncUtils { } @Override - public void call(HashMap nativeAssetsBalances) { + public void call(Integer coinType, HashMap nativeAssetsBalances) { + this.coinType = coinType; this.nativeAssetsBalances = nativeAssetsBalances; super.fireResponseCompleteCallback(); } } public static class GetBlockchainTokensBalancesResponseContext extends SingleResponseBaseContext - implements Callbacks.Callback1>> { + implements Callbacks.Callback2>> { public HashMap> blockchainTokensBalances; + public int coinType; public GetBlockchainTokensBalancesResponseContext(Runnable responseCompleteCallback) { super(responseCompleteCallback); } @Override - public void call(HashMap> blockchainTokensBalances) { + public void call(Integer coinType, + HashMap> blockchainTokensBalances) { + this.coinType = coinType; this.blockchainTokensBalances = blockchainTokensBalances; super.fireResponseCompleteCallback(); } @@ -368,4 +374,19 @@ public class AsyncUtils { super.fireResponseCompleteCallback(); } } + + public static class GetP3ABalancesContext extends SingleResponseBaseContext + implements Callbacks.Callback1>> { + public HashMap> activeAddresses; + + public GetP3ABalancesContext(Runnable responseCompleteCallback) { + super(responseCompleteCallback); + } + + @Override + public void call(HashMap> activeAddresses) { + this.activeAddresses = activeAddresses; + super.fireResponseCompleteCallback(); + } + } } diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/util/BalanceHelper.java b/android/java/org/chromium/chrome/browser/crypto_wallet/util/BalanceHelper.java index 6ab45286082..4231e18ba85 100644 --- a/android/java/org/chromium/chrome/browser/crypto_wallet/util/BalanceHelper.java +++ b/android/java/org/chromium/chrome/browser/crypto_wallet/util/BalanceHelper.java @@ -10,18 +10,26 @@ import org.chromium.brave_wallet.mojom.AccountInfo; import org.chromium.brave_wallet.mojom.AssetPrice; import org.chromium.brave_wallet.mojom.AssetPriceTimeframe; import org.chromium.brave_wallet.mojom.AssetRatioService; +import org.chromium.brave_wallet.mojom.BlockchainRegistry; import org.chromium.brave_wallet.mojom.BlockchainToken; +import org.chromium.brave_wallet.mojom.BraveWalletService; import org.chromium.brave_wallet.mojom.CoinType; import org.chromium.brave_wallet.mojom.JsonRpcService; +import org.chromium.brave_wallet.mojom.KeyringService; import org.chromium.brave_wallet.mojom.NetworkInfo; import org.chromium.brave_wallet.mojom.ProviderError; +import org.chromium.chrome.browser.BraveConfig; +import org.chromium.chrome.browser.crypto_wallet.activities.BraveWalletBaseActivity; +import org.chromium.chrome.browser.crypto_wallet.util.AssetUtils; +import org.chromium.chrome.browser.preferences.BravePrefServiceBridge; import org.chromium.mojo.bindings.Callbacks; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Locale; -import java.util.stream.Stream; +import java.util.Map; public class BalanceHelper { private static String TAG = "BalanceHelper"; @@ -31,7 +39,7 @@ public class BalanceHelper { */ public static void getNativeAssetsBalances(JsonRpcService jsonRpcService, NetworkInfo selectedNetwork, AccountInfo[] accountInfos, - Callbacks.Callback1> callback) { + Callbacks.Callback2> callback) { if (jsonRpcService == null) return; HashMap nativeAssetsBalances = new HashMap(); @@ -75,7 +83,7 @@ public class BalanceHelper { nativeAssetsBalances.put(context.accountAddress, nativeAssetBalance); } - callback.call(nativeAssetsBalances); + callback.call(selectedNetwork.coin, nativeAssetsBalances); }); } @@ -86,7 +94,7 @@ public class BalanceHelper { */ public static void getBlockchainTokensBalances(JsonRpcService jsonRpcService, NetworkInfo selectedNetwork, AccountInfo[] accountInfos, BlockchainToken[] tokens, - Callbacks.Callback1>> callback) { + Callbacks.Callback2>> callback) { if (jsonRpcService == null) return; HashMap> blockchainTokensBalances = new HashMap>(); @@ -169,10 +177,93 @@ public class BalanceHelper { } } - callback.call(blockchainTokensBalances); + callback.call(selectedNetwork.coin, blockchainTokensBalances); }); } + public static void getP3ABalances(BraveWalletBaseActivity activity, NetworkInfo selectedNetwork, + Callbacks.Callback1>> callback) { + KeyringService keyringService = activity.getKeyringService(); + BraveWalletService braveWalletService = activity.getBraveWalletService(); + BlockchainRegistry blockchainRegistry = activity.getBlockchainRegistry(); + JsonRpcService jsonRpcService = activity.getJsonRpcService(); + assert braveWalletService != null && blockchainRegistry != null && keyringService != null + && jsonRpcService != null; + + boolean P3AEnabled = + BraveConfig.P3A_ENABLED && BravePrefServiceBridge.getInstance().getP3AEnabled(); + + HashMap> activeAddresses = new HashMap>(); + for (int coinType : Utils.P3ACoinTypes) + activeAddresses.put(coinType, new HashSet()); + + if (!P3AEnabled) { + callback.call(activeAddresses); + return; + } else { + Utils.getP3ANetworks(relevantNetworks -> { + // Exclude selectedNetwork if in relevantNetworks, also sort by CoinType + HashMap> sortedNetworks = + filterAndSortNetworksP3A(relevantNetworks, selectedNetwork); + int numNetworks = 0; + for (int coinType : Utils.P3ACoinTypes) + numNetworks += sortedNetworks.get(coinType).size(); + + AsyncUtils.MultiResponseHandler multiResponse = + new AsyncUtils.MultiResponseHandler(numNetworks * 2); + ArrayList + nativeAssetsBalancesResponses = + new ArrayList(); + ArrayList + blockchainTokensBalancesResponses = new ArrayList< + AsyncUtils.GetBlockchainTokensBalancesResponseContext>(); + + for (int coinType : Utils.P3ACoinTypes) { + keyringService.getKeyringInfo(AssetUtils.getKeyringForCoinType(coinType), keyringInfo -> { + for (NetworkInfo network : sortedNetworks.get(coinType)) { + TokenUtils.getUserOrAllTokensFiltered(braveWalletService, + blockchainRegistry, network, coinType, TokenUtils.TokenType.ALL, + true, tokens -> { + AsyncUtils.GetNativeAssetsBalancesResponseContext + getNativeAssetsBalancesContext = + new AsyncUtils + .GetNativeAssetsBalancesResponseContext( + multiResponse + .singleResponseComplete); + getNativeAssetsBalances(jsonRpcService, network, + keyringInfo.accountInfos, + getNativeAssetsBalancesContext); + nativeAssetsBalancesResponses.add( + getNativeAssetsBalancesContext); + AsyncUtils.GetBlockchainTokensBalancesResponseContext + getBlockchainTokensBalancesContext = + new AsyncUtils + .GetBlockchainTokensBalancesResponseContext( + multiResponse + .singleResponseComplete); + getBlockchainTokensBalances(jsonRpcService, network, + keyringInfo.accountInfos, tokens, + getBlockchainTokensBalancesContext); + blockchainTokensBalancesResponses.add( + getBlockchainTokensBalancesContext); + }); + } + }); + } + + multiResponse.setWhenAllCompletedAction(() -> { + updateActiveAddresses( + nativeAssetsBalancesResponses.toArray( + new AsyncUtils.GetNativeAssetsBalancesResponseContext[0]), + blockchainTokensBalancesResponses.toArray( + new AsyncUtils.GetBlockchainTokensBalancesResponseContext[0]), + activeAddresses); + callback.call(activeAddresses); + }); + }); + } + } + private static T addBalanceResponseContext( ArrayList contexts, T context, String accountAddress, BlockchainToken token) { @@ -181,4 +272,52 @@ public class BalanceHelper { contexts.add(context); return context; } + + private static HashMap> filterAndSortNetworksP3A( + NetworkInfo[] relevantNetworks, NetworkInfo selectedNetwork) { + HashMap> networksPerCoin = + new HashMap>(); + networksPerCoin.put(CoinType.ETH, new ArrayList()); + networksPerCoin.put(CoinType.SOL, new ArrayList()); + networksPerCoin.put(CoinType.FIL, new ArrayList()); + for (NetworkInfo network : relevantNetworks) { + if (network.chainId.equals(selectedNetwork.chainId)) continue; + switch (network.coin) { + case CoinType.ETH: + networksPerCoin.get(CoinType.ETH).add(network); + break; + case CoinType.SOL: + networksPerCoin.get(CoinType.SOL).add(network); + break; + case CoinType.FIL: + networksPerCoin.get(CoinType.FIL).add(network); + break; + } + } + return networksPerCoin; + } + + public static void updateActiveAddresses( + AsyncUtils.GetNativeAssetsBalancesResponseContext[] nativeAssetsBalancesResponses, + AsyncUtils + .GetBlockchainTokensBalancesResponseContext[] blockchainTokensBalancesResponses, + HashMap> activeAddresses) { + for (AsyncUtils.GetNativeAssetsBalancesResponseContext ctx : + nativeAssetsBalancesResponses) { + for (Map.Entry nativeEntry : ctx.nativeAssetsBalances.entrySet()) { + if (nativeEntry.getValue() > 0.0d) + activeAddresses.get(ctx.coinType).add(nativeEntry.getKey()); + } + } + for (AsyncUtils.GetBlockchainTokensBalancesResponseContext ctx : + blockchainTokensBalancesResponses) { + for (Map.Entry> accEntry : + ctx.blockchainTokensBalances.entrySet()) { + for (Map.Entry tokenEntry : accEntry.getValue().entrySet()) { + if (tokenEntry.getValue() > 0.0d) + activeAddresses.get(ctx.coinType).add(accEntry.getKey()); + } + } + } + } } diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/util/Utils.java b/android/java/org/chromium/chrome/browser/crypto_wallet/util/Utils.java index 69cea1b81da..41eaaffa54b 100644 --- a/android/java/org/chromium/chrome/browser/crypto_wallet/util/Utils.java +++ b/android/java/org/chromium/chrome/browser/crypto_wallet/util/Utils.java @@ -94,6 +94,7 @@ import org.chromium.chrome.browser.crypto_wallet.model.WalletListItemModel; import org.chromium.chrome.browser.crypto_wallet.observers.ApprovedTxObserver; import org.chromium.chrome.browser.crypto_wallet.util.WalletConstants; import org.chromium.chrome.browser.profiles.Profile; +import org.chromium.chrome.browser.util.LiveDataUtil; import org.chromium.chrome.browser.util.TabUtils; import org.chromium.mojo.bindings.Callbacks; import org.chromium.ui.text.NoUnderlineClickableSpan; @@ -162,6 +163,9 @@ public class Utils { public static final BigInteger MAX_UINT256 = BigInteger.ONE.shiftLeft(256).subtract(BigInteger.ONE); + // TODO(djandries): Add Filecoin when implemented + public static int[] P3ACoinTypes = {CoinType.ETH, CoinType.SOL}; + private static final int CLEAR_CLIPBOARD_INTERVAL = 60000; // In milliseconds public static List getRecoveryPhraseAsList(String recoveryPhrase) { @@ -1477,7 +1481,9 @@ public class Utils { JsonRpcService jsonRpcService = activity.getJsonRpcService(); assert braveWalletService != null && blockchainRegistry != null && assetRatioService != null && jsonRpcService != null; - AsyncUtils.MultiResponseHandler multiResponse = new AsyncUtils.MultiResponseHandler(3); + + BraveWalletP3a braveWalletP3A = activity.getBraveWalletP3A(); + AsyncUtils.MultiResponseHandler multiResponse = new AsyncUtils.MultiResponseHandler(4); TokenUtils.getUserOrAllTokensFiltered(braveWalletService, blockchainRegistry, selectedNetwork, selectedNetwork.coin, TokenUtils.TokenType.ALL, userAssetsOnly, @@ -1509,7 +1515,26 @@ public class Utils { BalanceHelper.getBlockchainTokensBalances(jsonRpcService, selectedNetwork, accountInfos, tokens, getBlockchainTokensBalancesContext); + AsyncUtils.GetP3ABalancesContext getP3ABalancesContext = + new AsyncUtils.GetP3ABalancesContext( + multiResponse.singleResponseComplete); + BalanceHelper.getP3ABalances(activity, selectedNetwork, getP3ABalancesContext); + multiResponse.setWhenAllCompletedAction(() -> { + // P3A active accounts + HashMap> activeAddresses = + getP3ABalancesContext.activeAddresses; + BalanceHelper.updateActiveAddresses( + new AsyncUtils.GetNativeAssetsBalancesResponseContext[] { + getNativeAssetsBalancesContext}, + new AsyncUtils.GetBlockchainTokensBalancesResponseContext[] { + getBlockchainTokensBalancesContext}, + activeAddresses); + for (int coinType : P3ACoinTypes) { + braveWalletP3A.recordActiveWalletCount( + activeAddresses.get(coinType).size(), coinType); + } + callback.call(fetchPricesContext.assetPrices, fullTokenList, getNativeAssetsBalancesContext.nativeAssetsBalances, getBlockchainTokensBalancesContext.blockchainTokensBalances); @@ -1517,70 +1542,30 @@ public class Utils { }); } - public static void reportActiveWalletsForP3A(BraveWalletBaseActivity activity) { - reportActiveCoinWalletsForP3A(activity, CoinType.ETH); - reportActiveCoinWalletsForP3A(activity, CoinType.SOL); - // TODO(djandries): Add Filecoin when implemented - } - - private static void reportActiveCoinWalletsForP3A( - BraveWalletBaseActivity activity, int coinType) { - BraveWalletP3a braveWalletP3A = activity.getBraveWalletP3A(); - KeyringService keyringService = activity.getKeyringService(); - JsonRpcService jsonRpcService = activity.getJsonRpcService(); - assert braveWalletP3A != null && keyringService != null && jsonRpcService != null; - + public static void getP3ANetworks(Callbacks.Callback1 callback) { + BraveActivity activity = BraveActivity.getBraveActivity(); + if (activity == null) { + callback.call(new NetworkInfo[0]); + } + ArrayList relevantNetworks = new ArrayList(); boolean countTestNetworks = CommandLine.getInstance().hasSwitch( BraveWalletConstants.P3A_COUNT_TEST_NETWORKS_SWITCH); + LiveDataUtil.observeOnce( + activity.getWalletModel().getCryptoModel().getNetworkModel().mCryptoNetworks, + allNetworks -> { + for (NetworkInfo network : allNetworks) { + // Exclude testnet chain data by default. + // Testnet chain counting can be enabled via the + // --p3a-count-wallet-test-networks switch + if (countTestNetworks + || !WalletConstants.KNOWN_TEST_CHAIN_IDS.contains( + network.chainId)) { + relevantNetworks.add(network); + } + } - keyringService.getKeyringInfo(getKeyringForCoinType(coinType), keyringInfo -> { - jsonRpcService.getAllNetworks(coinType, networks -> { - ArrayList relevantNetworks = new ArrayList(); - for (NetworkInfo net : networks) { - // Exclude testnet chain data by default. - // Testnet chain counting can be enabled via the - // --p3a-count-wallet-test-networks switch - if (countTestNetworks - || !WalletConstants.KNOWN_TEST_CHAIN_IDS.contains(net.chainId)) { - relevantNetworks.add(net); - } - } - AsyncUtils.MultiResponseHandler multiResponse = - new AsyncUtils.MultiResponseHandler(relevantNetworks.size()); - ArrayList txExtraInfoResponses = - new ArrayList(); - for (NetworkInfo net : relevantNetworks) { - AsyncUtils.GetTxExtraInfoResponseContext ctx = - new AsyncUtils.GetTxExtraInfoResponseContext( - multiResponse.singleResponseComplete); - getTxExtraInfo(activity, net, keyringInfo.accountInfos, null, true, ctx); - txExtraInfoResponses.add(ctx); - } - multiResponse.setWhenAllCompletedAction(() -> { - HashSet activeAddresses = new HashSet(); - for (AsyncUtils.GetTxExtraInfoResponseContext ctx : txExtraInfoResponses) { - // If account has a non-zero native coin balance or blockchain token - // balance, add the account address to the set. - for (Map.Entry entry : - ctx.nativeAssetsBalances.entrySet()) { - if (entry.getValue() > 0.0d) { - activeAddresses.add(entry.getKey()); - } - } - for (Map.Entry> accEntry : - ctx.blockchainTokensBalances.entrySet()) { - for (Map.Entry tokenEntry : - accEntry.getValue().entrySet()) { - if (tokenEntry.getValue() > 0.0d) { - activeAddresses.add(accEntry.getKey()); - } - } - } - } - braveWalletP3A.recordActiveWalletCount(activeAddresses.size(), coinType); + callback.call(relevantNetworks.toArray(new NetworkInfo[0])); }); - }); - }); } public static boolean isNativeToken(NetworkInfo selectedNetwork, BlockchainToken token) {