diff --git a/android/brave_java_resources.gni b/android/brave_java_resources.gni index 7fb6fdd592e7..70f619cd62e1 100644 --- a/android/brave_java_resources.gni +++ b/android/brave_java_resources.gni @@ -881,10 +881,10 @@ brave_java_resources = [ "java/res/layout/fragment_create_account.xml", "java/res/layout/fragment_create_account.xml", "java/res/layout/fragment_create_custom_filters.xml", + "java/res/layout/fragment_creating_wallet.xml", "java/res/layout/fragment_cross_promotional_modal_dialog.xml", "java/res/layout/fragment_dapps_message.xml", "java/res/layout/fragment_dormant_users_engagement_dialog.xml", - "java/res/layout/fragment_download_component_progress.xml", "java/res/layout/fragment_encryption_key.xml", "java/res/layout/fragment_link_subscription_dialog.xml", "java/res/layout/fragment_market.xml", diff --git a/android/brave_java_sources.gni b/android/brave_java_sources.gni index 44de53d2a59a..6cf0b4e3d49e 100644 --- a/android/brave_java_sources.gni +++ b/android/brave_java_sources.gni @@ -90,7 +90,6 @@ brave_java_sources = [ "../../brave/android/java/org/chromium/chrome/browser/brave_news/models/FeedItemsCard.java", "../../brave/android/java/org/chromium/chrome/browser/brave_stats/BraveStatsBottomSheetDialogFragment.java", "../../brave/android/java/org/chromium/chrome/browser/brave_stats/BraveStatsUtil.java", - "../../brave/android/java/org/chromium/chrome/browser/component_updater/BraveComponentUpdater.java", "../../brave/android/java/org/chromium/chrome/browser/contextmenu/BraveChromeContextMenuPopulator.java", "../../brave/android/java/org/chromium/chrome/browser/crash/BravePureJavaExceptionReporter.java", "../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/AssetRatioServiceFactory.java", @@ -158,8 +157,8 @@ brave_java_sources = [ "../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/dapps/SignTransactionFragment.java", "../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/dapps/SiweMessageFragment.java", "../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/onboarding_fragments/BackupWalletFragment.java", + "../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/onboarding_fragments/CreatingWalletFragment.java", "../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/onboarding_fragments/CryptoOnboardingFragment.java", - "../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/onboarding_fragments/DownloadComponentProgressFragment.java", "../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/onboarding_fragments/RecoveryPhraseFragment.java", "../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/onboarding_fragments/RestoreWalletFragment.java", "../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/onboarding_fragments/SecurePasswordFragment.java", @@ -210,7 +209,6 @@ brave_java_sources = [ "../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/util/Utils.java", "../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/util/Validations.java", "../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/util/WalletConstants.java", - "../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/util/WalletDataFilesInstaller.java", "../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/util/WalletDataFilesInstallerUtil.java", "../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/util/WalletNativeUtils.java", "../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/util/WalletUtils.java", diff --git a/android/java/org/chromium/chrome/browser/component_updater/BraveComponentUpdater.java b/android/java/org/chromium/chrome/browser/component_updater/BraveComponentUpdater.java deleted file mode 100644 index c6f47f2e9c56..000000000000 --- a/android/java/org/chromium/chrome/browser/component_updater/BraveComponentUpdater.java +++ /dev/null @@ -1,159 +0,0 @@ -/* Copyright (c) 2023 The Brave Authors. All rights reserved. - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at https://mozilla.org/MPL/2.0/. */ - -package org.chromium.chrome.browser.component_updater; - -import org.json.JSONException; -import org.json.JSONObject; - -import org.chromium.base.Log; -import org.chromium.base.ThreadUtils; -import org.chromium.base.annotations.CalledByNative; -import org.chromium.base.annotations.JNINamespace; -import org.chromium.base.annotations.NativeMethods; - -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -/** - * Class for handling progress of component update in Java. - * Basically this is just bridge to native component updater classes at - * brave_component_updater_android.cc. - */ -@JNINamespace("chrome::android") -public class BraveComponentUpdater { - private static final String TAG = "BCU"; - private long mNativeBraveComponentUpdaterAndroid; - private static BraveComponentUpdater sBraveComponentUpdater; - - public static BraveComponentUpdater get() { - ThreadUtils.assertOnUiThread(); - if (sBraveComponentUpdater == null) { - sBraveComponentUpdater = new BraveComponentUpdater(); - } - return sBraveComponentUpdater; - } - - private BraveComponentUpdater() { - init(); - } - - @CalledByNative - private void setNativePtr(long nativePtr) { - assert mNativeBraveComponentUpdaterAndroid == 0; - mNativeBraveComponentUpdaterAndroid = nativePtr; - } - - private void init() { - if (mNativeBraveComponentUpdaterAndroid == 0) { - BraveComponentUpdaterJni.get().init(BraveComponentUpdater.this); - } - } - - @Override - protected void finalize() { - destroy(); - } - - private void destroy() { - if (mNativeBraveComponentUpdaterAndroid != 0) { - BraveComponentUpdaterJni.get().destroy(mNativeBraveComponentUpdaterAndroid); - mNativeBraveComponentUpdaterAndroid = 0; - } - } - - /** - * Listener for the component update changes. - */ - public interface ComponentUpdaterListener { - // Invoked when component update info has changed. - void onComponentUpdateEvent(int event, String id); - } - - private final List mComponentUpdaterListeners = - new CopyOnWriteArrayList<>(); - - public void addComponentUpdateEventListener(ComponentUpdaterListener listener) { - ThreadUtils.assertOnUiThread(); - mComponentUpdaterListeners.add(listener); - } - - public void removeComponentUpdateEventListener(ComponentUpdaterListener listener) { - ThreadUtils.assertOnUiThread(); - mComponentUpdaterListeners.remove(listener); - } - - @CalledByNative - protected void componentStateUpdated(int event, String id) { - for (ComponentUpdaterListener listener : mComponentUpdaterListeners) { - listener.onComponentUpdateEvent(event, id); - } - } - - /** - * Class describing the progress of component download. - */ - public static class CrxUpdateItem { - // From components/update_client/update_client.h - public static final int STATUS_NEW = 0; - public static final int STATUS_CHECKING = 1; - public static final int STATUS_CAN_UPDATE = 2; - public static final int STATUS_DOWNLOADING_DIFF = 3; - public static final int STATUS_DOWNLOADING = 4; - public static final int STATUS_DOWNLOADED = 5; - public static final int STATUS_UPDATING_DIFF = 6; - public static final int STATUS_UPDATING = 7; - public static final int STATUS_UPDATED = 8; - public static final int STATUS_UP_TO_DATE = 9; - public static final int STATUS_UPDATE_ERROR = 10; - public static final int STATUS_UNINSTALLED = 11; - public static final int STATUS_REGISTRATION = 12; - public static final int STATUS_RUN = 13; - public static final int STATUS_LAST_STATUS = 14; - - public String mId; - public long mDownloadedBytes = -1; - public long mTotalBytes = -1; - public int mState = -1; - - public boolean isInProgress() { - return mState == STATUS_DOWNLOADING_DIFF || mState == STATUS_DOWNLOADING - || mState == STATUS_DOWNLOADED || mState == STATUS_UPDATING_DIFF - || mState == STATUS_UPDATING; - } - - public void normalizeZeroProgress() { - if (mState == STATUS_DOWNLOADING && mTotalBytes == -1 && mDownloadedBytes == -1) { - mTotalBytes = 100; - mDownloadedBytes = 0; - } - } - } - - public CrxUpdateItem getUpdateState(String id) { - String json = BraveComponentUpdaterJni.get().getUpdateState( - mNativeBraveComponentUpdaterAndroid, id); - - CrxUpdateItem crxUpdateItem = new CrxUpdateItem(); - try { - JSONObject result = new JSONObject(json); - crxUpdateItem.mId = result.getString("id"); - crxUpdateItem.mDownloadedBytes = (long) result.getDouble("downloaded_bytes"); - crxUpdateItem.mTotalBytes = (long) result.getDouble("total_bytes"); - crxUpdateItem.mState = result.getInt("state"); - } catch (JSONException e) { - Log.e(TAG, "getUpdateState JSONException error ", e); - } - - return crxUpdateItem; - } - - @NativeMethods - interface Natives { - void init(BraveComponentUpdater caller); - void destroy(long nativeBraveComponentUpdaterAndroid); - String getUpdateState(long nativeBraveComponentUpdaterAndroid, String id); - } -} 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 5ee6b691bdd6..be3044ccb506 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 @@ -44,6 +44,7 @@ import org.chromium.chrome.browser.crypto_wallet.fragments.PortfolioFragment; import org.chromium.chrome.browser.crypto_wallet.fragments.SwapBottomSheetDialogFragment; import org.chromium.chrome.browser.crypto_wallet.fragments.onboarding_fragments.BackupWalletFragment; +import org.chromium.chrome.browser.crypto_wallet.fragments.onboarding_fragments.CreatingWalletFragment; import org.chromium.chrome.browser.crypto_wallet.fragments.onboarding_fragments.RecoveryPhraseFragment; import org.chromium.chrome.browser.crypto_wallet.fragments.onboarding_fragments.RestoreWalletFragment; import org.chromium.chrome.browser.crypto_wallet.fragments.onboarding_fragments.SecurePasswordFragment; @@ -313,6 +314,15 @@ private void replaceNavigationFragments(int type, boolean doNavigate, boolean is getResources().getString(R.string.restore_crypto_account), restoreWalletFragment), mCryptoWalletOnboardingViewPager.getCurrentItem() + 1); + + CreatingWalletFragment creatingWalletFragment = new CreatingWalletFragment(); + creatingWalletFragment.setOnNextPageListener(this); + mCryptoWalletOnboardingPagerAdapter.replaceWithNavigationItem( + new NavigationItem( + getResources() + .getString(R.string.your_wallet_is_restoring_page_title), + creatingWalletFragment), + mCryptoWalletOnboardingPagerAdapter.getCount()); } else if (type == ONBOARDING_ACTION) { List navigationItems = new ArrayList<>(); SecurePasswordFragment securePasswordFragment = new SecurePasswordFragment(); @@ -320,6 +330,7 @@ private void replaceNavigationFragments(int type, boolean doNavigate, boolean is navigationItems.add( new NavigationItem(getResources().getString(R.string.secure_your_crypto), securePasswordFragment)); + addWalletCreatingPage(navigationItems, true); addBackupWalletSequence(navigationItems, true); mCryptoWalletOnboardingPagerAdapter.replaceWithNavigationItems( navigationItems, mCryptoWalletOnboardingViewPager.getCurrentItem() + 1); @@ -384,12 +395,24 @@ private void addBackupWalletSequence( verifyRecoveryPhraseFragment)); } + private void addWalletCreatingPage(List navigationItems, boolean isOnboarding) { + CreatingWalletFragment creatingWalletFragment = new CreatingWalletFragment(); + creatingWalletFragment.setOnNextPageListener(this); + navigationItems.add( + new NavigationItem( + getResources().getString(R.string.your_wallet_is_creating_page_title), + creatingWalletFragment)); + } + public void showOnboardingLayout() { addRemoveSecureFlag(true); mCryptoOnboardingLayout.setVisibility(View.VISIBLE); mCryptoLayout.setVisibility(View.GONE); List navigationItems = new ArrayList<>(); + // We don't need addWalletCreatingPage here, as showOnboardingLayout + // is invoked only when we didn't back up wallet initially and doing + // it later from `Backup your crypto wallet` bubble. addBackupWalletSequence(navigationItems, false); if (mCryptoWalletOnboardingPagerAdapter != null diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/onboarding_fragments/CreatingWalletFragment.java b/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/onboarding_fragments/CreatingWalletFragment.java new file mode 100644 index 000000000000..c33f47940756 --- /dev/null +++ b/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/onboarding_fragments/CreatingWalletFragment.java @@ -0,0 +1,23 @@ +/* Copyright (c) 2023 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +package org.chromium.chrome.browser.crypto_wallet.fragments.onboarding_fragments; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import org.chromium.chrome.R; + +/** Onboarding fragment for Brave Wallet which shows the spinner while wallet is created/restored */ +public class CreatingWalletFragment extends CryptoOnboardingFragment { + @Override + public View onCreateView( + LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_creating_wallet, container, false); + return view; + } +} diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/onboarding_fragments/DownloadComponentProgressFragment.java b/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/onboarding_fragments/DownloadComponentProgressFragment.java deleted file mode 100644 index 5a3a0260ed06..000000000000 --- a/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/onboarding_fragments/DownloadComponentProgressFragment.java +++ /dev/null @@ -1,109 +0,0 @@ -/* Copyright (c) 2023 The Brave Authors. All rights reserved. - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at https://mozilla.org/MPL/2.0/. */ - -package org.chromium.chrome.browser.crypto_wallet.fragments.onboarding_fragments; - -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; - -import org.chromium.base.TimeUtils.ElapsedRealtimeMillisTimer; -import org.chromium.chrome.R; -import org.chromium.chrome.browser.component_updater.BraveComponentUpdater; -import org.chromium.chrome.browser.crypto_wallet.util.WalletDataFilesInstaller; - -/** - * A fragment to display progress of Brave Wallet data files component - * download. Used on all Brave Wallet Onboarding fragments. - */ -public class DownloadComponentProgressFragment extends Fragment { - private static final String WALLET_COMPONENT_ID = - WalletDataFilesInstaller.getWalletDataFilesComponentId(); - private TextView mComponentDownloadProgress; - private BraveComponentUpdater.ComponentUpdaterListener mComponentUpdaterListener; - private ElapsedRealtimeMillisTimer mGracePeriodNoDisplayTimer; - private static final long GRACE_NO_DISPLAY_MSEC = 1000; - - @Override - public View onCreateView( - LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - return inflater.inflate(R.layout.fragment_download_component_progress, container, false); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - mComponentDownloadProgress = view.findViewById(R.id.download_progress_text_view); - setupDownloadProgress(); - } - - @Override - public void onDestroyView() { - if (mComponentUpdaterListener != null) { - BraveComponentUpdater.get().removeComponentUpdateEventListener( - mComponentUpdaterListener); - mComponentUpdaterListener = null; - } - super.onDestroyView(); - } - - private void setupDownloadProgress() { - BraveComponentUpdater.CrxUpdateItem updateItem = BraveComponentUpdater.get().getUpdateState( - WalletDataFilesInstaller.getWalletDataFilesComponentId()); - showHideProgressByItem(updateItem); - - mComponentUpdaterListener = (event, id) -> { - if (!WALLET_COMPONENT_ID.equals(id)) { - return; - } - BraveComponentUpdater.CrxUpdateItem crxUpdateItem = - BraveComponentUpdater.get().getUpdateState( - WalletDataFilesInstaller.getWalletDataFilesComponentId()); - showHideProgressByItem(crxUpdateItem); - }; - - BraveComponentUpdater.get().addComponentUpdateEventListener(mComponentUpdaterListener); - } - - private void showHideProgressByItem(BraveComponentUpdater.CrxUpdateItem updateItem) { - assert updateItem.mTotalBytes < (long) Integer.MAX_VALUE; - assert updateItem.mDownloadedBytes < (long) Integer.MAX_VALUE; - - if (updateItem.mTotalBytes > 0 && updateItem.mDownloadedBytes == updateItem.mTotalBytes - || !updateItem.isInProgress()) { - mComponentDownloadProgress.setVisibility(View.GONE); - return; - } - - // On fast internet connection download goes quickly, and the text view can be - // shown for only ~0.3 sec, that doesn't have much sense and may look strange. - // We will use the timer and don't display text view for 1 sec so if it - // complete in 1 sec - we will not show it at all. - if (mGracePeriodNoDisplayTimer == null) { - mGracePeriodNoDisplayTimer = new ElapsedRealtimeMillisTimer(); - } - - if (mGracePeriodNoDisplayTimer.getElapsedMillis() < GRACE_NO_DISPLAY_MSEC) { - return; - } - - updateItem.normalizeZeroProgress(); - - mComponentDownloadProgress.setVisibility(View.VISIBLE); - - assert updateItem.mTotalBytes != 0; - String progressText = String.format(getString(R.string.download_wallet_data_files_progress), - (int) (100 * updateItem.mDownloadedBytes / updateItem.mTotalBytes)); - - mComponentDownloadProgress.setText(progressText); - } -} diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/onboarding_fragments/RestoreWalletFragment.java b/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/onboarding_fragments/RestoreWalletFragment.java index 5830102e1367..2d9ad1fdc6d9 100644 --- a/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/onboarding_fragments/RestoreWalletFragment.java +++ b/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/onboarding_fragments/RestoreWalletFragment.java @@ -39,13 +39,13 @@ public class RestoreWalletFragment extends CryptoOnboardingFragment { private static final String IS_ONBOARDING = "is_onboarding"; - private EditText recoveryPhraseText; - private EditText passwordEdittext; - private EditText retypePasswordEdittext; - private CheckBox showRecoveryPhraseCheckbox; - private CheckBox restoreLegacyWalletCheckbox; - private boolean isLegacyWalletRestoreEnable; - private boolean isOnboarding; + private EditText mRecoveryPhraseText; + private EditText mPasswordEdittext; + private EditText mRetypePasswordEdittext; + private CheckBox mShowRecoveryPhraseCheckbox; + private CheckBox mRestoreLegacyWalletCheckbox; + private boolean mIsLegacyWalletRestoreEnable; + private boolean mIsOnboarding; public static RestoreWalletFragment newInstance(boolean isOnboarding) { RestoreWalletFragment fragment = new RestoreWalletFragment(); @@ -67,76 +67,85 @@ private KeyringService getKeyringService() { @Override public View onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - isOnboarding = getArguments().getBoolean(IS_ONBOARDING); + mIsOnboarding = getArguments().getBoolean(IS_ONBOARDING); return inflater.inflate(R.layout.fragment_restore_wallet, container, false); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - recoveryPhraseText = view.findViewById(R.id.recovery_phrase_text); - passwordEdittext = view.findViewById(R.id.restore_wallet_password); - retypePasswordEdittext = view.findViewById(R.id.restore_wallet_retype_password); - showRecoveryPhraseCheckbox = view.findViewById(R.id.restore_wallet_checkbox); - restoreLegacyWalletCheckbox = view.findViewById(R.id.restore_legacy_wallet_checkbox); + mRecoveryPhraseText = view.findViewById(R.id.recovery_phrase_text); + mPasswordEdittext = view.findViewById(R.id.restore_wallet_password); + mRetypePasswordEdittext = view.findViewById(R.id.restore_wallet_retype_password); + mShowRecoveryPhraseCheckbox = view.findViewById(R.id.restore_wallet_checkbox); + mRestoreLegacyWalletCheckbox = view.findViewById(R.id.restore_legacy_wallet_checkbox); ImageView restoreWalletCopyImage = view.findViewById(R.id.restore_wallet_copy_image); assert getActivity() != null; restoreWalletCopyImage.setOnClickListener( - v -> recoveryPhraseText.setText(Utils.getTextFromClipboard(getActivity()))); - - showRecoveryPhraseCheckbox.setOnCheckedChangeListener((buttonView, isChecked) -> { - int cursorPos = recoveryPhraseText.getSelectionStart(); - if (isChecked) { - recoveryPhraseText.setTransformationMethod( - HideReturnsTransformationMethod.getInstance()); - } else { - recoveryPhraseText.setTransformationMethod( - PasswordTransformationMethod.getInstance()); - } - recoveryPhraseText.setSelection(cursorPos); - }); - - recoveryPhraseText.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {} - - @Override - public void afterTextChanged(Editable editable) {} - - @Override - public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { - String recoveryPhrase = charSequence.toString().trim(); - - // validate recoveryPhrase contains only string. not JSON and length is 24 - if (recoveryPhrase.matches("[a-zA-Z\\s]+") - && recoveryPhrase.split("\\s+").length == 24) { - restoreLegacyWalletCheckbox.setVisibility(View.VISIBLE); - } else { - restoreLegacyWalletCheckbox.setVisibility(View.GONE); - } - } - }); - - restoreLegacyWalletCheckbox.setOnCheckedChangeListener( - (buttonView, isChecked) -> { isLegacyWalletRestoreEnable = isChecked; }); + v -> mRecoveryPhraseText.setText(Utils.getTextFromClipboard(getActivity()))); + + mShowRecoveryPhraseCheckbox.setOnCheckedChangeListener( + (buttonView, isChecked) -> { + int cursorPos = mRecoveryPhraseText.getSelectionStart(); + if (isChecked) { + mRecoveryPhraseText.setTransformationMethod( + HideReturnsTransformationMethod.getInstance()); + } else { + mRecoveryPhraseText.setTransformationMethod( + PasswordTransformationMethod.getInstance()); + } + mRecoveryPhraseText.setSelection(cursorPos); + }); - Button secureCryptoButton = view.findViewById(R.id.btn_restore_wallet); - secureCryptoButton.setOnClickListener(v -> { - String passwordInput = passwordEdittext.getText().toString(); + mRecoveryPhraseText.addTextChangedListener( + new TextWatcher() { + @Override + public void beforeTextChanged( + CharSequence charSequence, int i, int i1, int i2) {} - KeyringService keyringService = getKeyringService(); - assert keyringService != null; - keyringService.isStrongPassword(passwordInput, result -> { - if (!result) { - passwordEdittext.setError(getResources().getString(R.string.password_text)); + @Override + public void afterTextChanged(Editable editable) {} - return; - } + @Override + public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { + String recoveryPhrase = charSequence.toString().trim(); - proceedWithAStrongPassword(passwordInput, recoveryPhraseText); - }); - }); + // validate recoveryPhrase contains only string. not JSON and length is 24 + if (recoveryPhrase.matches("[a-zA-Z\\s]+") + && recoveryPhrase.split("\\s+").length == 24) { + mRestoreLegacyWalletCheckbox.setVisibility(View.VISIBLE); + } else { + mRestoreLegacyWalletCheckbox.setVisibility(View.GONE); + } + } + }); + + mRestoreLegacyWalletCheckbox.setOnCheckedChangeListener( + (buttonView, isChecked) -> { + mIsLegacyWalletRestoreEnable = isChecked; + }); + + Button secureCryptoButton = view.findViewById(R.id.btn_restore_wallet); + secureCryptoButton.setOnClickListener( + v -> { + String passwordInput = mPasswordEdittext.getText().toString(); + + KeyringService keyringService = getKeyringService(); + assert keyringService != null; + keyringService.isStrongPassword( + passwordInput, + result -> { + if (!result) { + mPasswordEdittext.setError( + getResources().getString(R.string.password_text)); + + return; + } + + proceedWithAStrongPassword(passwordInput, mRecoveryPhraseText); + }); + }); } @RequiresApi(api = Build.VERSION_CODES.P) @@ -172,17 +181,20 @@ public void onAuthenticationError(int errorCode, CharSequence errString) { showFingerprintDialog(authenticationCallback); } - private void proceedWithAStrongPassword(String passwordInput, EditText recoveryPhraseText) { - String retypePasswordInput = retypePasswordEdittext.getText().toString(); + private void proceedWithAStrongPassword(String passwordInput, EditText mRecoveryPhraseText) { + String retypePasswordInput = mRetypePasswordEdittext.getText().toString(); if (!passwordInput.equals(retypePasswordInput)) { - retypePasswordEdittext.setError( + mRetypePasswordEdittext.setError( getResources().getString(R.string.retype_password_error)); } else { KeyringService keyringService = getKeyringService(); assert keyringService != null; - keyringService.restoreWallet(recoveryPhraseText.getText().toString().trim(), - passwordInput, isLegacyWalletRestoreEnable, result -> { + keyringService.restoreWallet( + mRecoveryPhraseText.getText().toString().trim(), + passwordInput, + mIsLegacyWalletRestoreEnable, + result -> { if (result) { Utils.hideKeyboard(getActivity()); keyringService.notifyWalletBackupComplete(); @@ -195,26 +207,30 @@ private void proceedWithAStrongPassword(String passwordInput, EditText recoveryP onNextPage.gotoNextPage(true); } Utils.setCryptoOnboarding(false); - Utils.clearClipboard(recoveryPhraseText.getText().toString().trim(), 0); + Utils.clearClipboard( + mRecoveryPhraseText.getText().toString().trim(), 0); Utils.clearClipboard(passwordInput, 0); Utils.clearClipboard(retypePasswordInput, 0); cleanUp(); } else { - Toast.makeText(getActivity(), R.string.account_recovery_failed, - Toast.LENGTH_SHORT) + Toast.makeText( + getActivity(), + R.string.account_recovery_failed, + Toast.LENGTH_SHORT) .show(); } }); + onNextPage.gotoNextPage(false); } } private void cleanUp() { - recoveryPhraseText.getText().clear(); - passwordEdittext.getText().clear(); - retypePasswordEdittext.getText().clear(); - showRecoveryPhraseCheckbox.setChecked(false); - restoreLegacyWalletCheckbox.setChecked(false); + mRecoveryPhraseText.getText().clear(); + mPasswordEdittext.getText().clear(); + mRetypePasswordEdittext.getText().clear(); + mShowRecoveryPhraseCheckbox.setChecked(false); + mRestoreLegacyWalletCheckbox.setChecked(false); } @RequiresApi(api = Build.VERSION_CODES.P) diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/onboarding_fragments/SecurePasswordFragment.java b/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/onboarding_fragments/SecurePasswordFragment.java index 0c14ebaa5caa..d18417c68d61 100644 --- a/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/onboarding_fragments/SecurePasswordFragment.java +++ b/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/onboarding_fragments/SecurePasswordFragment.java @@ -165,6 +165,7 @@ private void goToTheNextPage(String passwordInput) { mOnboardingViewModel.setPassword(passwordInput); onNextPage.gotoNextPage(false); }); + showCreatingWalletPage(); } } @@ -183,4 +184,8 @@ private void showFingerprintDialog( .build() .authenticate(new CancellationSignal(), executor, authenticationCallback); } + + private void showCreatingWalletPage() { + onNextPage.gotoNextPage(false); + } } diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/onboarding_fragments/SetupWalletFragment.java b/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/onboarding_fragments/SetupWalletFragment.java index 164b1422e039..fb4f0c70d486 100644 --- a/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/onboarding_fragments/SetupWalletFragment.java +++ b/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/onboarding_fragments/SetupWalletFragment.java @@ -26,7 +26,6 @@ import org.chromium.chrome.browser.app.BraveActivity; import org.chromium.chrome.browser.app.helpers.Api33AndPlusBackPressHelper; import org.chromium.chrome.browser.crypto_wallet.util.Utils; -import org.chromium.chrome.browser.crypto_wallet.util.WalletDataFilesInstaller; /** * Fragment to setup Brave Wallet @@ -80,7 +79,6 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat mRestartSetupAction = false; mRestartRestoreAction = false; }); - WalletDataFilesInstaller.registerWalletDataFilesComponentOnDemand(); } // We need to remove that check and restart once diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/util/WalletDataFilesInstaller.java b/android/java/org/chromium/chrome/browser/crypto_wallet/util/WalletDataFilesInstaller.java deleted file mode 100644 index f2621aad87d9..000000000000 --- a/android/java/org/chromium/chrome/browser/crypto_wallet/util/WalletDataFilesInstaller.java +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright (c) 2023 The Brave Authors. All rights reserved. - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at https://mozilla.org/MPL/2.0/. */ - -package org.chromium.chrome.browser.crypto_wallet.util; - -import org.chromium.base.annotations.JNINamespace; -import org.chromium.base.annotations.NativeMethods; - -/** - * Class for JNI interaction with wallet_data_files_installer_android.cc - */ -@JNINamespace("chrome::android") -public class WalletDataFilesInstaller { - public static void registerWalletDataFilesComponentOnDemand() { - WalletDataFilesInstallerJni.get().registerWalletDataFilesComponentOnDemand(); - } - - public static String getWalletDataFilesComponentId() { - return WalletDataFilesInstallerJni.get().getWalletDataFilesComponentId(); - } - - @NativeMethods - interface Natives { - void registerWalletDataFilesComponentOnDemand(); - String getWalletDataFilesComponentId(); - } -} diff --git a/android/java/res/layout/fragment_backup_wallet.xml b/android/java/res/layout/fragment_backup_wallet.xml index 7b5b559c362d..1d6be80f8ddc 100644 --- a/android/java/res/layout/fragment_backup_wallet.xml +++ b/android/java/res/layout/fragment_backup_wallet.xml @@ -150,13 +150,6 @@ android:textSize="16sp" android:textColor="@color/wallet_text_color"/> - - diff --git a/android/java/res/layout/fragment_creating_wallet.xml b/android/java/res/layout/fragment_creating_wallet.xml new file mode 100644 index 000000000000..11e2f8e57b72 --- /dev/null +++ b/android/java/res/layout/fragment_creating_wallet.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/android/java/res/layout/fragment_download_component_progress.xml b/android/java/res/layout/fragment_download_component_progress.xml deleted file mode 100644 index 75e7ebe9f642..000000000000 --- a/android/java/res/layout/fragment_download_component_progress.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - diff --git a/android/java/res/layout/fragment_recovery_phrase.xml b/android/java/res/layout/fragment_recovery_phrase.xml index 511d666bf5b9..e847c51d20b9 100644 --- a/android/java/res/layout/fragment_recovery_phrase.xml +++ b/android/java/res/layout/fragment_recovery_phrase.xml @@ -144,12 +144,6 @@ android:textSize="16sp" android:textColor="@color/wallet_text_color"/> - diff --git a/android/java/res/layout/fragment_restore_wallet.xml b/android/java/res/layout/fragment_restore_wallet.xml index cd1d71dfaa36..e9fb13b30db4 100644 --- a/android/java/res/layout/fragment_restore_wallet.xml +++ b/android/java/res/layout/fragment_restore_wallet.xml @@ -176,12 +176,6 @@ android:textColor="@android:color/white" style="?android:attr/borderlessButtonStyle"/> - diff --git a/android/java/res/layout/fragment_secure_password.xml b/android/java/res/layout/fragment_secure_password.xml index d3bdd668ec23..70d06c4bd4dd 100644 --- a/android/java/res/layout/fragment_secure_password.xml +++ b/android/java/res/layout/fragment_secure_password.xml @@ -114,13 +114,6 @@ android:layout_marginBottom="8dp" android:textColor="@android:color/white" style="?android:attr/borderlessButtonStyle"/> - - diff --git a/android/java/res/layout/fragment_verify_recovery_phrase.xml b/android/java/res/layout/fragment_verify_recovery_phrase.xml index 6895228b821a..a335430f2903 100644 --- a/android/java/res/layout/fragment_verify_recovery_phrase.xml +++ b/android/java/res/layout/fragment_verify_recovery_phrase.xml @@ -91,13 +91,6 @@ android:textSize="16sp" android:textColor="@color/wallet_text_color"/> - - diff --git a/browser/brave_browser_process_impl.cc b/browser/brave_browser_process_impl.cc index 587817fc5c40..503d64e7f089 100644 --- a/browser/brave_browser_process_impl.cc +++ b/browser/brave_browser_process_impl.cc @@ -16,6 +16,7 @@ #include "brave/browser/brave_referrals/referrals_service_delegate.h" #include "brave/browser/brave_shields/ad_block_subscription_download_manager_getter.h" #include "brave/browser/brave_stats/brave_stats_updater.h" +#include "brave/browser/brave_wallet/wallet_data_files_installer_delegate_impl.h" #include "brave/browser/component_updater/brave_component_updater_configurator.h" #include "brave/browser/misc_metrics/process_misc_metrics.h" #include "brave/browser/net/brave_system_request_handler.h" @@ -33,6 +34,7 @@ #include "brave/components/brave_shields/browser/brave_farbling_service.h" #include "brave/components/brave_shields/common/features.h" #include "brave/components/brave_sync/network_time_helper.h" +#include "brave/components/brave_wallet/browser/wallet_data_files_installer.h" #include "brave/components/constants/pref_names.h" #include "brave/components/debounce/browser/debounce_component_installer.h" #include "brave/components/debounce/common/features.h" @@ -240,6 +242,9 @@ void BraveBrowserProcessImpl::StartBraveServices() { brave_sync::NetworkTimeHelper::GetInstance()->SetNetworkTimeTracker( g_browser_process->network_time_tracker()); + + brave_wallet::WalletDataFilesInstaller::GetInstance().SetDelegate( + std::make_unique()); } brave_shields::AdBlockService* BraveBrowserProcessImpl::ad_block_service() { diff --git a/browser/brave_wallet/android/sources.gni b/browser/brave_wallet/android/sources.gni index 829f7410a755..ca9069d8121a 100644 --- a/browser/brave_wallet/android/sources.gni +++ b/browser/brave_wallet/android/sources.gni @@ -11,7 +11,6 @@ brave_browser_brave_wallet_android_sources = [ "//brave/browser/brave_wallet/android/keyring_service_factory_android.cc", "//brave/browser/brave_wallet/android/swap_service_factory_android.cc", "//brave/browser/brave_wallet/android/tx_service_factory_android.cc", - "//brave/browser/brave_wallet/android/wallet_data_files_installer_android.cc", "//brave/browser/brave_wallet/android/wallet_native_utils_android.cc", "//brave/browser/permissions/brave_dapp_permission_prompt_dialog_controller_android.cc", "//brave/browser/permissions/brave_dapp_permission_prompt_dialog_controller_android.h", diff --git a/browser/brave_wallet/android/wallet_data_files_installer_android.cc b/browser/brave_wallet/android/wallet_data_files_installer_android.cc deleted file mode 100644 index ffe67b805a4c..000000000000 --- a/browser/brave_wallet/android/wallet_data_files_installer_android.cc +++ /dev/null @@ -1,54 +0,0 @@ -/* Copyright (c) 2023 The Brave Authors. All rights reserved. - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at https://mozilla.org/MPL/2.0/. */ - -#include "base/android/jni_android.h" -#include "base/android/jni_string.h" -#include "base/containers/contains.h" -#include "base/functional/bind.h" -#include "base/task/sequenced_task_runner.h" -#include "brave/build/android/jni_headers/WalletDataFilesInstaller_jni.h" -#include "brave/components/brave_wallet/browser/wallet_data_files_installer.h" -#include "build/build_config.h" -#include "chrome/browser/browser_process.h" -#include "components/component_updater/component_updater_service.h" - -static_assert BUILDFLAG(IS_ANDROID); - -namespace chrome { -namespace android { - -namespace { - -bool IsBraveWalletDataFilesComponentRegistered() { - std::vector registered_ids = - g_browser_process->component_updater()->GetComponentIDs(); - return base::Contains( - registered_ids, - std::string(brave_wallet::GetWalletDataFilesComponentId())); -} - -} // namespace - -static void -JNI_WalletDataFilesInstaller_RegisterWalletDataFilesComponentOnDemand( - JNIEnv* env) { - if (IsBraveWalletDataFilesComponentRegistered()) { - return; - } - - component_updater::ComponentUpdateService* cus = - g_browser_process->component_updater(); - - ::brave_wallet::RegisterWalletDataFilesComponentOnDemand(cus); -} - -static base::android::ScopedJavaLocalRef -JNI_WalletDataFilesInstaller_GetWalletDataFilesComponentId(JNIEnv* env) { - return base::android::ConvertUTF8ToJavaString( - env, ::brave_wallet::GetWalletDataFilesComponentId()); -} - -} // namespace android -} // namespace chrome diff --git a/browser/brave_wallet/wallet_data_files_installer_delegate_impl.cc b/browser/brave_wallet/wallet_data_files_installer_delegate_impl.cc new file mode 100644 index 000000000000..1546f74e9e36 --- /dev/null +++ b/browser/brave_wallet/wallet_data_files_installer_delegate_impl.cc @@ -0,0 +1,19 @@ +/* Copyright (c) 2023 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/browser/brave_wallet/wallet_data_files_installer_delegate_impl.h" +#include "chrome/browser/browser_process.h" + +namespace brave_wallet { + +component_updater::ComponentUpdateService* +WalletDataFilesInstallerDelegateImpl::GetComponentUpdater() { + if (!g_browser_process) { // May be null in unit tests. + return nullptr; + } + return g_browser_process->component_updater(); +} + +} // namespace brave_wallet diff --git a/browser/brave_wallet/wallet_data_files_installer_delegate_impl.h b/browser/brave_wallet/wallet_data_files_installer_delegate_impl.h new file mode 100644 index 000000000000..e764d0d7abdd --- /dev/null +++ b/browser/brave_wallet/wallet_data_files_installer_delegate_impl.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2023 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_BROWSER_BRAVE_WALLET_WALLET_DATA_FILES_INSTALLER_DELEGATE_IMPL_H_ +#define BRAVE_BROWSER_BRAVE_WALLET_WALLET_DATA_FILES_INSTALLER_DELEGATE_IMPL_H_ + +#include "brave/components/brave_wallet/browser/wallet_data_files_installer_delegate.h" + +namespace brave_wallet { + +class WalletDataFilesInstallerDelegateImpl + : public WalletDataFilesInstallerDelegate { + public: + WalletDataFilesInstallerDelegateImpl() = default; + ~WalletDataFilesInstallerDelegateImpl() override = default; + component_updater::ComponentUpdateService* GetComponentUpdater() override; +}; + +} // namespace brave_wallet + +#endif // BRAVE_BROWSER_BRAVE_WALLET_WALLET_DATA_FILES_INSTALLER_DELEGATE_IMPL_H_ diff --git a/browser/component_updater/BUILD.gn b/browser/component_updater/BUILD.gn index dedc0dcbc076..21c8458b48eb 100644 --- a/browser/component_updater/BUILD.gn +++ b/browser/component_updater/BUILD.gn @@ -28,15 +28,4 @@ source_set("component_updater") { if (is_win) { deps += [ "//chrome/installer/util:with_no_strings" ] } - - if (is_android) { - sources += [ - "brave_component_updater_android.cc", - "brave_component_updater_android.h", - ] - deps += [ - "//brave/build/android:jni_headers", - "//chrome/browser:browser_process", - ] - } } diff --git a/browser/component_updater/brave_component_updater_android.cc b/browser/component_updater/brave_component_updater_android.cc deleted file mode 100644 index 0be3ae54a497..000000000000 --- a/browser/component_updater/brave_component_updater_android.cc +++ /dev/null @@ -1,82 +0,0 @@ -/* Copyright (c) 2023 The Brave Authors. All rights reserved. - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at https://mozilla.org/MPL/2.0/. */ - -#include "brave/browser/component_updater/brave_component_updater_android.h" - -#include "base/android/jni_android.h" -#include "base/android/jni_string.h" -#include "base/json/json_writer.h" -#include "brave/build/android/jni_headers/BraveComponentUpdater_jni.h" -#include "chrome/browser/browser_process.h" -#include "components/update_client/crx_update_item.h" -#include "components/update_client/update_engine.h" - -namespace chrome { -namespace android { - -BraveComponentUpdaterAndroid::BraveComponentUpdaterAndroid( - JNIEnv* env, - const base::android::JavaRef& obj) - : weak_java_brave_sync_worker_(env, obj) { - Java_BraveComponentUpdater_setNativePtr(env, obj, - reinterpret_cast(this)); - - component_updater::ComponentUpdateService* component_updater = - g_browser_process->component_updater(); - DCHECK(component_updater); - - if (component_updater) { - observation_.Observe(component_updater); - } -} - -BraveComponentUpdaterAndroid::~BraveComponentUpdaterAndroid() { - // Observer will be removed by ScopedObservation -} - -void BraveComponentUpdaterAndroid::Destroy(JNIEnv* env) { - delete this; -} - -void BraveComponentUpdaterAndroid::OnEvent(Events event, - const std::string& id) { - // Notify Java code - JNIEnv* env = base::android::AttachCurrentThread(); - Java_BraveComponentUpdater_componentStateUpdated( - env, weak_java_brave_sync_worker_.get(env), static_cast(event), - base::android::ConvertUTF8ToJavaString(env, id)); -} - -base::android::ScopedJavaLocalRef -BraveComponentUpdaterAndroid::GetUpdateState( - JNIEnv* env, - const base::android::JavaParamRef& id) { - std::string json_string; - update_client::CrxUpdateItem item; - - if (g_browser_process->component_updater()->GetComponentDetails( - base::android::ConvertJavaStringToUTF8(id), &item)) { - base::Value::Dict value; - value.Set("id", item.id); - value.Set("downloaded_bytes", static_cast(item.downloaded_bytes)); - value.Set("total_bytes", static_cast(item.total_bytes)); - value.Set("state", static_cast(item.state)); - - if (!base::JSONWriter::Write(value, &json_string)) { - VLOG(1) << "Writing to JSON string failed. Passing empty result to Java " - "code."; - } - } - return base::android::ConvertUTF8ToJavaString(env, json_string); -} - -static void JNI_BraveComponentUpdater_Init( - JNIEnv* env, - const base::android::JavaParamRef& jcaller) { - new BraveComponentUpdaterAndroid(env, jcaller); -} - -} // namespace android -} // namespace chrome diff --git a/browser/component_updater/brave_component_updater_android.h b/browser/component_updater/brave_component_updater_android.h deleted file mode 100644 index 568db6e417c9..000000000000 --- a/browser/component_updater/brave_component_updater_android.h +++ /dev/null @@ -1,47 +0,0 @@ -/* Copyright (c) 2023 The Brave Authors. All rights reserved. - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at https://mozilla.org/MPL/2.0/. */ - -#ifndef BRAVE_BROWSER_COMPONENT_UPDATER_BRAVE_COMPONENT_UPDATER_ANDROID_H_ -#define BRAVE_BROWSER_COMPONENT_UPDATER_BRAVE_COMPONENT_UPDATER_ANDROID_H_ - -#include - -#include - -#include "base/android/jni_weak_ref.h" -#include "base/memory/raw_ptr.h" -#include "base/scoped_observation.h" -#include "components/component_updater/component_updater_service.h" - -namespace chrome { -namespace android { - -class BraveComponentUpdaterAndroid : public component_updater::ServiceObserver { - public: - BraveComponentUpdaterAndroid(JNIEnv* env, - const base::android::JavaRef& obj); - ~BraveComponentUpdaterAndroid() override; - - base::android::ScopedJavaLocalRef GetUpdateState( - JNIEnv* env, - const base::android::JavaParamRef& id); - - void Destroy(JNIEnv* env); - - private: - // ServiceObserver implementation. - void OnEvent(Events event, const std::string& id) override; - - base::ScopedObservation - observation_{this}; - - JavaObjectWeakGlobalRef weak_java_brave_sync_worker_; -}; - -} // namespace android -} // namespace chrome - -#endif // BRAVE_BROWSER_COMPONENT_UPDATER_BRAVE_COMPONENT_UPDATER_ANDROID_H_ diff --git a/browser/extensions/api/brave_wallet_api.cc b/browser/extensions/api/brave_wallet_api.cc index af78152f8ffe..476094ffdf3a 100644 --- a/browser/extensions/api/brave_wallet_api.cc +++ b/browser/extensions/api/brave_wallet_api.cc @@ -23,6 +23,7 @@ #include "brave/components/brave_wallet/common/common_utils.h" #include "brave/components/l10n/common/localization_util.h" #include "brave/grit/brave_generated_resources.h" +#include "chrome/browser/browser_process.h" #include "chrome/browser/extensions/api/tabs/tabs_constants.h" #include "chrome/browser/extensions/extension_tab_util.h" #include "chrome/browser/profiles/profile.h" @@ -68,9 +69,7 @@ ExtensionFunction::ResponseAction BraveWalletNotifyWalletUnlockFunction::Run() { return RespondNow(Error("Not available in Tor context")); } - Profile* profile = Profile::FromBrowserContext(browser_context()); - ::brave_wallet::UpdateLastUnlockPref(profile->GetPrefs()); - + ::brave_wallet::UpdateLastUnlockPref(g_browser_process->local_state()); return RespondNow(NoArguments()); } diff --git a/browser/sources.gni b/browser/sources.gni index 4db6ae3a7164..d0ab534fc878 100644 --- a/browser/sources.gni +++ b/browser/sources.gni @@ -88,6 +88,8 @@ brave_chrome_browser_sources = [ "//brave/browser/brave_shell_integration.h", "//brave/browser/brave_tab_helpers.cc", "//brave/browser/brave_tab_helpers.h", + "//brave/browser/brave_wallet/wallet_data_files_installer_delegate_impl.cc", + "//brave/browser/brave_wallet/wallet_data_files_installer_delegate_impl.h", "//brave/browser/browser_context_keyed_service_factories.cc", "//brave/browser/browser_context_keyed_service_factories.h", "//brave/browser/geolocation/brave_geolocation_permission_context_delegate.cc", diff --git a/browser/ui/android/strings/android_brave_strings.grd b/browser/ui/android/strings/android_brave_strings.grd index 2d5135122d91..81c260fc2fe2 100644 --- a/browser/ui/android/strings/android_brave_strings.grd +++ b/browser/ui/android/strings/android_brave_strings.grd @@ -2085,6 +2085,15 @@ Are you sure you want to do this? Verified password did not match + + Creating Wallet... + + + Your wallet is restoring... + + + Your wallet is being created... + 1H @@ -3216,9 +3225,6 @@ If you don't accept this request, VPN will not reconnect and your internet conne Solana - - Downloading wallet data file \u2022 %1$d37\u0025\u0025 - Connected to %1$s diff --git a/build/android/BUILD.gn b/build/android/BUILD.gn index 617ab74a83f0..4dd84a86b4b7 100644 --- a/build/android/BUILD.gn +++ b/build/android/BUILD.gn @@ -214,7 +214,6 @@ generate_jni("jni_headers") { "//brave/android/java/org/chromium/chrome/browser/bookmarks/BraveBookmarkBridge.java", "//brave/android/java/org/chromium/chrome/browser/brave_leo/BraveLeoSettingsLauncherHelper.java", "//brave/android/java/org/chromium/chrome/browser/brave_news/BraveNewsControllerFactory.java", - "//brave/android/java/org/chromium/chrome/browser/component_updater/BraveComponentUpdater.java", "//brave/android/java/org/chromium/chrome/browser/crypto_wallet/AssetRatioServiceFactory.java", "//brave/android/java/org/chromium/chrome/browser/crypto_wallet/BlockchainRegistryFactory.java", "//brave/android/java/org/chromium/chrome/browser/crypto_wallet/BraveWalletProviderDelegateImplHelper.java", @@ -225,7 +224,6 @@ generate_jni("jni_headers") { "//brave/android/java/org/chromium/chrome/browser/crypto_wallet/TxServiceFactory.java", "//brave/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/dapps/ConnectAccountFragment.java", "//brave/android/java/org/chromium/chrome/browser/crypto_wallet/permission/BraveDappPermissionPromptDialog.java", - "//brave/android/java/org/chromium/chrome/browser/crypto_wallet/util/WalletDataFilesInstaller.java", "//brave/android/java/org/chromium/chrome/browser/crypto_wallet/util/WalletDataFilesInstallerUtil.java", "//brave/android/java/org/chromium/chrome/browser/crypto_wallet/util/WalletNativeUtils.java", "//brave/android/java/org/chromium/chrome/browser/informers/BraveSyncAccountDeletedInformer.java", diff --git a/build/android/config.gni b/build/android/config.gni index 310e266b2472..ed470ad4fdc6 100644 --- a/build/android/config.gni +++ b/build/android/config.gni @@ -110,7 +110,6 @@ brave_jni_headers_sources = [ "//brave/android/java/org/chromium/chrome/browser/app/BraveActivity.java", "//brave/android/java/org/chromium/chrome/browser/bookmarks/BraveBookmarkBridge.java", "//brave/android/java/org/chromium/chrome/browser/brave_news/BraveNewsControllerFactory.java", - "//brave/android/java/org/chromium/chrome/browser/component_updater/BraveComponentUpdater.java", "//brave/android/java/org/chromium/chrome/browser/crypto_wallet/AssetRatioServiceFactory.java", "//brave/android/java/org/chromium/chrome/browser/crypto_wallet/BlockchainRegistryFactory.java", "//brave/android/java/org/chromium/chrome/browser/crypto_wallet/BraveWalletProviderDelegateImplHelper.java", @@ -121,7 +120,6 @@ brave_jni_headers_sources = [ "//brave/android/java/org/chromium/chrome/browser/crypto_wallet/TxServiceFactory.java", "//brave/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/dapps/ConnectAccountFragment.java", "//brave/android/java/org/chromium/chrome/browser/crypto_wallet/permission/BraveDappPermissionPromptDialog.java", - "//brave/android/java/org/chromium/chrome/browser/crypto_wallet/util/WalletDataFilesInstaller.java", "//brave/android/java/org/chromium/chrome/browser/crypto_wallet/util/WalletNativeUtils.java", "//brave/android/java/org/chromium/chrome/browser/informers/BraveSyncAccountDeletedInformer.java", "//brave/android/java/org/chromium/chrome/browser/misc_metrics/MiscAndroidMetricsFactory.java", diff --git a/chromium_src/chrome/browser/component_updater/registration.cc b/chromium_src/chrome/browser/component_updater/registration.cc index 7210d44d476f..de43e05b82c5 100644 --- a/chromium_src/chrome/browser/component_updater/registration.cc +++ b/chromium_src/chrome/browser/component_updater/registration.cc @@ -33,7 +33,9 @@ namespace component_updater { void RegisterComponentsForUpdate() { RegisterComponentsForUpdate_ChromiumImpl(); ComponentUpdateService* cus = g_browser_process->component_updater(); - brave_wallet::RegisterWalletDataFilesComponent(cus); + brave_wallet::WalletDataFilesInstaller::GetInstance() + .MaybeRegisterWalletDataFilesComponent(cus, + g_browser_process->local_state()); psst::RegisterPsstComponent( cus, base::BindOnce(&component_updater::BraveOnDemandUpdate)); } diff --git a/components/brave_wallet/browser/BUILD.gn b/components/brave_wallet/browser/BUILD.gn index 11e32ffbe9b9..40f884bc9d0a 100644 --- a/components/brave_wallet/browser/BUILD.gn +++ b/components/brave_wallet/browser/BUILD.gn @@ -205,6 +205,7 @@ static_library("browser") { "unstoppable_domains_multichain_calls.h", "wallet_data_files_installer.cc", "wallet_data_files_installer.h", + "wallet_data_files_installer_delegate.h", "zcash/zcash_block_tracker.cc", "zcash/zcash_block_tracker.h", "zcash/zcash_rpc.cc", @@ -261,6 +262,7 @@ static_library("browser") { "//components/keyed_service/core", "//components/prefs", "//components/sync_preferences", + "//components/update_client", "//components/value_store", "//crypto", "//services/data_decoder/public/cpp", @@ -280,14 +282,6 @@ static_library("browser") { } configs += [ ":sardine_config" ] - - if (is_android) { - sources += [ - "wallet_data_files_installer_android_util.cc", - "wallet_data_files_installer_android_util.h", - ] - deps += [ "//brave/build/android:jni_headers" ] - } } if (!is_ios) { diff --git a/components/brave_wallet/browser/blockchain_registry.cc b/components/brave_wallet/browser/blockchain_registry.cc index d4df2ed8a67a..02a2b00e1ce5 100644 --- a/components/brave_wallet/browser/blockchain_registry.cc +++ b/components/brave_wallet/browser/blockchain_registry.cc @@ -9,21 +9,246 @@ #include #include "base/containers/flat_set.h" +#include "base/files/file_path.h" #include "base/no_destructor.h" #include "base/ranges/algorithm.h" #include "base/strings/stringprintf.h" #include "brave/components/brave_wallet/browser/brave_wallet_constants.h" #include "brave/components/brave_wallet/browser/brave_wallet_utils.h" +#include "brave/components/brave_wallet/browser/wallet_data_files_installer.h" #include "brave/components/brave_wallet/common/brave_wallet.mojom-shared.h" +#include "brave/components/json/rs/src/lib.rs.h" #include "net/base/url_util.h" +#include "services/data_decoder/public/cpp/json_sanitizer.h" namespace brave_wallet { +namespace { + +struct ParseListsResult { + CoingeckoIdsMap coingecko_ids_map; + TokenListMap token_list_map; + ChainList chain_list; + DappListMap dapp_lists; + OnRampTokensListMap on_ramp_token_lists; + OffRampTokensListMap off_ramp_token_lists; + std::vector on_ramp_currencies_list; + std::vector ofac_addresses; +}; + +void HandleRampTokenLists(const absl::optional& result, + ParseListsResult& out) { + if (!result.has_value()) { + return; + } + auto parsedRampTokensListMaps = ParseRampTokenListMaps(*result); + if (!parsedRampTokensListMaps) { + VLOG(1) << "Can't parse on/off ramp token lists."; + return; + } + + if (parsedRampTokensListMaps->first.empty()) { + VLOG(1) << "On ramp supported token lists is empty."; + } else { + out.on_ramp_token_lists = std::move(parsedRampTokensListMaps->first); + } + + if (parsedRampTokensListMaps->second.empty()) { + VLOG(1) << "Off ramp supported sell token lists is empty."; + } else { + out.off_ramp_token_lists = std::move(parsedRampTokensListMaps->second); + } +} + +void HandleOnRampCurrenciesLists(const absl::optional& result, + ParseListsResult& out) { + if (!result.has_value()) { + return; + } + + absl::optional> lists = + ParseOnRampCurrencyLists(*result); + if (!lists) { + VLOG(1) << "Can't parse on ramp supported sell token lists."; + return; + } + + out.on_ramp_currencies_list = std::move(*lists); +} + +base::FilePath ResolveAbsolutePath(const base::FilePath& input_path) { + // On some platforms (e.g. Mac) we use symlinks for paths. Convert paths to + // absolute paths to avoid unexpected failure. base::MakeAbsoluteFilePath() + // requires IO so it needs to be posted. + const base::FilePath output_path = base::MakeAbsoluteFilePath(input_path); + + if (output_path.empty()) { + LOG(ERROR) << "Failed to get absolute install path."; + } + + return output_path; +} + +absl::optional ParseJsonFile(base::FilePath path, + const std::string& filename) { + // We do not sanitize the result here via JsonSanitizer::Sanitize to optimize + // the performance because we are processing data from our own CRX downloaded + // via component updater, hence it is considered as trusted input. + // See https://github.com/brave/brave-browser/issues/30940 for details. + std::string json_content; + const base::FilePath json_path = path.AppendASCII(filename); + if (!base::ReadFileToString(json_path, &json_content)) { + LOG(ERROR) << "Can't read file: " << filename; + return absl::nullopt; + } + + return json_content; +} + +void DoParseCoingeckoIdsMap(const base::FilePath& dir, ParseListsResult& out) { + auto result = ParseJsonFile(dir, "coingecko-ids.json"); + if (!result) { + return; + } + + absl::optional coingecko_ids_map = + ParseCoingeckoIdsMap(*result); + if (!coingecko_ids_map) { + VLOG(1) << "Can't parse coingecko-ids.json"; + return; + } + + 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); + if (!result) { + return; + } + + TokenListMap lists; + if (!ParseTokenList(*result, &lists, coin_type)) { + VLOG(1) << "Can't parse token list."; + return; + } + + for (auto& list_pair : lists) { + out.token_list_map[list_pair.first] = std::move(list_pair.second); + } +} + +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) { + return; + } + + ChainList chains; + if (!ParseChainList(*result, &chains)) { + VLOG(1) << "Can't parse chain list."; + return; + } + + out.chain_list = std::move(chains); +} + +void DoParseDappLists(const base::FilePath& dir, ParseListsResult& out) { + auto result = ParseJsonFile(dir, "dapp-lists.json"); + if (!result) { + return; + } + + auto converted_json = + std::string(json::convert_all_numbers_to_string(*result, "")); + if (converted_json.empty()) { + return; + } + + absl::optional lists = ParseDappLists(converted_json); + if (!lists) { + VLOG(1) << "Can't parse dapp lists."; + return; + } + + out.dapp_lists = std::move(*lists); +} + +void DoParseOnRampLists(const base::FilePath& dir, ParseListsResult& out) { + auto result = ParseJsonFile(dir, "ramp-tokens.json"); + HandleRampTokenLists(result, out); + + result = ParseJsonFile(dir, "on-ramp-currency-lists.json"); + HandleOnRampCurrenciesLists(result, out); +} + +void DoParseOfacAddressesLists(const base::FilePath& dir, + ParseListsResult& out) { + auto result = + ParseJsonFile(dir, "ofac-sanctioned-digital-currency-addresses.json"); + if (!result) { + return; + } + + absl::optional> list = + ParseOfacAddressesList(*result); + if (!list) { + VLOG(1) << "Can't parse ofac addresses list."; + return; + } + + out.ofac_addresses = std::move(*list); +} + +void UpdateRegistry(base::OnceClosure callback, ParseListsResult result) { + auto* registry = BlockchainRegistry::GetInstance(); + registry->UpdateCoingeckoIdsMap(std::move(result.coingecko_ids_map)); + registry->UpdateTokenList(std::move(result.token_list_map)); + registry->UpdateChainList(std::move(result.chain_list)); + registry->UpdateDappList(std::move(result.dapp_lists)); + registry->UpdateOnRampTokenLists(std::move(result.on_ramp_token_lists)); + registry->UpdateOffRampTokenLists(std::move(result.off_ramp_token_lists)); + registry->UpdateOnRampCurrenciesLists( + std::move(result.on_ramp_currencies_list)); + registry->UpdateOfacAddressesList(std::move(result.ofac_addresses)); + std::move(callback).Run(); +} + +ParseListsResult DoParseLists(const base::FilePath& install_dir) { + const base::FilePath absolute_install_dir = ResolveAbsolutePath(install_dir); + if (absolute_install_dir.empty()) { + return {}; + } + + ParseListsResult result; + DoParseCoingeckoIdsMap(absolute_install_dir, result); + DoParseTokenList(absolute_install_dir, result); + DoParseChainList(absolute_install_dir, result); + DoParseDappLists(absolute_install_dir, result); + DoParseOnRampLists(absolute_install_dir, result); + DoParseOfacAddressesLists(absolute_install_dir, result); + return result; +} + +} // namespace + BlockchainRegistry::BlockchainRegistry() = default; + BlockchainRegistry::~BlockchainRegistry() = default; BlockchainRegistry* BlockchainRegistry::GetInstance() { static base::NoDestructor instance; + DCHECK_CALLED_ON_VALID_SEQUENCE(instance->sequence_checker_); return instance.get(); } @@ -308,4 +533,37 @@ bool BlockchainRegistry::IsOfacAddress(const std::string& address) { return base::Contains(ofac_addresses_, base::ToLowerASCII(address)); } +void BlockchainRegistry::ParseLists(const base::FilePath& install_dir, + base::OnceClosure callback) { + if (install_dir.empty()) { + std::move(callback).Run(); + return; + } + + base::ThreadPool::PostTaskAndReplyWithResult( + FROM_HERE, + {base::MayBlock(), base::TaskPriority::USER_VISIBLE, + base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}, + base::BindOnce(&DoParseLists, install_dir), + base::BindOnce(&UpdateRegistry, std::move(callback))); +} + +bool BlockchainRegistry::IsEmptyForTesting() { + return coingecko_ids_map_.empty() && token_list_map_.empty() && + chain_list_.empty() && dapp_lists_.empty() && + on_ramp_token_lists_.empty() && off_ramp_token_lists_.empty() && + on_ramp_currencies_list_.empty() && ofac_addresses_.empty(); +} + +void BlockchainRegistry::ResetForTesting() { + coingecko_ids_map_.clear(); + token_list_map_.clear(); + dapp_lists_.clear(); + on_ramp_token_lists_.clear(); + off_ramp_token_lists_.clear(); + on_ramp_currencies_list_.clear(); + ofac_addresses_.clear(); + receivers_.Clear(); +} + } // namespace brave_wallet diff --git a/components/brave_wallet/browser/blockchain_registry.h b/components/brave_wallet/browser/blockchain_registry.h index 8117fb1fe757..1204f86c1756 100644 --- a/components/brave_wallet/browser/blockchain_registry.h +++ b/components/brave_wallet/browser/blockchain_registry.h @@ -9,6 +9,10 @@ #include #include +#include "base/files/file_util.h" +#include "base/functional/bind.h" +#include "base/sequence_checker.h" +#include "base/task/thread_pool.h" #include "brave/components/brave_wallet/browser/blockchain_list_parser.h" #include "brave/components/brave_wallet/common/brave_wallet.mojom.h" #include "build/build_config.h" @@ -19,6 +23,7 @@ namespace base { template class NoDestructor; +class FilePath; } // namespace base namespace brave_wallet { @@ -26,7 +31,6 @@ namespace brave_wallet { class BlockchainRegistry : public mojom::BlockchainRegistry { public: BlockchainRegistry(const BlockchainRegistry&) = delete; - ~BlockchainRegistry() override; BlockchainRegistry& operator=(const BlockchainRegistry&) = delete; static BlockchainRegistry* GetInstance(); @@ -85,10 +89,21 @@ class BlockchainRegistry : public mojom::BlockchainRegistry { const std::string& contract_address, GetCoingeckoIdCallback callback) override; bool IsOfacAddress(const std::string& address); + void ParseLists(const base::FilePath& dir, base::OnceClosure callback); + + bool IsEmptyForTesting(); + void ResetForTesting(); + + private: + friend base::NoDestructor; + BlockchainRegistry(); + ~BlockchainRegistry() override; - protected: std::vector* GetTokenListFromChainId( const std::string& chain_id); + std::vector GetBuyTokens( + const std::vector& providers, + const std::string& chain_id); CoingeckoIdsMap coingecko_ids_map_; TokenListMap token_list_map_; @@ -98,15 +113,9 @@ class BlockchainRegistry : public mojom::BlockchainRegistry { OffRampTokensListMap off_ramp_token_lists_; std::vector on_ramp_currencies_list_; base::flat_set ofac_addresses_; - friend base::NoDestructor; - - BlockchainRegistry(); - private: + SEQUENCE_CHECKER(sequence_checker_); mojo::ReceiverSet receivers_; - std::vector GetBuyTokens( - const std::vector& providers, - const std::string& chain_id); }; } // namespace brave_wallet diff --git a/components/brave_wallet/browser/blockchain_registry_unittest.cc b/components/brave_wallet/browser/blockchain_registry_unittest.cc index 44f0add0c1ba..e58a23acbe7e 100644 --- a/components/brave_wallet/browser/blockchain_registry_unittest.cc +++ b/components/brave_wallet/browser/blockchain_registry_unittest.cc @@ -7,6 +7,9 @@ #include #include +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/files/scoped_temp_dir.h" #include "base/test/bind.h" #include "base/test/task_environment.h" #include "brave/components/brave_wallet/browser/blockchain_list_parser.h" @@ -16,6 +19,8 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#define FPL(x) FILE_PATH_LITERAL(x) + using testing::ElementsAreArray; namespace brave_wallet { @@ -1037,4 +1042,129 @@ TEST(BlockchainRegistryUnitTest, GetCoingeckoId) { "usd-coin"); } +TEST(BlockchainRegistryUnitTest, ParseLists) { + base::test::TaskEnvironment task_environment; + base::ScopedTempDir install_dir; + ASSERT_TRUE(install_dir.CreateUniqueTempDir()); + const base::FilePath path = install_dir.GetPath(); + + 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)); + ASSERT_TRUE( + base::WriteFile(path.Append(FPL("chainlist.json")), chain_list_json)); + ASSERT_TRUE( + base::WriteFile(path.Append(FPL("dapp-lists.json")), dapp_lists_json)); + ASSERT_TRUE(base::WriteFile(path.Append(FPL("ramp-tokens.json")), + ramp_token_lists_json)); + ASSERT_TRUE(base::WriteFile(path.Append(FPL("on-ramp-currency-lists.json")), + on_ramp_currency_lists_json)); + ASSERT_TRUE(base::WriteFile( + path.Append(FPL("ofac-sanctioned-digital-currency-addresses.json")), + R"({"addresses": ["0xb9ef770b6a5e12e45983c5d80545258aa38f3b78"]})")); + + auto* registry = BlockchainRegistry::GetInstance(); + auto run_loop = std::make_unique(); + registry->ParseLists(path, + base::BindLambdaForTesting([&]() { run_loop->Quit(); })); + run_loop->Run(); + + // coingecko-ids.json + EXPECT_EQ( + registry->GetCoingeckoId(mojom::kOptimismMainnetChainId, + "0x7f5c764cbc14f9669b88837ca1490cca17c31607"), + "usd-coin"); + + // contract-map.json + EXPECT_EQ( + registry + ->GetTokenByAddress(mojom::kMainnetChainId, mojom::CoinType::ETH, + "0x0D8775F648430679A709E98d2b0Cb6250d2887EF") + ->symbol, + "BAT"); + + // evm-contract-map.json + EXPECT_EQ(registry + ->GetTokenByAddress( + mojom::kPolygonMainnetChainId, mojom::CoinType::ETH, + "0xc2132D05D31c914a87C6611C10748AEb04B58e8F") + ->symbol, + "USDT"); + + // solana-contract-map.json + EXPECT_EQ( + registry + ->GetTokenByAddress(mojom::kSolanaMainnet, mojom::CoinType::SOL, + "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v") + ->symbol, + "USDC"); + + // chainlist.json + EXPECT_THAT(GetChainIds(registry->GetPrepopulatedNetworks()), + ElementsAreArray({"0x1", "0x89"})); + + // dapp-lists.json + run_loop = std::make_unique(); + + registry->GetTopDapps( + mojom::kMainnetChainId, mojom::CoinType::ETH, + base::BindLambdaForTesting([&](std::vector dapp_list) { + ASSERT_EQ(dapp_list.size(), 1UL); + EXPECT_EQ(dapp_list[0]->name, "Uniswap V3"); + run_loop->Quit(); + })); + run_loop->Run(); + + // ramp-tokens.json + run_loop = std::make_unique(); + registry->GetBuyTokens( + mojom::OnRampProvider::kRamp, mojom::kMainnetChainId, + base::BindLambdaForTesting( + [&](std::vector token_list) { + EXPECT_NE(token_list.size(), 0UL); + EXPECT_EQ(token_list[0]->name, "Ethereum"); + run_loop->Quit(); + })); + run_loop->Run(); + + run_loop = std::make_unique(); + registry->GetSellTokens( + mojom::OffRampProvider::kRamp, mojom::kMainnetChainId, + base::BindLambdaForTesting( + [&](std::vector token_list) { + EXPECT_NE(token_list.size(), 0UL); + EXPECT_EQ(token_list[0]->name, "Ethereum"); + run_loop->Quit(); + })); + run_loop->Run(); + + // on-ramp-currency-lists.json + run_loop = std::make_unique(); + registry->GetOnRampCurrencies(base::BindLambdaForTesting( + [&](std::vector currency_list) { + EXPECT_NE(currency_list.size(), 0UL); + EXPECT_EQ(currency_list[0]->currency_code, "ARS"); + run_loop->Quit(); + })); + run_loop->Run(); + + // ofac-sanctioned-digital-currency-addresses.json + EXPECT_TRUE( + registry->IsOfacAddress("0xb9ef770b6a5e12e45983c5d80545258aa38f3b78")); +} + } // namespace brave_wallet diff --git a/components/brave_wallet/browser/brave_wallet_constants.h b/components/brave_wallet/browser/brave_wallet_constants.h index d5752f0703b9..05b0087c9f0b 100644 --- a/components/brave_wallet/browser/brave_wallet_constants.h +++ b/components/brave_wallet/browser/brave_wallet_constants.h @@ -1037,6 +1037,7 @@ constexpr webui::LocalizedString kLocalizedStrings[] = { {"braveWalletPasswordIsMediumStrength", IDS_BRAVE_WALLET_PASSWORD_IS_MEDIUM_STRENGTH}, {"braveWalletPasswordIsWeak", IDS_BRAVE_WALLET_PASSWORD_IS_WEAK}, + {"braveWalletCreatingWallet", IDS_BRAVE_WALLET_CREATING_WALLET}, {"braveWalletOnboardingRecoveryPhraseBackupIntroTitle", IDS_BRAVE_WALLET_ONBOARDING_RECOVERY_PHRASE_BACKUP_INTRO_TITLE}, {"braveWalletOnboardingRecoveryPhraseBackupIntroDescription", diff --git a/components/brave_wallet/browser/brave_wallet_service.cc b/components/brave_wallet/browser/brave_wallet_service.cc index daa2a42ae7a8..8f34bbc8beb8 100644 --- a/components/brave_wallet/browser/brave_wallet_service.cc +++ b/components/brave_wallet/browser/brave_wallet_service.cc @@ -24,6 +24,7 @@ #include "brave/components/brave_wallet/browser/keyring_service.h" #include "brave/components/brave_wallet/browser/pref_names.h" #include "brave/components/brave_wallet/browser/tx_service.h" +#include "brave/components/brave_wallet/browser/wallet_data_files_installer.h" #include "brave/components/brave_wallet/common/brave_wallet.mojom.h" #include "brave/components/brave_wallet/common/brave_wallet_response_helpers.h" #include "brave/components/brave_wallet/common/brave_wallet_types.h" @@ -1682,7 +1683,17 @@ void BraveWalletService::OnGetImportInfo( mojom::kDefaultKeyringId, info.number_of_accounts - 1); } - std::move(callback).Run(is_valid_mnemonic, absl::nullopt); + + // Only register the component if the import is successful. + CHECK(is_valid_mnemonic); + WalletDataFilesInstaller::GetInstance() + .MaybeRegisterWalletDataFilesComponentOnDemand(base::BindOnce( + [](base::OnceCallback&)> + callback) { + std::move(callback).Run(true /* is_valid_mnemonic */, + absl::nullopt); + }, + std::move(callback))); } void BraveWalletService::AddSignMessageRequest( diff --git a/components/brave_wallet/browser/brave_wallet_utils.cc b/components/brave_wallet/browser/brave_wallet_utils.cc index ef891fb4c8a6..ea6be15e0000 100644 --- a/components/brave_wallet/browser/brave_wallet_utils.cc +++ b/components/brave_wallet/browser/brave_wallet_utils.cc @@ -1217,6 +1217,10 @@ void UpdateLastUnlockPref(PrefService* prefs) { prefs->SetTime(kBraveWalletLastUnlockTime, base::Time::Now()); } +bool HasCreatedWallets(PrefService* prefs) { + return !prefs->GetTime(kBraveWalletLastUnlockTime).is_null(); +} + base::Value::Dict TransactionReceiptToValue( const TransactionReceipt& tx_receipt) { base::Value::Dict dict; diff --git a/components/brave_wallet/browser/brave_wallet_utils.h b/components/brave_wallet/browser/brave_wallet_utils.h index 59d8a1ef5fc6..805db1db230a 100644 --- a/components/brave_wallet/browser/brave_wallet_utils.h +++ b/components/brave_wallet/browser/brave_wallet_utils.h @@ -53,6 +53,10 @@ bool DecodeString(size_t offset, const std::string& input, std::string* output); // it unlocks. void UpdateLastUnlockPref(PrefService* prefs); +// Use kBraveWalletLastUnlockTime pref to determine if any wallets has been +// created before, regardless of still existed or not. +bool HasCreatedWallets(PrefService* prefs); + base::Value::Dict TransactionReceiptToValue( const TransactionReceipt& tx_receipt); absl::optional ValueToTransactionReceipt( diff --git a/components/brave_wallet/browser/keyring_service.cc b/components/brave_wallet/browser/keyring_service.cc index d1be0a5006ab..2dc9c1fc4138 100644 --- a/components/brave_wallet/browser/keyring_service.cc +++ b/components/brave_wallet/browser/keyring_service.cc @@ -32,6 +32,7 @@ #include "brave/components/brave_wallet/browser/json_rpc_service.h" #include "brave/components/brave_wallet/browser/pref_names.h" #include "brave/components/brave_wallet/browser/solana_keyring.h" +#include "brave/components/brave_wallet/browser/wallet_data_files_installer.h" #include "brave/components/brave_wallet/browser/zcash/zcash_keyring.h" #include "brave/components/brave_wallet/common/bitcoin_utils.h" #include "brave/components/brave_wallet/common/brave_wallet.mojom.h" @@ -1101,15 +1102,32 @@ void KeyringService::CreateWallet(const std::string& mnemonic, } ResetAllAccountInfosCache(); - std::move(callback).Run(mnemonic); + + WalletDataFilesInstaller::GetInstance() + .MaybeRegisterWalletDataFilesComponentOnDemand(base::BindOnce( + [](const std::string& mnemonic, CreateWalletCallback callback) { + std::move(callback).Run(mnemonic); + }, + mnemonic, std::move(callback))); } void KeyringService::RestoreWallet(const std::string& mnemonic, const std::string& password, bool is_legacy_brave_wallet, RestoreWalletCallback callback) { - std::move(callback).Run( - RestoreWalletSync(mnemonic, password, is_legacy_brave_wallet)); + bool is_valid_mnemonic = + RestoreWalletSync(mnemonic, password, is_legacy_brave_wallet); + if (!is_valid_mnemonic) { + std::move(callback).Run(false); + return; + } + + // Only register the component if restore is successful. + CHECK(is_valid_mnemonic); + WalletDataFilesInstaller::GetInstance() + .MaybeRegisterWalletDataFilesComponentOnDemand(base::BindOnce( + [](RestoreWalletCallback callback) { std::move(callback).Run(true); }, + std::move(callback))); } bool KeyringService::RestoreWalletSync(const std::string& mnemonic, diff --git a/components/brave_wallet/browser/test/BUILD.gn b/components/brave_wallet/browser/test/BUILD.gn index fee2326c6d70..febeb7106a34 100644 --- a/components/brave_wallet/browser/test/BUILD.gn +++ b/components/brave_wallet/browser/test/BUILD.gn @@ -92,6 +92,7 @@ source_set("brave_wallet_unit_tests") { "//brave/components/brave_wallet/browser/tx_storage_delegate_impl_unittest.cc", "//brave/components/brave_wallet/browser/unstoppable_domains_dns_resolve_unittest.cc", "//brave/components/brave_wallet/browser/unstoppable_domains_multichain_calls_unittest.cc", + "//brave/components/brave_wallet/browser/wallet_data_files_installer_unittest.cc", "//brave/components/brave_wallet/browser/zcash/zcash_keyring_unittest.cc", ] @@ -106,6 +107,7 @@ source_set("brave_wallet_unit_tests") { ":test_data", ":test_support", "//base/test:test_support", + "//brave/components/brave_component_updater/browser", "//brave/components/brave_wallet/browser", "//brave/components/brave_wallet/browser:constants", "//brave/components/brave_wallet/browser:generated_bitcoin_rpc_responses", @@ -128,6 +130,7 @@ source_set("brave_wallet_unit_tests") { "//brave/components/ipfs", "//brave/components/json/rs:rust_lib", "//brave/components/resources:strings_grit", + "//components/component_updater:test_support", "//components/permissions", "//components/prefs", "//components/prefs:test_support", diff --git a/components/brave_wallet/browser/wallet_data_files_installer.cc b/components/brave_wallet/browser/wallet_data_files_installer.cc index e40d36916206..e7932862e438 100644 --- a/components/brave_wallet/browser/wallet_data_files_installer.cc +++ b/components/brave_wallet/browser/wallet_data_files_installer.cc @@ -10,29 +10,20 @@ #include #include -#include "base/files/file_path.h" -#include "base/files/file_util.h" #include "base/functional/bind.h" #include "base/logging.h" -#include "base/task/thread_pool.h" #include "base/values.h" #include "brave/components/brave_component_updater/browser/brave_on_demand_updater.h" -#include "brave/components/brave_wallet/browser/blockchain_list_parser.h" #include "brave/components/brave_wallet/browser/blockchain_registry.h" #include "brave/components/brave_wallet/browser/brave_wallet_constants.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/json/rs/src/lib.rs.h" #include "components/component_updater/component_installer.h" -#include "components/component_updater/component_updater_service.h" +#include "components/prefs/pref_service.h" #include "crypto/sha2.h" -#include "services/data_decoder/public/cpp/json_sanitizer.h" #include "third_party/abseil-cpp/absl/types/optional.h" -#if BUILDFLAG(IS_ANDROID) -#include "brave/components/brave_wallet/browser/wallet_data_files_installer_android_util.h" -#endif - namespace brave_wallet { namespace { @@ -55,276 +46,6 @@ static_assert(std::size(kWalletDataFilesSha2Hash) == crypto::kSHA256Length, absl::optional last_installed_wallet_version; -void OnSanitizedTokenList(mojom::CoinType coin, - data_decoder::JsonSanitizer::Result result) { - TokenListMap lists; - if (!result.has_value()) { - VLOG(1) << "TokenList JSON validation error:" << result.error(); - return; - } - - if (!ParseTokenList(*result, &lists, coin)) { - VLOG(1) << "Can't parse token list."; - return; - } - - for (auto& list_pair : lists) { - BlockchainRegistry::GetInstance()->UpdateTokenList( - list_pair.first, std::move(list_pair.second)); - } -} - -void OnSanitizedChainList(data_decoder::JsonSanitizer::Result result) { - ChainList chains; - if (!result.has_value()) { - VLOG(1) << "TokenList JSON validation error:" << result.error(); - return; - } - - if (!ParseChainList(*result, &chains)) { - VLOG(1) << "Can't parse chain list."; - return; - } - - BlockchainRegistry::GetInstance()->UpdateChainList(std::move(chains)); -} - -void OnSanitizedDappLists(data_decoder::JsonSanitizer::Result result) { - if (!result.has_value()) { - VLOG(1) << "DappLists JSON validation error:" << result.error(); - return; - } - - auto converted_json = - std::string(json::convert_all_numbers_to_string(*result, "")); - if (converted_json.empty()) { - return; - } - - absl::optional lists = ParseDappLists(converted_json); - if (!lists) { - VLOG(1) << "Can't parse dapp lists."; - return; - } - - BlockchainRegistry::GetInstance()->UpdateDappList(std::move(*lists)); -} - -void OnSanitizedRampTokenLists(data_decoder::JsonSanitizer::Result result) { - if (!result.has_value()) { - VLOG(1) << "Ramp lists JSON validation error:" << result.error(); - return; - } - - auto parsedRampTokensListMaps = ParseRampTokenListMaps(*result); - if (!parsedRampTokensListMaps) { - VLOG(1) << "Can't parse on/off ramp token lists."; - return; - } - - if (parsedRampTokensListMaps->first.empty()) { - VLOG(1) << "On ramp supported token lists is empty."; - } else { - BlockchainRegistry::GetInstance()->UpdateOnRampTokenLists( - std::move(parsedRampTokensListMaps->first)); - } - - if (parsedRampTokensListMaps->second.empty()) { - VLOG(1) << "Off ramp supported sell token lists is empty."; - } else { - BlockchainRegistry::GetInstance()->UpdateOffRampTokenLists( - std::move(parsedRampTokensListMaps->second)); - } -} - -void OnSanitizedOnRampCurrenciesLists( - data_decoder::JsonSanitizer::Result result) { - if (!result.has_value()) { - VLOG(1) << "OnRamp lists JSON validation error:" << result.error(); - return; - } - - absl::optional> lists = - ParseOnRampCurrencyLists(*result); - if (!lists) { - VLOG(1) << "Can't parse on ramp supported sell token lists."; - return; - } - - BlockchainRegistry::GetInstance()->UpdateOnRampCurrenciesLists( - std::move(*lists)); -} - -void OnSanitizedCoingeckoIdsMap(data_decoder::JsonSanitizer::Result result) { - if (!result.has_value()) { - VLOG(1) << "CoingeckoIdsMap JSON validation error:" << result.error(); - return; - } - - absl::optional coingecko_ids_map = - ParseCoingeckoIdsMap(*result); - if (!coingecko_ids_map) { - VLOG(1) << "Can't parse coingecko-ids.json"; - return; - } - - BlockchainRegistry::GetInstance()->UpdateCoingeckoIdsMap( - std::move(*coingecko_ids_map)); -} - -void OnSanitizedOfacAddressesList(data_decoder::JsonSanitizer::Result result) { - if (!result.has_value()) { - VLOG(1) << "OFAC addresses list JSON validation error:" << result.error(); - return; - } - - absl::optional> list = - ParseOfacAddressesList(*result); - if (!list) { - VLOG(1) << "Can't parse ofac addresses list."; - return; - } - - BlockchainRegistry::GetInstance()->UpdateOfacAddressesList(std::move(*list)); -} - -void HandleJsonFileParsing( - base::FilePath absolute_install_dir, - const std::string& filename, - base::OnceCallback)> - callback) { - const base::FilePath json_path = absolute_install_dir.AppendASCII(filename); - std::string json_content; - if (!base::ReadFileToString(json_path, &json_content)) { - LOG(ERROR) << "Can't read file: " << filename; - return; - } - - data_decoder::JsonSanitizer::Sanitize(std::move(json_content), - std::move(callback)); -} - -void HandleParseCoingeckoIdsMap(base::FilePath absolute_install_dir, - const std::string& filename) { - HandleJsonFileParsing(absolute_install_dir, filename, - base::BindOnce(&OnSanitizedCoingeckoIdsMap)); -} - -void HandleParseTokenList(base::FilePath absolute_install_dir, - const std::string& filename, - mojom::CoinType coin_type) { - HandleJsonFileParsing(absolute_install_dir, filename, - base::BindOnce(&OnSanitizedTokenList, coin_type)); -} - -void HandleParseChainList(base::FilePath absolute_install_dir, - const std::string& filename) { - HandleJsonFileParsing(absolute_install_dir, filename, - base::BindOnce(&OnSanitizedChainList)); -} - -void HandleParseDappList(base::FilePath absolute_install_dir, - const std::string& filename) { - HandleJsonFileParsing(absolute_install_dir, filename, - base::BindOnce(&OnSanitizedDappLists)); -} - -void HandleParseRampTokenLists(base::FilePath absolute_install_dir, - const std::string& filename) { - HandleJsonFileParsing(absolute_install_dir, filename, - base::BindOnce(&OnSanitizedRampTokenLists)); -} - -void HandleParseOnRampCurrenciesLists(base::FilePath absolute_install_dir, - const std::string& filename) { - HandleJsonFileParsing(absolute_install_dir, filename, - base::BindOnce(&OnSanitizedOnRampCurrenciesLists)); -} - -void HandleParseOfacAddressesList(base::FilePath absolute_install_dir, - const std::string& filename) { - HandleJsonFileParsing(absolute_install_dir, filename, - base::BindOnce(&OnSanitizedOfacAddressesList)); -} - -bool ResolveAbsolutePath(const base::FilePath& input_path, - base::FilePath& output_path) { - // On some platforms (e.g. Mac) we use symlinks for paths. Convert paths to - // absolute paths to avoid unexpected failure. base::MakeAbsoluteFilePath() - // requires IO so it can only be done in this function. - output_path = base::MakeAbsoluteFilePath(input_path); - - if (output_path.empty()) { - LOG(ERROR) << "Failed to get absolute install path."; - return false; - } - - return true; -} - -void ParseCoingeckoIdsMapAndUpdateRegistry(const base::FilePath& install_dir) { - base::FilePath absolute_install_dir; - if (!ResolveAbsolutePath(install_dir, absolute_install_dir)) { - return; - } - - HandleParseCoingeckoIdsMap(absolute_install_dir, "coingecko-ids.json"); -} - -void ParseTokenListAndUpdateRegistry(const base::FilePath& install_dir) { - base::FilePath absolute_install_dir; - if (!ResolveAbsolutePath(install_dir, absolute_install_dir)) { - return; - } - - HandleParseTokenList(absolute_install_dir, "contract-map.json", - mojom::CoinType::ETH); - HandleParseTokenList(absolute_install_dir, "evm-contract-map.json", - mojom::CoinType::ETH); - HandleParseTokenList(absolute_install_dir, "solana-contract-map.json", - mojom::CoinType::SOL); -} - -void ParseChainListAndUpdateRegistry(const base::FilePath& install_dir) { - base::FilePath absolute_install_dir; - if (!ResolveAbsolutePath(install_dir, absolute_install_dir)) { - return; - } - - HandleParseChainList(absolute_install_dir, "chainlist.json"); -} - -void ParseDappListsAndUpdateRegistry(const base::FilePath& install_dir) { - base::FilePath absolute_install_dir; - if (!ResolveAbsolutePath(install_dir, absolute_install_dir)) { - return; - } - - HandleParseDappList(absolute_install_dir, "dapp-lists.json"); -} - -void ParseOnRampListsAndUpdateRegistry(const base::FilePath& install_dir) { - base::FilePath absolute_install_dir; - if (!ResolveAbsolutePath(install_dir, absolute_install_dir)) { - return; - } - - HandleParseRampTokenLists(absolute_install_dir, "ramp-tokens.json"); - HandleParseOnRampCurrenciesLists(absolute_install_dir, - "on-ramp-currency-lists.json"); -} - -void ParseOfacAddressesListsAndUpdateRegistry( - const base::FilePath& install_dir) { - base::FilePath absolute_install_dir; - if (!ResolveAbsolutePath(install_dir, absolute_install_dir)) { - return; - } - - HandleParseOfacAddressesList( - absolute_install_dir, "ofac-sanctioned-digital-currency-addresses.json"); -} - } // namespace class WalletDataFilesInstallerPolicy @@ -334,7 +55,6 @@ class WalletDataFilesInstallerPolicy ~WalletDataFilesInstallerPolicy() override = default; private: - scoped_refptr sequenced_task_runner_; // The following methods override ComponentInstallerPolicy. bool SupportsGroupPolicyEnabledComponentUpdates() const override; bool RequiresNetworkEncryption() const override; @@ -358,11 +78,7 @@ class WalletDataFilesInstallerPolicy const WalletDataFilesInstallerPolicy&) = delete; }; -WalletDataFilesInstallerPolicy::WalletDataFilesInstallerPolicy() { - sequenced_task_runner_ = base::ThreadPool::CreateSequencedTaskRunner( - {base::MayBlock(), base::TaskPriority::USER_VISIBLE, - base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}); -} +WalletDataFilesInstallerPolicy::WalletDataFilesInstallerPolicy() = default; bool WalletDataFilesInstallerPolicy:: SupportsGroupPolicyEnabledComponentUpdates() const { @@ -387,25 +103,7 @@ void WalletDataFilesInstallerPolicy::ComponentReady( const base::FilePath& path, base::Value::Dict manifest) { last_installed_wallet_version = version; - - sequenced_task_runner_->PostTask( - FROM_HERE, base::BindOnce(&ParseCoingeckoIdsMapAndUpdateRegistry, path)); - - sequenced_task_runner_->PostTask( - FROM_HERE, base::BindOnce(&ParseTokenListAndUpdateRegistry, path)); - - sequenced_task_runner_->PostTask( - FROM_HERE, base::BindOnce(&ParseChainListAndUpdateRegistry, path)); - - sequenced_task_runner_->PostTask( - FROM_HERE, base::BindOnce(&ParseDappListsAndUpdateRegistry, path)); - - sequenced_task_runner_->PostTask( - FROM_HERE, base::BindOnce(&ParseOnRampListsAndUpdateRegistry, path)); - - sequenced_task_runner_->PostTask( - FROM_HERE, - base::BindOnce(&ParseOfacAddressesListsAndUpdateRegistry, path)); + WalletDataFilesInstaller::GetInstance().OnComponentReady(path); } bool WalletDataFilesInstallerPolicy::VerifyInstallation( @@ -434,27 +132,35 @@ WalletDataFilesInstallerPolicy::GetInstallerAttributes() const { return update_client::InstallerAttributes(); } -void RegisterWalletDataFilesComponent( - component_updater::ComponentUpdateService* cus) { -#if BUILDFLAG(IS_ANDROID) - bool should_register_component = - IsNativeWalletEnabled() && IsBraveWalletConfiguredOnAndroid(); -#else - bool should_register_component = IsNativeWalletEnabled(); -#endif - if (should_register_component) { - auto installer = - base::MakeRefCounted( - std::make_unique()); - installer->Register( - cus, base::BindOnce([]() { - brave_component_updater::BraveOnDemandUpdater::GetInstance() - ->OnDemandUpdate(kComponentId); - })); +absl::optional GetLastInstalledWalletVersion() { + return last_installed_wallet_version; +} + +void SetLastInstalledWalletVersionForTest(const base::Version& version) { + last_installed_wallet_version = version; +} + +WalletDataFilesInstaller::WalletDataFilesInstaller() = default; + +WalletDataFilesInstaller::~WalletDataFilesInstaller() = default; + +// static +WalletDataFilesInstaller& WalletDataFilesInstaller::GetInstance() { + static base::NoDestructor instance; + return *instance; +} + +void WalletDataFilesInstaller::SetDelegate( + std::unique_ptr delegate) { + CHECK(!delegate_); + + delegate_ = std::move(delegate); + if (auto* cus = delegate_->GetComponentUpdater()) { + component_updater_observation_.Observe(cus); } } -void RegisterWalletDataFilesComponentOnDemand( +void WalletDataFilesInstaller::RegisterWalletDataFilesComponentInternal( component_updater::ComponentUpdateService* cus) { auto installer = base::MakeRefCounted( std::make_unique()); @@ -466,16 +172,62 @@ void RegisterWalletDataFilesComponentOnDemand( })); } -absl::optional GetLastInstalledWalletVersion() { - return last_installed_wallet_version; +void WalletDataFilesInstaller::MaybeRegisterWalletDataFilesComponent( + component_updater::ComponentUpdateService* cus, + PrefService* local_state) { + if (!IsNativeWalletEnabled() || !HasCreatedWallets(local_state)) { + return; + } + + registered_ = true; + RegisterWalletDataFilesComponentInternal(cus); } -void SetLastInstalledWalletVersionForTest(const base::Version& version) { - last_installed_wallet_version = version; +void WalletDataFilesInstaller::MaybeRegisterWalletDataFilesComponentOnDemand( + InstallCallback install_callback) { + if (registered_ || !delegate_) { // delegate_ can be nullptr in tests. + std::move(install_callback).Run(); + return; + } + + auto* cus = delegate_->GetComponentUpdater(); + if (!cus) { + std::move(install_callback).Run(); + return; + } + + registered_ = true; + CHECK(!install_callback_); + install_callback_ = std::move(install_callback); + RegisterWalletDataFilesComponentInternal(cus); +} + +void WalletDataFilesInstaller::OnComponentReady(const base::FilePath& path) { + auto callback = + install_callback_ ? std::move(install_callback_) : base::DoNothing(); + BlockchainRegistry::GetInstance()->ParseLists(path, std::move(callback)); +} + +void WalletDataFilesInstaller::OnEvent( + update_client::UpdateClient::Observer::Events event, + const std::string& id) { + if (id != kComponentId) { + return; + } + + if (event == + update_client::UpdateClient::Observer::Events::COMPONENT_UPDATE_ERROR) { + if (install_callback_) { + std::move(install_callback_).Run(); + } + } } -std::string GetWalletDataFilesComponentId() { - return kComponentId; +void WalletDataFilesInstaller::ResetForTesting() { + component_updater_observation_.Reset(); + delegate_.reset(); + registered_ = false; + install_callback_.Reset(); } } // namespace brave_wallet diff --git a/components/brave_wallet/browser/wallet_data_files_installer.h b/components/brave_wallet/browser/wallet_data_files_installer.h index 1e6d3215ecc9..c26c0e1ca79f 100644 --- a/components/brave_wallet/browser/wallet_data_files_installer.h +++ b/components/brave_wallet/browser/wallet_data_files_installer.h @@ -6,25 +6,72 @@ #ifndef BRAVE_COMPONENTS_BRAVE_WALLET_BROWSER_WALLET_DATA_FILES_INSTALLER_H_ #define BRAVE_COMPONENTS_BRAVE_WALLET_BROWSER_WALLET_DATA_FILES_INSTALLER_H_ +#include #include -#include "base/functional/callback_forward.h" +#include "base/files/file_path.h" +#include "base/functional/callback.h" +#include "base/no_destructor.h" +#include "base/scoped_observation.h" #include "base/version.h" +#include "brave/components/brave_wallet/browser/wallet_data_files_installer_delegate.h" +#include "components/component_updater/component_updater_service.h" +#include "components/update_client/update_client.h" #include "third_party/abseil-cpp/absl/types/optional.h" -namespace component_updater { -class ComponentUpdateService; +namespace base { +class FilePath; } +class PrefService; + namespace brave_wallet { -void RegisterWalletDataFilesComponent( - component_updater::ComponentUpdateService* cus); -void RegisterWalletDataFilesComponentOnDemand( - component_updater::ComponentUpdateService* cus); absl::optional GetLastInstalledWalletVersion(); void SetLastInstalledWalletVersionForTest(const base::Version& version); -std::string GetWalletDataFilesComponentId(); + +class WalletDataFilesInstaller + : public component_updater::ComponentUpdateService::Observer { + public: + WalletDataFilesInstaller(const WalletDataFilesInstaller&) = delete; + WalletDataFilesInstaller& operator=(const WalletDataFilesInstaller&) = delete; + + static WalletDataFilesInstaller& GetInstance(); + + void SetDelegate(std::unique_ptr delegate); + + void MaybeRegisterWalletDataFilesComponent( + component_updater::ComponentUpdateService* cus, + PrefService* local_state); + + using InstallCallback = base::OnceClosure; + void MaybeRegisterWalletDataFilesComponentOnDemand( + InstallCallback install_callback); + + void OnComponentReady(const base::FilePath& path); + + // component_updater::ComponentUpdateService::Observer: + void OnEvent(update_client::UpdateClient::Observer::Events event, + const std::string& id) override; + + void ResetForTesting(); + + private: + friend base::NoDestructor; + WalletDataFilesInstaller(); + ~WalletDataFilesInstaller() override; + + void RegisterWalletDataFilesComponentInternal( + component_updater::ComponentUpdateService* cus); + + base::ScopedObservation + component_updater_observation_{this}; + + std::unique_ptr delegate_; + bool registered_ = false; + InstallCallback install_callback_; +}; } // namespace brave_wallet diff --git a/components/brave_wallet/browser/wallet_data_files_installer_android_util.cc b/components/brave_wallet/browser/wallet_data_files_installer_android_util.cc deleted file mode 100644 index f62b56c9b455..000000000000 --- a/components/brave_wallet/browser/wallet_data_files_installer_android_util.cc +++ /dev/null @@ -1,21 +0,0 @@ -/* Copyright (c) 2023 The Brave Authors. All rights reserved. - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at https://mozilla.org/MPL/2.0/. */ - -#include "brave/components/brave_wallet/browser/wallet_data_files_installer_android_util.h" - -#include - -#include "base/android/jni_android.h" -#include "brave/build/android/jni_headers/WalletDataFilesInstallerUtil_jni.h" - -namespace brave_wallet { - -bool IsBraveWalletConfiguredOnAndroid() { - JNIEnv* env = base::android::AttachCurrentThread(); - return Java_WalletDataFilesInstallerUtil_isBraveWalletConfiguredOnAndroid( - env); -} - -} // namespace brave_wallet diff --git a/components/brave_wallet/browser/wallet_data_files_installer_android_util.h b/components/brave_wallet/browser/wallet_data_files_installer_delegate.h similarity index 58% rename from components/brave_wallet/browser/wallet_data_files_installer_android_util.h rename to components/brave_wallet/browser/wallet_data_files_installer_delegate.h index 9c6197205887..eeb9077d13ba 100644 --- a/components/brave_wallet/browser/wallet_data_files_installer_android_util.h +++ b/components/brave_wallet/browser/wallet_data_files_installer_delegate.h @@ -3,17 +3,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at https://mozilla.org/MPL/2.0/. */ -#ifndef BRAVE_COMPONENTS_BRAVE_WALLET_BROWSER_WALLET_DATA_FILES_INSTALLER_ANDROID_UTIL_H_ -#define BRAVE_COMPONENTS_BRAVE_WALLET_BROWSER_WALLET_DATA_FILES_INSTALLER_ANDROID_UTIL_H_ +#ifndef BRAVE_COMPONENTS_BRAVE_WALLET_BROWSER_WALLET_DATA_FILES_INSTALLER_DELEGATE_H_ +#define BRAVE_COMPONENTS_BRAVE_WALLET_BROWSER_WALLET_DATA_FILES_INSTALLER_DELEGATE_H_ -#include "build/build_config.h" - -static_assert BUILDFLAG(IS_ANDROID); +#include "components/component_updater/component_updater_service.h" namespace brave_wallet { -bool IsBraveWalletConfiguredOnAndroid(); +class WalletDataFilesInstallerDelegate { + public: + WalletDataFilesInstallerDelegate() = default; + virtual ~WalletDataFilesInstallerDelegate() = default; + virtual component_updater::ComponentUpdateService* GetComponentUpdater() = 0; +}; } // namespace brave_wallet -#endif // BRAVE_COMPONENTS_BRAVE_WALLET_BROWSER_WALLET_DATA_FILES_INSTALLER_ANDROID_UTIL_H_ +#endif // BRAVE_COMPONENTS_BRAVE_WALLET_BROWSER_WALLET_DATA_FILES_INSTALLER_DELEGATE_H_ diff --git a/components/brave_wallet/browser/wallet_data_files_installer_unittest.cc b/components/brave_wallet/browser/wallet_data_files_installer_unittest.cc new file mode 100644 index 000000000000..6f5245a9446b --- /dev/null +++ b/components/brave_wallet/browser/wallet_data_files_installer_unittest.cc @@ -0,0 +1,357 @@ +/* Copyright (c) 2023 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include + +#include "base/files/file_path.h" +#include "base/files/scoped_temp_dir.h" +#include "base/run_loop.h" +#include "base/test/bind.h" +#include "base/test/scoped_feature_list.h" +#include "base/test/task_environment.h" +#include "base/time/time.h" +#include "brave/components/brave_component_updater/browser/brave_on_demand_updater.h" +#include "brave/components/brave_wallet/browser/blockchain_registry.h" +#include "brave/components/brave_wallet/browser/brave_wallet_prefs.h" +#include "brave/components/brave_wallet/browser/brave_wallet_service.h" +#include "brave/components/brave_wallet/browser/brave_wallet_service_delegate.h" +#include "brave/components/brave_wallet/browser/json_rpc_service.h" +#include "brave/components/brave_wallet/browser/keyring_service.h" +#include "brave/components/brave_wallet/browser/pref_names.h" +#include "brave/components/brave_wallet/browser/test_utils.h" +#include "brave/components/brave_wallet/browser/wallet_data_files_installer.h" +#include "brave/components/brave_wallet/common/brave_wallet.mojom.h" +#include "brave/components/brave_wallet/common/features.h" +#include "components/component_updater/mock_component_updater_service.h" +#include "components/sync_preferences/testing_pref_service_syncable.h" +#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h" +#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" +#include "services/network/test/test_url_loader_factory.h" +#include "testing/gtest/include/gtest/gtest.h" + +#define FPL(x) FILE_PATH_LITERAL(x) + +namespace brave_wallet { + +constexpr char kComponentId[] = "bbckkcdiepaecefgfnibemejliemjnio"; + +class MockWalletDataFilesInstallerDelegateImpl + : public WalletDataFilesInstallerDelegate { + public: + MockWalletDataFilesInstallerDelegateImpl() = default; + + explicit MockWalletDataFilesInstallerDelegateImpl( + component_updater::ComponentUpdateService* cus) + : cus_(cus) {} + + ~MockWalletDataFilesInstallerDelegateImpl() override = default; + + component_updater::ComponentUpdateService* GetComponentUpdater() override { + return cus_.get(); + } + + private: + raw_ptr cus_; +}; + +class MockBraveWalletServiceDelegateImpl : public BraveWalletServiceDelegate { + public: + MockBraveWalletServiceDelegateImpl() = default; + ~MockBraveWalletServiceDelegateImpl() override = default; + + using GetImportInfoCallback = + base::OnceCallback; + void GetImportInfoFromExternalWallet( + mojom::ExternalWalletType type, + const std::string& password, + GetImportInfoCallback callback) override { + std::move(callback).Run(true, ImportInfo({kMnemonicDivideCruise, false, 1}), + ImportError::kNone); + } +}; + +class WalletDataFilesInstallerUnitTest : public testing::Test { + public: + WalletDataFilesInstallerUnitTest() + : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME), + shared_url_loader_factory_( + base::MakeRefCounted( + &url_loader_factory_)) {} + + ~WalletDataFilesInstallerUnitTest() override = default; + + void SetUp() override { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature( + brave_wallet::features::kNativeBraveWalletFeature); + + RegisterProfilePrefs(prefs_.registry()); + RegisterLocalStatePrefs(local_state_.registry()); + RegisterProfilePrefsForMigration(prefs_.registry()); + RegisterLocalStatePrefsForMigration(local_state_.registry()); + + keyring_service_ = + std::make_unique(nullptr, &prefs_, &local_state_); + json_rpc_service_ = std::make_unique( + shared_url_loader_factory_, &prefs_); + brave_wallet_service_ = std::make_unique( + shared_url_loader_factory_, + std::make_unique(), + keyring_service_.get(), json_rpc_service_.get(), nullptr, nullptr, + nullptr, &prefs_, &local_state_); + + cus_ = std::make_unique(); + installer().SetDelegate( + std::make_unique(cus_.get())); + + url_loader_factory_.SetInterceptor(base::BindLambdaForTesting( + [&](const network::ResourceRequest& request) { + url_loader_factory_.ClearResponses(); + url_loader_factory_.AddResponse(request.url.spec(), "", + net::HTTP_REQUEST_TIMEOUT); + })); + + ASSERT_TRUE(install_dir_.CreateUniqueTempDir()); + } + + void TearDown() override { + installer().ResetForTesting(); + registry()->ResetForTesting(); + } + + void WriteCoingeckoIdsMapToFile() { + const std::string coingecko_ids_map_json = R"({ + "0xa": { + "0x7f5c764cbc14f9669b88837ca1490cca17c31607": "usd-coin" + } + })"; + ASSERT_TRUE(base::WriteFile(install_dir().Append(FPL("coingecko-ids.json")), + coingecko_ids_map_json)); + } + + void CreateWallet() { + base::RunLoop run_loop; + keyring_service_->CreateWallet( + kMnemonicDivideCruise, kTestWalletPassword, + base::BindLambdaForTesting([&run_loop](const std::string& mnemonic) { + ASSERT_FALSE(mnemonic.empty()); + run_loop.Quit(); + })); + run_loop.Run(); + } + + void RestoreWallet() { + base::RunLoop run_loop; + keyring_service_->RestoreWallet( + kMnemonicDivideCruise, kTestWalletPassword, false, + base::BindLambdaForTesting([&](bool success) { + ASSERT_TRUE(success); + run_loop.Quit(); + })); + run_loop.Run(); + } + + void ImportFromExternalWallet() { + base::RunLoop run_loop; + brave_wallet_service_->ImportFromExternalWallet( + mojom::ExternalWalletType::MetaMask, kTestWalletPassword, + kTestWalletPassword, + base::BindLambdaForTesting( + [&run_loop](bool success, const absl::optional& err) { + ASSERT_TRUE(success); + ASSERT_FALSE(err); + run_loop.Quit(); + })); + run_loop.Run(); + } + + PrefService* local_state() { return &local_state_; } + base::FilePath install_dir() { return install_dir_.GetPath(); } + component_updater::MockComponentUpdateService* updater() { + return cus_.get(); + } + + WalletDataFilesInstaller& installer() { + return WalletDataFilesInstaller::GetInstance(); + } + BlockchainRegistry* registry() { return BlockchainRegistry::GetInstance(); } + + void SetOnDemandUpdateCallbackWithComponentReady(const base::FilePath& path) { + brave_component_updater::BraveOnDemandUpdater::GetInstance() + ->RegisterOnDemandUpdateCallback( + base::BindLambdaForTesting([path, this](const std::string& id) { + if (id != kComponentId) { + return; + } + + // Unblock CreateWallet once the component is registered. + installer().OnComponentReady(path); + })); + } + + void SetOnDemandUpdateCallbackWithEmptyCallback() { + brave_component_updater::BraveOnDemandUpdater::GetInstance() + ->RegisterOnDemandUpdateCallback(base::DoNothing()); + } + + void SetOnDemandUpdateCallbackWithComponentUpdateError() { + brave_component_updater::BraveOnDemandUpdater::GetInstance() + ->RegisterOnDemandUpdateCallback( + base::BindLambdaForTesting([&](const std::string& id) { + if (id != kComponentId) { + return; + } + + installer().OnEvent(update_client::UpdateClient::Observer:: + Events::COMPONENT_UPDATE_ERROR, + kComponentId); + })); + } + + protected: + void RunUntilIdle() { + task_environment_.RunUntilIdle(); + base::RunLoop().RunUntilIdle(); + } + + private: + base::test::TaskEnvironment task_environment_; + sync_preferences::TestingPrefServiceSyncable prefs_; + sync_preferences::TestingPrefServiceSyncable local_state_; + network::TestURLLoaderFactory url_loader_factory_; + scoped_refptr shared_url_loader_factory_; + data_decoder::test::InProcessDataDecoder in_process_data_decoder_; + std::unique_ptr keyring_service_; + std::unique_ptr json_rpc_service_; + std::unique_ptr brave_wallet_service_; + std::unique_ptr cus_; + base::ScopedTempDir install_dir_; +}; + +TEST_F(WalletDataFilesInstallerUnitTest, + MaybeRegisterWalletDataFilesComponent_NoRegisterWithoutWallets) { + EXPECT_CALL(*updater(), RegisterComponent(testing::_)).Times(0); + installer().MaybeRegisterWalletDataFilesComponent(updater(), local_state()); + RunUntilIdle(); +} + +TEST_F(WalletDataFilesInstallerUnitTest, + MaybeRegisterWalletDataFilesComponent_RegisterWithWallets) { + EXPECT_CALL(*updater(), RegisterComponent(testing::_)) + .Times(1) + .WillOnce(testing::Return(true)); + brave_component_updater::BraveOnDemandUpdater::GetInstance() + ->RegisterOnDemandUpdateCallback(base::DoNothing()); + // Mimic created wallets. + local_state()->SetTime(kBraveWalletLastUnlockTime, base::Time::Now()); + installer().MaybeRegisterWalletDataFilesComponent(updater(), local_state()); + RunUntilIdle(); +} + +TEST_F(WalletDataFilesInstallerUnitTest, OnDemandInstallAndParsing_EmptyPath) { + EXPECT_CALL(*updater(), RegisterComponent(testing::_)) + .Times(1) + .WillOnce(testing::Return(true)); + SetOnDemandUpdateCallbackWithComponentReady(base::FilePath()); + CreateWallet(); + + RunUntilIdle(); + EXPECT_TRUE(registry()->IsEmptyForTesting()); +} + +TEST_F(WalletDataFilesInstallerUnitTest, + OnDemandInstallAndParsing_FileNotFound) { + EXPECT_CALL(*updater(), RegisterComponent(testing::_)) + .Times(1) + .WillOnce(testing::Return(true)); + SetOnDemandUpdateCallbackWithComponentReady(install_dir()); + CreateWallet(); + + RunUntilIdle(); + EXPECT_TRUE(registry()->IsEmptyForTesting()); +} + +// This test case covers: 1) Normal JSON file parsing, 2) Parse JSON file +// failed, 3) Cannot find file. CreateWallet should be done still with. +// CreateWallet call should be resolved, and blockchain registry will have +// data where parsing is successful. +TEST_F(WalletDataFilesInstallerUnitTest, + OnDemandInstallAndParsing_ParseJsonFiles) { + EXPECT_CALL(*updater(), RegisterComponent(testing::_)) + .Times(1) + .WillOnce(testing::Return(true)); + SetOnDemandUpdateCallbackWithComponentReady(install_dir()); + + WriteCoingeckoIdsMapToFile(); + ASSERT_TRUE( + base::WriteFile(install_dir().Append(FPL("contract-map.json")), "bad")); + + const std::string ofac_list_json = R"({ + "addresses": [ + "0xb9ef770b6a5e12e45983c5d80545258aa38f3b78" + ] + })"; + ASSERT_TRUE(base::WriteFile( + install_dir().Append( + FPL("ofac-sanctioned-digital-currency-addresses.json")), + ofac_list_json)); + + CreateWallet(); + + RunUntilIdle(); + EXPECT_FALSE(registry()->IsEmptyForTesting()); + EXPECT_TRUE(registry()->GetPrepopulatedNetworks().empty()); + EXPECT_EQ(registry()->GetCoingeckoId( + "0xa", "0x7f5c764cbc14f9669b88837ca1490cca17c31607"), + "usd-coin"); + EXPECT_TRUE( + registry()->IsOfacAddress("0xb9ef770b6a5e12e45983c5d80545258aa38f3b78")); +} + +TEST_F(WalletDataFilesInstallerUnitTest, + OnDemandInstallAndParsing_InstallFail) { + EXPECT_CALL(*updater(), RegisterComponent(testing::_)) + .Times(1) + .WillOnce(testing::Return(true)); + SetOnDemandUpdateCallbackWithComponentUpdateError(); + CreateWallet(); + + RunUntilIdle(); + EXPECT_TRUE(registry()->IsEmptyForTesting()); +} + +TEST_F(WalletDataFilesInstallerUnitTest, + OnDemandInstallAndParsing_RestoreWallet) { + EXPECT_CALL(*updater(), RegisterComponent(testing::_)) + .Times(1) + .WillOnce(testing::Return(true)); + SetOnDemandUpdateCallbackWithComponentReady(install_dir()); + WriteCoingeckoIdsMapToFile(); + + RestoreWallet(); + RunUntilIdle(); + EXPECT_FALSE(registry()->IsEmptyForTesting()); + EXPECT_EQ(registry()->GetCoingeckoId( + "0xa", "0x7f5c764cbc14f9669b88837ca1490cca17c31607"), + "usd-coin"); +} + +TEST_F(WalletDataFilesInstallerUnitTest, + OnDemandInstallAndParsing_ImportFromExternalWallet) { + EXPECT_CALL(*updater(), RegisterComponent(testing::_)) + .Times(1) + .WillOnce(testing::Return(true)); + SetOnDemandUpdateCallbackWithComponentReady(install_dir()); + WriteCoingeckoIdsMapToFile(); + + ImportFromExternalWallet(); + RunUntilIdle(); + EXPECT_FALSE(registry()->IsEmptyForTesting()); + EXPECT_EQ(registry()->GetCoingeckoId( + "0xa", "0x7f5c764cbc14f9669b88837ca1490cca17c31607"), + "usd-coin"); +} + +} // namespace brave_wallet diff --git a/components/brave_wallet_ui/constants/types.ts b/components/brave_wallet_ui/constants/types.ts index 1272a150dc8b..553faa17d07b 100644 --- a/components/brave_wallet_ui/constants/types.ts +++ b/components/brave_wallet_ui/constants/types.ts @@ -306,6 +306,7 @@ export interface PageState { importWalletAttempts: number walletTermsAcknowledged: boolean selectedCoinMarket: BraveWallet.CoinMarket | undefined + isCreatingWallet: boolean } export interface WalletPageState { diff --git a/components/brave_wallet_ui/page/actions/wallet_page_actions.ts b/components/brave_wallet_ui/page/actions/wallet_page_actions.ts index 591a7eeb220c..743603f6a1e7 100644 --- a/components/brave_wallet_ui/page/actions/wallet_page_actions.ts +++ b/components/brave_wallet_ui/page/actions/wallet_page_actions.ts @@ -32,5 +32,6 @@ export const { updateSelectedAsset, walletBackupComplete, walletCreated, - walletSetupComplete + walletSetupComplete, + setIsCreatingWallet } = PageActions 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 9ec28ece74d1..60c0c378074c 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 @@ -67,8 +67,10 @@ async function importFromExternalWallet( handler.on( WalletPageActions.createWallet.type, async (store: Store, payload: CreateWalletPayloadType) => { + store.dispatch(WalletPageActions.setIsCreatingWallet(true)) const keyringService = getWalletPageApiProxy().keyringService const result = await keyringService.createWallet(payload.password) + store.dispatch(WalletPageActions.setIsCreatingWallet(false)) store.dispatch( WalletPageActions.walletCreated({ mnemonic: result.mnemonic }) ) @@ -78,12 +80,14 @@ handler.on( handler.on( WalletPageActions.restoreWallet.type, async (store: Store, payload: RestoreWalletPayloadType) => { + store.dispatch(WalletPageActions.setIsCreatingWallet(true)) const keyringService = getWalletPageApiProxy().keyringService const result = await keyringService.restoreWallet( payload.mnemonic, payload.password, payload.isLegacy ) + store.dispatch(WalletPageActions.setIsCreatingWallet(false)) if (!result.isValidMnemonic) { store.dispatch( WalletPageActions.hasMnemonicError(!result.isValidMnemonic) @@ -159,11 +163,13 @@ handler.on(WalletPageActions.checkWalletsToImport.type, async (store) => { handler.on( WalletPageActions.importFromCryptoWallets.type, async (store: Store, payload: ImportFromExternalWalletPayloadType) => { + store.dispatch(WalletPageActions.setIsCreatingWallet(true)) const results: ImportWalletErrorPayloadType = await importFromExternalWallet( BraveWallet.ExternalWalletType.CryptoWallets, payload ) + store.dispatch(WalletPageActions.setIsCreatingWallet(false)) store.dispatch(WalletPageActions.setImportWalletError(results)) } ) @@ -171,11 +177,13 @@ handler.on( handler.on( WalletPageActions.importFromMetaMask.type, async (store: Store, payload: ImportFromExternalWalletPayloadType) => { + store.dispatch(WalletPageActions.setIsCreatingWallet(true)) const results: ImportWalletErrorPayloadType = await importFromExternalWallet( BraveWallet.ExternalWalletType.MetaMask, payload ) + store.dispatch(WalletPageActions.setIsCreatingWallet(false)) store.dispatch(WalletPageActions.setImportWalletError(results)) } ) diff --git a/components/brave_wallet_ui/page/reducers/page_reducer.ts b/components/brave_wallet_ui/page/reducers/page_reducer.ts index c57bfdd0d2ea..dbfe034799e3 100644 --- a/components/brave_wallet_ui/page/reducers/page_reducer.ts +++ b/components/brave_wallet_ui/page/reducers/page_reducer.ts @@ -42,7 +42,8 @@ const defaultState: PageState = { isImportWalletsCheckComplete: false, importWalletAttempts: 0, walletTermsAcknowledged: false, - selectedCoinMarket: undefined + selectedCoinMarket: undefined, + isCreatingWallet: false } export const WalletPageAsyncActions = { @@ -196,6 +197,10 @@ export const createPageSlice = (initialState: PageState = defaultState) => { updateAutoPinEnabled(state, { payload }: PayloadAction) { state.isAutoPinEnabled = payload + }, + + setIsCreatingWallet(state, { payload }: PayloadAction) { + state.isCreatingWallet = payload } } }) diff --git a/components/brave_wallet_ui/page/screens/onboarding/create-password/onboarding-create-password.tsx b/components/brave_wallet_ui/page/screens/onboarding/create-password/onboarding-create-password.tsx index 676ee96e2463..0ecba902187b 100644 --- a/components/brave_wallet_ui/page/screens/onboarding/create-password/onboarding-create-password.tsx +++ b/components/brave_wallet_ui/page/screens/onboarding/create-password/onboarding-create-password.tsx @@ -4,13 +4,21 @@ // you can obtain one at https://mozilla.org/MPL/2.0/. import * as React from 'react' -import { useDispatch, useSelector } from 'react-redux' +import { useDispatch } from 'react-redux' + +// selectors +import { + useSafePageSelector, + useSafeWalletSelector +} from '../../../../common/hooks/use-safe-selector' +import { PageSelectors } from '../../../selectors' +import { WalletSelectors } from '../../../../common/selectors' // utils import { getLocale } from '../../../../../common/locale' // routes -import { WalletRoutes, WalletState } from '../../../../constants/types' +import { WalletRoutes } from '../../../../constants/types' // actions import { WalletPageActions } from '../../../actions' @@ -25,6 +33,7 @@ import { } from '../../../../components/shared/password-input/new-password-input' import { OnboardingNewWalletStepsNavigation } from '../components/onboarding-steps-navigation/onboarding-steps-navigation' import { CenteredPageLayout } from '../../../../components/desktop/centered-page-layout/centered-page-layout' +import { CreatingWallet } from '../creating_wallet/creating_wallet' // styles import { @@ -48,9 +57,8 @@ export const OnboardingCreatePassword = ( // redux const dispatch = useDispatch() - const isWalletCreated = useSelector( - ({ wallet }: { wallet: WalletState }) => wallet.isWalletCreated - ) + const isWalletCreated = useSafeWalletSelector(WalletSelectors.isWalletCreated) + const isCreatingWallet = useSafePageSelector(PageSelectors.isCreatingWallet) // state const [isValid, setIsValid] = React.useState(false) @@ -73,10 +81,14 @@ export const OnboardingCreatePassword = ( // effects React.useEffect(() => { - if (isWalletCreated) { + if (!isCreatingWallet && isWalletCreated) { onWalletCreated() } - }, [isWalletCreated, onWalletCreated]) + }, [isWalletCreated, onWalletCreated, isCreatingWallet]) + + if (isCreatingWallet) { + return + } // render return ( diff --git a/components/brave_wallet_ui/page/screens/onboarding/creating_wallet/creating_wallet.style.ts b/components/brave_wallet_ui/page/screens/onboarding/creating_wallet/creating_wallet.style.ts new file mode 100644 index 000000000000..4b5be1b197b1 --- /dev/null +++ b/components/brave_wallet_ui/page/screens/onboarding/creating_wallet/creating_wallet.style.ts @@ -0,0 +1,23 @@ +// Copyright (c) 2023 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. +import styled from 'styled-components' +import ProgressRing from '@brave/leo/react/progressRing' +import * as leo from '@brave/leo/tokens/css' + +// Shared Styles +import { Text } from '../../../../components/shared/style' + +export const LoadingIcon = styled(ProgressRing)` + --leo-progressring-size: 40px; + --leo-progressring-background-color: rgba(217, 217, 217, 1); + --leo-progressring-color: ${leo.color.icon.interactive}; + margin-bottom: 20px; +` + +export const CreatingWalletText = styled(Text)` + font-weight: 500; + line-height: 28px; + color: ${leo.color.text.primary}; +` diff --git a/components/brave_wallet_ui/page/screens/onboarding/creating_wallet/creating_wallet.tsx b/components/brave_wallet_ui/page/screens/onboarding/creating_wallet/creating_wallet.tsx new file mode 100644 index 000000000000..0b02f9774c17 --- /dev/null +++ b/components/brave_wallet_ui/page/screens/onboarding/creating_wallet/creating_wallet.tsx @@ -0,0 +1,40 @@ +// Copyright (c) 2023 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this file, +// You can obtain one at https://mozilla.org/MPL/2.0/. + +import * as React from 'react' + +// Utils +import { getLocale } from '../../../../../common/locale' + +// Components +import { + CenteredPageLayout +} from +'../../../../components/desktop/centered-page-layout/centered-page-layout' + +// Styled Components +import { CreatingWalletText, LoadingIcon } from './creating_wallet.style' +import { StyledWrapper, MainWrapper } from '../onboarding.style' +import { Column } from '../../../../components/shared/style' + +export const CreatingWallet = () => { + return ( + + + + + + + {getLocale('braveWalletCreatingWallet')} + + + + + + ) +} diff --git a/components/brave_wallet_ui/page/screens/onboarding/onboarding.routes.tsx b/components/brave_wallet_ui/page/screens/onboarding/onboarding.routes.tsx index c55f9aabc6e2..b891699e1634 100644 --- a/components/brave_wallet_ui/page/screens/onboarding/onboarding.routes.tsx +++ b/components/brave_wallet_ui/page/screens/onboarding/onboarding.routes.tsx @@ -4,9 +4,16 @@ // you can obtain one at https://mozilla.org/MPL/2.0/. import * as React from 'react' -import { useSelector } from 'react-redux' import { Redirect, Route, Switch, useHistory, useLocation } from 'react-router' +// selectors +import { + useSafePageSelector, // + useSafeWalletSelector +} from '../../../common/hooks/use-safe-selector' +import { PageSelectors } from '../../selectors' +import { WalletSelectors } from '../../../common/selectors' + // utils import { useApiProxy } from '../../../common/hooks/use-api-proxy' @@ -20,12 +27,7 @@ import { OnboardingImportOrRestoreWallet } from './import-or-restore-wallet/impo import { OnboardingRestoreFromRecoveryPhrase } from './restore-from-recovery-phrase/restore-from-recovery-phrase' // types -import { - BraveWallet, - PageState, - WalletRoutes, - WalletState -} from '../../../constants/types' +import { BraveWallet, WalletRoutes } from '../../../constants/types' import { OnboardingSuccess } from './onboarding-success/onboarding-success' import { OnboardingConnectHardwareWallet } from './connect-hardware/onboarding-connect-hardware-wallet' @@ -38,12 +40,11 @@ export const OnboardingRoutes = () => { const { braveWalletP3A } = useApiProxy() // redux - const isWalletCreated = useSelector( - ({ wallet }: { wallet: WalletState }) => wallet.isWalletCreated - ) - const termsAcknowledged = useSelector( - ({ page }: { page: PageState }) => page.walletTermsAcknowledged + const isWalletCreated = useSafeWalletSelector(WalletSelectors.isWalletCreated) + const termsAcknowledged = useSafePageSelector( + PageSelectors.walletTermsAcknowledged ) + const isCreatingWallet = useSafePageSelector(PageSelectors.isCreatingWallet) // methods const goToConnectHardware = React.useCallback(() => { @@ -75,6 +76,9 @@ export const OnboardingRoutes = () => { } }, [location]) + // computed + const showOnboardingRestore = !isWalletCreated || isCreatingWallet + // render return ( @@ -126,7 +130,7 @@ export const OnboardingRoutes = () => { )} - {!isWalletCreated && ( + {showOnboardingRestore && ( { )} - {!isWalletCreated && ( + {showOnboardingRestore && ( { )} - {!isWalletCreated && ( + {showOnboardingRestore && ( { )} - {!isWalletCreated && ( + {showOnboardingRestore && ( { )} - {!isWalletCreated && ( + {showOnboardingRestore && ( page.invalidMnemonic - ) - const isImportWalletsCheckComplete = useSelector( - ({ page }: { page: PageState }) => page.isImportWalletsCheckComplete - ) - const importWalletError = useSelector( - ({ page }: { page: PageState }) => page.importWalletError + const invalidMnemonic = useSafePageSelector(PageSelectors.invalidMnemonic) + const isImportWalletsCheckComplete = useSafePageSelector( + PageSelectors.isImportWalletsCheckComplete ) - const importWalletAttempts = useSelector( - ({ page }: { page: PageState }) => page.importWalletAttempts + const importWalletError = useUnsafePageSelector( + PageSelectors.importWalletError ) - const isWalletCreated = useSelector( - ({ wallet }: { wallet: WalletState }) => wallet.isWalletCreated + const importWalletAttempts = useSafePageSelector( + PageSelectors.importWalletAttempts ) + const isCreatingWallet = useSafePageSelector(PageSelectors.isCreatingWallet) // computed const isImportingFromMetaMaskExtension = restoreFrom === 'metamask' @@ -364,13 +364,6 @@ export const OnboardingRestoreFromRecoveryPhrase = ({ } }, [isCheckingExtensions]) - React.useEffect(() => { - // go to onboarding success screen when wallet restoration completes - if (isWalletCreated) { - history.push(WalletRoutes.OnboardingComplete) - } - }, [isWalletCreated]) - React.useEffect(() => { // clear other errors on step change if ( @@ -406,6 +399,10 @@ export const OnboardingRestoreFromRecoveryPhrase = ({ importWalletError?.hasError ]) + if (isCreatingWallet) { + return + } + // render return ( diff --git a/components/brave_wallet_ui/page/selectors/page-selectors.ts b/components/brave_wallet_ui/page/selectors/page-selectors.ts index 5093241ebe62..31c8dd098894 100644 --- a/components/brave_wallet_ui/page/selectors/page-selectors.ts +++ b/components/brave_wallet_ui/page/selectors/page-selectors.ts @@ -29,6 +29,7 @@ export const showIsRestoring = ({ page }: State) => page.showIsRestoring export const showRecoveryPhrase = ({ page }: State) => page.showRecoveryPhrase export const walletTermsAcknowledged = ({ page }: State) => page.walletTermsAcknowledged +export const isCreatingWallet = ({ page }: State) => page.isCreatingWallet // unsafe selectors (will cause re-render if not strictly equal "===") (objects // and lists) diff --git a/components/brave_wallet_ui/stories/locale.ts b/components/brave_wallet_ui/stories/locale.ts index 46a27fcb3fe8..17337e64f405 100644 --- a/components/brave_wallet_ui/stories/locale.ts +++ b/components/brave_wallet_ui/stories/locale.ts @@ -300,6 +300,9 @@ provideStrings({ braveWalletPasswordIsMediumStrength: 'Medium', braveWalletPasswordIsWeak: 'Weak', + // Creating Wallet + braveWalletCreatingWallet: 'Creating Wallet...', + // Create Password - Stength Tooltip braveWalletPasswordStrengthTooltipHeading: 'At least:', braveWalletPasswordStrengthTooltipIsLongEnough: '8 characters', diff --git a/components/brave_wallet_ui/stories/mock-data/mock-page-state.ts b/components/brave_wallet_ui/stories/mock-data/mock-page-state.ts index 71ffd76a6599..51840ff078bd 100644 --- a/components/brave_wallet_ui/stories/mock-data/mock-page-state.ts +++ b/components/brave_wallet_ui/stories/mock-data/mock-page-state.ts @@ -34,5 +34,6 @@ export const mockPageState: PageState = { .replace('velvet', 'THIRD') .concat(` ${mockedMnemonic} LAST`), enablingAutoPin: false, - isAutoPinEnabled: false + isAutoPinEnabled: false, + isCreatingWallet: false } diff --git a/components/resources/wallet_strings.grdp b/components/resources/wallet_strings.grdp index 3f5bd488a1d8..4a75088174c1 100644 --- a/components/resources/wallet_strings.grdp +++ b/components/resources/wallet_strings.grdp @@ -765,6 +765,7 @@ Strong! Medium Weak + Creating Wallet... Before you start backing up wallet The 12-24 word recovery phrase is a private key you can use to regain access to your wallet in case you lose a connected device(s). Store it someplace safe, and in the exact order it appears below. Brave cannot access your secret recovery phrase. Keep it safe, and never share it with anyone else. diff --git a/ios/app/brave_core_main.mm b/ios/app/brave_core_main.mm index e3f4ef85e348..d683ff321cb9 100644 --- a/ios/app/brave_core_main.mm +++ b/ios/app/brave_core_main.mm @@ -313,7 +313,9 @@ - (void)registerComponentsForUpdate: base::BindRepeating(&component_updater::BraveOnDemandUpdate)); RegisterSafetyTipsComponent(cus); - brave_wallet::RegisterWalletDataFilesComponent(cus); + brave_wallet::WalletDataFilesInstaller::GetInstance() + .MaybeRegisterWalletDataFilesComponent( + cus, GetApplicationContext()->GetLocalState()); } + (void)setLogHandler:(BraveCoreLogHandler)logHandler { diff --git a/ios/browser/application_context/BUILD.gn b/ios/browser/application_context/BUILD.gn index d9c4c79ed555..69dc5a0b5b6e 100644 --- a/ios/browser/application_context/BUILD.gn +++ b/ios/browser/application_context/BUILD.gn @@ -14,7 +14,9 @@ source_set("application_context") { deps = [ "//base", "//brave/components/brave_component_updater/browser", + "//brave/components/brave_wallet/browser", "//brave/components/url_sanitizer/browser", + "//brave/ios/browser/brave_wallet", "//ios/chrome/browser/application_context/model", "//ios/chrome/browser/shared/model/application_context", ] diff --git a/ios/browser/application_context/brave_application_context_impl.mm b/ios/browser/application_context/brave_application_context_impl.mm index 9b17cd0460a7..ba5ad0c0cb7a 100644 --- a/ios/browser/application_context/brave_application_context_impl.mm +++ b/ios/browser/application_context/brave_application_context_impl.mm @@ -12,7 +12,9 @@ #include "brave/components/brave_component_updater/browser/brave_component.h" #include "brave/components/brave_component_updater/browser/brave_component_updater_delegate.h" #include "brave/components/brave_component_updater/browser/local_data_files_service.h" +#include "brave/components/brave_wallet/browser/wallet_data_files_installer.h" #include "brave/components/url_sanitizer/browser/url_sanitizer_component_installer.h" +#include "brave/ios/browser/brave_wallet/wallet_data_files_installer_delegate_impl.h" #include "ios/chrome/browser/shared/model/application_context/application_context.h" BraveApplicationContextImpl::BraveApplicationContextImpl( @@ -83,4 +85,7 @@ // Start the local data file service local_data_files_service()->Start(); + + brave_wallet::WalletDataFilesInstaller::GetInstance().SetDelegate( + std::make_unique()); } diff --git a/ios/browser/brave_wallet/BUILD.gn b/ios/browser/brave_wallet/BUILD.gn index 8066650d4b6a..207ead1e9186 100644 --- a/ios/browser/brave_wallet/BUILD.gn +++ b/ios/browser/brave_wallet/BUILD.gn @@ -21,6 +21,8 @@ source_set("brave_wallet") { "swap_service_factory.h", "tx_service_factory.cc", "tx_service_factory.h", + "wallet_data_files_installer_delegate_impl.h", + "wallet_data_files_installer_delegate_impl.mm", "zcash_wallet_service_factory.h", "zcash_wallet_service_factory.mm", ] diff --git a/ios/browser/brave_wallet/wallet_data_files_installer_delegate_impl.h b/ios/browser/brave_wallet/wallet_data_files_installer_delegate_impl.h new file mode 100644 index 000000000000..0701d70a9779 --- /dev/null +++ b/ios/browser/brave_wallet/wallet_data_files_installer_delegate_impl.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2023 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_IOS_BROWSER_BRAVE_WALLET_WALLET_DATA_FILES_INSTALLER_DELEGATE_IMPL_H_ +#define BRAVE_IOS_BROWSER_BRAVE_WALLET_WALLET_DATA_FILES_INSTALLER_DELEGATE_IMPL_H_ + +#include "brave/components/brave_wallet/browser/wallet_data_files_installer_delegate.h" + +namespace brave_wallet { + +class WalletDataFilesInstallerDelegateImpl + : public WalletDataFilesInstallerDelegate { + public: + WalletDataFilesInstallerDelegateImpl() = default; + ~WalletDataFilesInstallerDelegateImpl() override = default; + component_updater::ComponentUpdateService* GetComponentUpdater() override; +}; + +} // namespace brave_wallet + +#endif // BRAVE_IOS_BROWSER_BRAVE_WALLET_WALLET_DATA_FILES_INSTALLER_DELEGATE_IMPL_H_ diff --git a/ios/browser/brave_wallet/wallet_data_files_installer_delegate_impl.mm b/ios/browser/brave_wallet/wallet_data_files_installer_delegate_impl.mm new file mode 100644 index 000000000000..f8e482ef6686 --- /dev/null +++ b/ios/browser/brave_wallet/wallet_data_files_installer_delegate_impl.mm @@ -0,0 +1,16 @@ +/* Copyright (c) 2023 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/ios/browser/brave_wallet/wallet_data_files_installer_delegate_impl.h" +#include "ios/chrome/browser/shared/model/application_context/application_context.h" + +namespace brave_wallet { + +component_updater::ComponentUpdateService* +WalletDataFilesInstallerDelegateImpl::GetComponentUpdater() { + return GetApplicationContext()->GetComponentUpdateService(); +} + +} // namespace brave_wallet