diff --git a/android/brave_java_resources.gni b/android/brave_java_resources.gni
index 6ba0c11eabe3..71da9d54947c 100644
--- a/android/brave_java_resources.gni
+++ b/android/brave_java_resources.gni
@@ -169,6 +169,7 @@ brave_java_resources = [
"java/res/drawable-night-xhdpi/ic_cookie_background.png",
"java/res/drawable-night-xxhdpi/ic_cookie_background.png",
"java/res/drawable-night-xxxhdpi/ic_cookie_background.png",
+ "java/res/drawable-night/sardine_logo.xml",
"java/res/drawable-nodpi/dylan_malval_sea_min.webp",
"java/res/drawable-nodpi/eth.png",
"java/res/drawable-nodpi/fee_icon.png",
@@ -692,6 +693,7 @@ brave_java_resources = [
"java/res/drawable/radio_button_holo_bg.xml",
"java/res/drawable/radio_button_selected_bg.xml",
"java/res/drawable/radiobutton_background.xml",
+ "java/res/drawable/ramp_logo.xml",
"java/res/drawable/rating_bottomsheet_background.xml",
"java/res/drawable/rating_news_button_background.xml",
"java/res/drawable/recovery_phrase_bg.xml",
@@ -720,6 +722,7 @@ brave_java_resources = [
"java/res/drawable/rounded_top_corners.xml",
"java/res/drawable/rounded_wallet_edittext.xml",
"java/res/drawable/rounded_white_holo_bg.xml",
+ "java/res/drawable/sardine_logo.xml",
"java/res/drawable/selected_dot.xml",
"java/res/drawable/selected_indicator.xml",
"java/res/drawable/set_default_rounded_button_disabled.xml",
@@ -731,6 +734,7 @@ brave_java_resources = [
"java/res/drawable/tab_selector.xml",
"java/res/drawable/tip_amount.xml",
"java/res/drawable/toggle_text_color_selector.xml",
+ "java/res/drawable/transak_logo.xml",
"java/res/drawable/transparent_bg_bordered.xml",
"java/res/drawable/twitter_button_background.xml",
"java/res/drawable/unverified_48_rounded_bg.xml",
@@ -769,6 +773,7 @@ brave_java_resources = [
"java/res/layout/activity_network_selector.xml",
"java/res/layout/activity_nft_detail.xml",
"java/res/layout/activity_onboarding.xml",
+ "java/res/layout/activity_select_purchase_method.xml",
"java/res/layout/activity_split_tunnel.xml",
"java/res/layout/activity_welcome_onboarding.xml",
"java/res/layout/application_item_layout.xml",
diff --git a/android/brave_java_sources.gni b/android/brave_java_sources.gni
index d41786059963..8772b4f881b0 100644
--- a/android/brave_java_sources.gni
+++ b/android/brave_java_sources.gni
@@ -45,6 +45,7 @@ brave_java_sources = [
"../../brave/android/java/org/chromium/chrome/browser/app/BraveActivity.java",
"../../brave/android/java/org/chromium/chrome/browser/app/appmenu/AppMenuIconRowFooter.java",
"../../brave/android/java/org/chromium/chrome/browser/app/appmenu/BraveAppMenuPropertiesDelegateImpl.java",
+ "../../brave/android/java/org/chromium/chrome/browser/app/domain/BuyModel.java",
"../../brave/android/java/org/chromium/chrome/browser/app/domain/CryptoModel.java",
"../../brave/android/java/org/chromium/chrome/browser/app/domain/CryptoSharedActions.java",
"../../brave/android/java/org/chromium/chrome/browser/app/domain/CryptoSharedData.java",
@@ -92,6 +93,7 @@ brave_java_sources = [
"../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/activities/BuySendSwapActivity.java",
"../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/activities/NetworkSelectorActivity.java",
"../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/activities/NftDetailActivity.java",
+ "../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/activities/SelectPurchaseMethodActivity.java",
"../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/adapters/AccountSpinnerAdapter.java",
"../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/adapters/ApproveTxFragmentPageAdapter.java",
"../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/adapters/CreateAccountAdapter.java",
diff --git a/android/java/AndroidManifest.xml b/android/java/AndroidManifest.xml
index dd300b5c4bc8..6274cbd0a5a2 100644
--- a/android/java/AndroidManifest.xml
+++ b/android/java/AndroidManifest.xml
@@ -86,6 +86,11 @@
android:screenOrientation="sensorPortrait"
tools:ignore="LockedOrientationActivity"/>
+
+
{
+ if (error != null && !error.isEmpty()) {
+ callback.OnUrlReady(null);
+ return;
+ }
+ callback.OnUrlReady(url);
+ });
+ }
+
+ public void isBuySupported(NetworkInfo selectedNetwork, String assetSymbol,
+ String contractAddress, String chainId, int[] rampProviders,
+ Callback1 callback) {
+ TokenUtils.getBuyTokensFiltered(mBlockchainRegistry, selectedNetwork,
+ TokenUtils.TokenType.ALL, rampProviders, tokens -> {
+ callback.call(JavaUtils.includes(tokens,
+ iToken
+ -> AssetUtils.Filters.isSameToken(
+ iToken, assetSymbol, contractAddress, chainId)));
+ });
+ }
+
+ void resetServices(AssetRatioService assetRatioService, BlockchainRegistry blockchainRegistry) {
+ synchronized (mLock) {
+ mAssetRatioService = assetRatioService;
+ mBlockchainRegistry = blockchainRegistry;
+ }
+ }
+
+ public interface OnRampCallback {
+ void OnUrlReady(String url);
+ }
+}
diff --git a/android/java/org/chromium/chrome/browser/app/domain/CryptoModel.java b/android/java/org/chromium/chrome/browser/app/domain/CryptoModel.java
index 6d5b32bc78c1..9ea9504f2819 100644
--- a/android/java/org/chromium/chrome/browser/app/domain/CryptoModel.java
+++ b/android/java/org/chromium/chrome/browser/app/domain/CryptoModel.java
@@ -78,6 +78,7 @@ public class CryptoModel {
private NetworkModel mNetworkModel;
private PortfolioModel mPortfolioModel;
+ private BuyModel mBuyModel;
// Todo: create method to create and return new models for Asset, Account,
// TransactionConfirmation, SwapModel, AssetModel, SendModel
@@ -134,6 +135,9 @@ public void resetServices(Context context, TxService mTxService, KeyringService
mPortfolioModel.resetServices(context, mTxService, mKeyringService, mBlockchainRegistry,
mJsonRpcService, mEthTxManagerProxy, mSolanaTxManagerProxy, mBraveWalletService,
mAssetRatioService);
+ if (mBuyModel != null) {
+ mBuyModel.resetServices(mAssetRatioService, mBlockchainRegistry);
+ }
}
init();
}
@@ -273,6 +277,13 @@ public NetworkModel getNetworkModel() {
return mNetworkModel;
}
+ public BuyModel getBuyModel() {
+ if (mBuyModel == null) {
+ mBuyModel = new BuyModel(mAssetRatioService, mBlockchainRegistry);
+ }
+ return mBuyModel;
+ }
+
public PortfolioModel getPortfolioModel() {
return mPortfolioModel;
}
@@ -324,21 +335,10 @@ public void setAccountInfosFromKeyRingModel(
mNetworkModel.setAccountInfosFromKeyRingModel(accountInfosFromKeyRingModel);
}
- // TODO: Move to BuyModel class
- public void isBuySupported(NetworkInfo selectedNetwork, String assetSymbol,
- String contractAddress, String chainId, Callback1 callback) {
- TokenUtils.getBuyTokensFiltered(
- mBlockchainRegistry, selectedNetwork, TokenUtils.TokenType.ALL, tokens -> {
- callback.call(JavaUtils.includes(tokens,
- iToken
- -> AssetUtils.Filters.isSameToken(
- iToken, assetSymbol, contractAddress, chainId)));
- });
- }
-
// Clear buy send swap model
public void clearBSS() {
mSendModel = null;
+ mBuyModel = null;
}
/*
diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/activities/AssetDetailActivity.java b/android/java/org/chromium/chrome/browser/crypto_wallet/activities/AssetDetailActivity.java
index eae9635015de..1f32a5027a9c 100644
--- a/android/java/org/chromium/chrome/browser/crypto_wallet/activities/AssetDetailActivity.java
+++ b/android/java/org/chromium/chrome/browser/crypto_wallet/activities/AssetDetailActivity.java
@@ -40,6 +40,7 @@
import org.chromium.brave_wallet.mojom.TransactionInfo;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.app.BraveActivity;
+import org.chromium.chrome.browser.app.domain.BuyModel;
import org.chromium.chrome.browser.app.domain.WalletModel;
import org.chromium.chrome.browser.crypto_wallet.BlockchainRegistryFactory;
import org.chromium.chrome.browser.crypto_wallet.adapters.WalletCoinAdapter;
@@ -506,8 +507,9 @@ private void showHideBuyUi() {
LiveDataUtil.observeOnce(mWalletModel.getCryptoModel().getNetworkModel().mDefaultNetwork,
selectedNetwork -> {
- mWalletModel.getCryptoModel().isBuySupported(selectedNetwork, mAssetSymbol,
- mContractAddress, mChainId, isBuyEnabled -> {
+ mWalletModel.getCryptoModel().getBuyModel().isBuySupported(selectedNetwork,
+ mAssetSymbol, mContractAddress, mChainId,
+ BuyModel.SUPPORTED_RAMP_PROVIDERS, isBuyEnabled -> {
if (isBuyEnabled) {
AndroidUtils.show(mBtnBuy);
} else {
diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/activities/BuySendSwapActivity.java b/android/java/org/chromium/chrome/browser/crypto_wallet/activities/BuySendSwapActivity.java
index 2aeae2a6db91..f447614f6a68 100644
--- a/android/java/org/chromium/chrome/browser/crypto_wallet/activities/BuySendSwapActivity.java
+++ b/android/java/org/chromium/chrome/browser/crypto_wallet/activities/BuySendSwapActivity.java
@@ -971,18 +971,11 @@ public void onTextChanged(CharSequence s, int start, int before, int count) {
});
}
} else if (mActivityType == ActivityType.BUY) {
- assert mBlockchainRegistry != null;
- String symbol = AssetUtils.mapToRampNetworkSymbol(mCurrentBlockchainToken);
- mBlockchainRegistry.getBuyUrl(OnRampProvider.RAMP, mCurrentBlockchainToken.chainId,
- from, symbol, amount, (url, error) -> {
- if (error != null && !error.isEmpty() && Utils.isDebuggable(this)) {
- Log.e(TAG, "Could not get buy URL: " + error);
- return;
- }
+ Intent selectPurchaseMethodIntent = SelectPurchaseMethodActivity.getIntent(this,
+ mCurrentBlockchainToken.chainId, from, mCurrentBlockchainToken.symbol,
+ mCurrentBlockchainToken.contractAddress, amount);
+ startActivity(selectPurchaseMethodIntent);
- TabUtils.openUrlInNewTab(false, url);
- TabUtils.bringChromeTabbedActivityToTheTop(this);
- });
} else if (mActivityType == ActivityType.SWAP) {
if (mCurrentBlockchainToken != null) {
String btnText = mBtnBuySendSwap.getText().toString();
diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/activities/SelectPurchaseMethodActivity.java b/android/java/org/chromium/chrome/browser/crypto_wallet/activities/SelectPurchaseMethodActivity.java
new file mode 100644
index 000000000000..a1d52fed2147
--- /dev/null
+++ b/android/java/org/chromium/chrome/browser/crypto_wallet/activities/SelectPurchaseMethodActivity.java
@@ -0,0 +1,156 @@
+/* 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.activities;
+
+import static org.chromium.chrome.browser.crypto_wallet.util.Utils.warnWhenError;
+
+import android.content.Context;
+import android.content.Intent;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+
+import androidx.annotation.StringRes;
+import androidx.appcompat.widget.Toolbar;
+
+import org.chromium.brave_wallet.mojom.OnRampProvider;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.app.BraveActivity;
+import org.chromium.chrome.browser.app.domain.BuyModel;
+import org.chromium.chrome.browser.app.domain.WalletModel;
+import org.chromium.chrome.browser.util.LiveDataUtil;
+import org.chromium.chrome.browser.util.TabUtils;
+
+public class SelectPurchaseMethodActivity extends BraveWalletBaseActivity {
+ private static final String CHAIN_ID = "chainId";
+ private static final String FROM = "from";
+ private static final String ASSET_SYMBOL = "assetSymbol";
+ private static final String CONTRACT_ADDRESS = "contractAddress";
+ private static final String AMOUNT = "amount";
+
+ private BuyModel mBuyModel;
+ private WalletModel mWalletModel;
+
+ private String mChainId;
+ private String mFrom;
+ private String mAssetSymbol;
+ private String mContractAddress;
+ private String mAmount;
+
+ private ViewGroup mRampNetworkLayout;
+ private ViewGroup mSardineLayout;
+ private ViewGroup mTransakLayout;
+ private Button mRampButton;
+ private Button mSardineButton;
+ private Button mTransakButton;
+
+ @Override
+ protected void triggerLayoutInflation() {
+ setContentView(R.layout.activity_select_purchase_method);
+
+ Intent intent = getIntent();
+ if (intent != null) {
+ mChainId = intent.getStringExtra(CHAIN_ID);
+ mFrom = intent.getStringExtra(FROM);
+ mAssetSymbol = intent.getStringExtra(ASSET_SYMBOL);
+ mContractAddress = intent.getStringExtra(CONTRACT_ADDRESS);
+ mAmount = intent.getStringExtra(AMOUNT);
+ }
+
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ mRampNetworkLayout = findViewById(R.id.ramp_network_container);
+ mSardineLayout = findViewById(R.id.sardine_container);
+ mTransakLayout = findViewById(R.id.transak_container);
+
+ mRampButton = findViewById(R.id.purchase_method_btn_ramp);
+ mRampButton.setText(getRampProviderTextButton(R.string.brave_wallet_ramp_network_short));
+ mSardineButton = findViewById(R.id.purchase_method_btn_sardine);
+ mSardineButton.setText(getRampProviderTextButton(R.string.brave_wallet_sardine_title));
+ mTransakButton = findViewById(R.id.purchase_method_btn_transak);
+ mTransakButton.setText(getRampProviderTextButton(R.string.brave_wallet_transak_title));
+
+ onInitialLayoutInflationComplete();
+ }
+
+ @Override
+ public void finishNativeInitialization() {
+ super.finishNativeInitialization();
+
+ BraveActivity activity = BraveActivity.getBraveActivity();
+ if (activity != null) {
+ mWalletModel = activity.getWalletModel();
+ if (mWalletModel != null && mWalletModel.getCryptoModel() != null) {
+ mBuyModel = mWalletModel.getCryptoModel().getBuyModel();
+ }
+ }
+ if (mWalletModel != null && mBuyModel != null) {
+ LiveDataUtil.observeOnce(
+ mWalletModel.getCryptoModel().getNetworkModel().mDefaultNetwork,
+ selectedNetwork -> {
+ mBuyModel.isBuySupported(selectedNetwork, mAssetSymbol, mContractAddress,
+ mChainId, new int[] {OnRampProvider.RAMP}, isBuyEnabled -> {
+ setupOnRampService(isBuyEnabled, OnRampProvider.RAMP,
+ mRampNetworkLayout, mRampButton);
+ });
+
+ mBuyModel.isBuySupported(selectedNetwork, mAssetSymbol, mContractAddress,
+ mChainId, new int[] {OnRampProvider.SARDINE}, isBuyEnabled -> {
+ setupOnRampService(isBuyEnabled, OnRampProvider.SARDINE,
+ mSardineLayout, mSardineButton);
+ });
+
+ mBuyModel.isBuySupported(selectedNetwork, mAssetSymbol, mContractAddress,
+ mChainId, new int[] {OnRampProvider.TRANSAK}, isBuyEnabled -> {
+ setupOnRampService(isBuyEnabled, OnRampProvider.TRANSAK,
+ mTransakLayout, mTransakButton);
+ });
+ });
+ }
+ }
+
+ private String getRampProviderTextButton(@StringRes int providerNameResource) {
+ return String.format(getString(R.string.brave_wallet_buy_with_ramp_provider),
+ getString(providerNameResource));
+ }
+
+ private void setupOnRampService(
+ boolean isBuyEnabled, int onRampProvider, ViewGroup onRampLayout, Button onRampButton) {
+ if (isBuyEnabled && mBuyModel.isAvailable(onRampProvider, getResources())) {
+ onRampLayout.setVisibility(View.VISIBLE);
+ mBuyModel.getBuyUrl(onRampProvider, mChainId, mFrom, mAssetSymbol, mAmount,
+ mContractAddress,
+ url -> { enableOnRampService(onRampLayout, onRampButton, url); });
+ }
+ }
+
+ private void enableOnRampService(
+ ViewGroup onRampLayout, Button onRampButton, String onRampUrl) {
+ if (onRampUrl == null) {
+ onRampLayout.setVisibility(View.GONE);
+ } else {
+ onRampButton.setOnClickListener(v -> {
+ TabUtils.openUrlInNewTab(false, onRampUrl);
+ TabUtils.bringChromeTabbedActivityToTheTop(this);
+ });
+ }
+ }
+
+ public static Intent getIntent(Context context, String chainId, String from,
+ String rampNetworkSymbol, String contractAddress, String amount) {
+ Intent intent = new Intent(context, SelectPurchaseMethodActivity.class);
+ intent.putExtra(CHAIN_ID, chainId);
+ intent.putExtra(FROM, from);
+ intent.putExtra(ASSET_SYMBOL, rampNetworkSymbol);
+ intent.putExtra(CONTRACT_ADDRESS, contractAddress);
+ intent.putExtra(AMOUNT, amount);
+ intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ return intent;
+ }
+}
diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/adapters/WalletCoinAdapter.java b/android/java/org/chromium/chrome/browser/crypto_wallet/adapters/WalletCoinAdapter.java
index 87438a7bd539..d7f65d7b8047 100644
--- a/android/java/org/chromium/chrome/browser/crypto_wallet/adapters/WalletCoinAdapter.java
+++ b/android/java/org/chromium/chrome/browser/crypto_wallet/adapters/WalletCoinAdapter.java
@@ -241,11 +241,11 @@ public void setWalletCoinAdapterType(AdapterType type) {
}
public void setWalletListItemModelList(List walletListItemModelList) {
- this.walletListItemModelList = walletListItemModelList;
+ this.walletListItemModelList = removeDuplicates(walletListItemModelList);
if (mType == AdapterType.EDIT_VISIBLE_ASSETS_LIST || mType == AdapterType.BUY_ASSETS_LIST
|| mType == AdapterType.SEND_ASSETS_LIST || mType == AdapterType.SWAP_TO_ASSETS_LIST
|| mType == AdapterType.SWAP_FROM_ASSETS_LIST) {
- walletListItemModelListCopy.addAll(walletListItemModelList);
+ walletListItemModelListCopy.addAll(this.walletListItemModelList);
mCheckedPositions.clear();
}
for (int i = 0; i < walletListItemModelListCopy.size(); i++) {
@@ -305,6 +305,43 @@ public void updateSelectedNetwork(String title, String subTitle) {
}
}
+ // Removing duplicates will allow the recycler viewer to render a clean list without showing the
+ // same assets multiple times. Currently, the list of available assets is fetched from Core
+ // API the returns a merged list containing the available assets per ramp provider.
+ // It's not unusual to have the same asset multiple times with the same contract address all
+ // upper case from a ramp provider and all lower case from another one. Thus it's important to
+ // compare the contract addresses ignoring case.
+ private List removeDuplicates(
+ List walletListItemModelList) {
+ List result = new ArrayList<>();
+ for (WalletListItemModel item : walletListItemModelList) {
+ if (item.getBlockchainToken() == null) {
+ // If blockchain token is null the item can be safely added without any risk
+ // of duplication.
+ result.add(item);
+ continue;
+ }
+ String contractAddress = item.getBlockchainToken().contractAddress;
+ boolean duplicate = false;
+ for (WalletListItemModel itemResult : result) {
+ // IMPORTANT: use `equalsIgnoreCase` to detect two contract addresses with different
+ // capitalization.
+ if (contractAddress.equalsIgnoreCase(
+ itemResult.getBlockchainToken().contractAddress)) {
+ // Duplicated item detected!
+ duplicate = true;
+ break;
+ }
+ }
+ // Do not add duplicated item.
+ if (!duplicate) {
+ result.add(item);
+ }
+ }
+
+ return result;
+ }
+
private void updateSelectedNetwork(int selectedAccountPosition) {
walletListItemModelList.get(previousSelectedPos).setIsUserSelected(false);
notifyItemChanged(previousSelectedPos);
diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/EditVisibleAssetsBottomSheetDialogFragment.java b/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/EditVisibleAssetsBottomSheetDialogFragment.java
index 955a3bada4fa..eeacf9f1e9bc 100644
--- a/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/EditVisibleAssetsBottomSheetDialogFragment.java
+++ b/android/java/org/chromium/chrome/browser/crypto_wallet/fragments/EditVisibleAssetsBottomSheetDialogFragment.java
@@ -49,6 +49,7 @@
import org.chromium.brave_wallet.mojom.NetworkInfo;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.app.BraveActivity;
+import org.chromium.chrome.browser.app.domain.BuyModel;
import org.chromium.chrome.browser.app.domain.WalletModel;
import org.chromium.chrome.browser.crypto_wallet.BlockchainRegistryFactory;
import org.chromium.chrome.browser.crypto_wallet.activities.BraveWalletBaseActivity;
@@ -278,7 +279,7 @@ public void onClick(View clickView) {
tokens -> { setUpAssetsList(view, tokens, new BlockchainToken[0]); });
} else if (mType == WalletCoinAdapter.AdapterType.BUY_ASSETS_LIST) {
TokenUtils.getBuyTokensFiltered(blockchainRegistry, mSelectedNetwork,
- TokenUtils.TokenType.ALL,
+ TokenUtils.TokenType.ALL, BuyModel.SUPPORTED_RAMP_PROVIDERS,
tokens -> { setUpAssetsList(view, tokens, new BlockchainToken[0]); });
}
}
diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/util/AssetUtils.java b/android/java/org/chromium/chrome/browser/crypto_wallet/util/AssetUtils.java
index a4561f2c00ea..373d519d6055 100644
--- a/android/java/org/chromium/chrome/browser/crypto_wallet/util/AssetUtils.java
+++ b/android/java/org/chromium/chrome/browser/crypto_wallet/util/AssetUtils.java
@@ -133,21 +133,21 @@ public static String getKeyringForCoinType(int coinType) {
return coin;
}
- public static String mapToRampNetworkSymbol(@NonNull BlockchainToken asset) {
- String assetChainId = asset.chainId;
- if (asset.symbol.equalsIgnoreCase("bat")
+ public static String mapToRampNetworkSymbol(
+ String assetChainId, String assetSymbol, String contractAddress) {
+ if (assetSymbol.equalsIgnoreCase("bat")
&& assetChainId.equals(BraveWalletConstants.MAINNET_CHAIN_ID)) {
// BAT is the only token on Ethereum Mainnet with a prefix on Ramp.Network
return "ETH_BAT";
} else if (assetChainId.equals(BraveWalletConstants.AVALANCHE_MAINNET_CHAIN_ID)
- && TextUtils.isEmpty(asset.contractAddress)) {
+ && TextUtils.isEmpty(contractAddress)) {
// AVAX native token has no prefix
- return asset.symbol;
+ return assetSymbol;
} else {
- String rampNetworkPrefix = getRampNetworkPrefix(asset.chainId);
+ String rampNetworkPrefix = getRampNetworkPrefix(assetChainId);
return TextUtils.isEmpty(rampNetworkPrefix)
- ? asset.symbol.toUpperCase(Locale.ENGLISH)
- : rampNetworkPrefix + "_" + asset.symbol.toUpperCase(Locale.ENGLISH);
+ ? assetSymbol.toUpperCase(Locale.ENGLISH)
+ : rampNetworkPrefix + "_" + assetSymbol.toUpperCase(Locale.ENGLISH);
}
}
diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/util/TokenUtils.java b/android/java/org/chromium/chrome/browser/crypto_wallet/util/TokenUtils.java
index 383a20854e60..73fd16a14dc7 100644
--- a/android/java/org/chromium/chrome/browser/crypto_wallet/util/TokenUtils.java
+++ b/android/java/org/chromium/chrome/browser/crypto_wallet/util/TokenUtils.java
@@ -120,9 +120,9 @@ public static void getUserOrAllTokensFiltered(BraveWalletService braveWalletServ
}
public static void getBuyTokensFiltered(BlockchainRegistry blockchainRegistry,
- NetworkInfo selectedNetwork, TokenType tokenType,
+ NetworkInfo selectedNetwork, TokenType tokenType, int[] rampProviders,
Callbacks.Callback1 callback) {
- blockchainRegistry.getBuyTokens(OnRampProvider.RAMP, selectedNetwork.chainId, tokens -> {
+ blockchainRegistry.getProvidersBuyTokens(rampProviders, selectedNetwork.chainId, tokens -> {
BlockchainToken[] filteredTokens =
filterTokens(selectedNetwork, tokens, tokenType, false);
Arrays.sort(filteredTokens, blockchainTokenComparatorPerGasOrBatType);
diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/util/WalletConstants.java b/android/java/org/chromium/chrome/browser/crypto_wallet/util/WalletConstants.java
index f0c27dc90a8c..3b91b4865bb0 100644
--- a/android/java/org/chromium/chrome/browser/crypto_wallet/util/WalletConstants.java
+++ b/android/java/org/chromium/chrome/browser/crypto_wallet/util/WalletConstants.java
@@ -10,6 +10,9 @@
public final class WalletConstants {
public static final long MILLI_SECOND = 1000;
+ // USD currency code used by on-ramp providers.
+ public static final String CURRENCY_CODE_USD = "USD";
+
// Android
public static final String LINE_SEPARATOR = "line.separator";
diff --git a/android/java/res/drawable-night/sardine_logo.xml b/android/java/res/drawable-night/sardine_logo.xml
new file mode 100644
index 000000000000..a1e04837cccc
--- /dev/null
+++ b/android/java/res/drawable-night/sardine_logo.xml
@@ -0,0 +1,9 @@
+
+
+
+
diff --git a/android/java/res/drawable/ramp_logo.xml b/android/java/res/drawable/ramp_logo.xml
new file mode 100644
index 000000000000..a37ca54bbea7
--- /dev/null
+++ b/android/java/res/drawable/ramp_logo.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
diff --git a/android/java/res/drawable/sardine_logo.xml b/android/java/res/drawable/sardine_logo.xml
new file mode 100644
index 000000000000..d7aba2dec039
--- /dev/null
+++ b/android/java/res/drawable/sardine_logo.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
diff --git a/android/java/res/drawable/transak_logo.xml b/android/java/res/drawable/transak_logo.xml
new file mode 100644
index 000000000000..40083a727628
--- /dev/null
+++ b/android/java/res/drawable/transak_logo.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/java/res/layout/activity_select_purchase_method.xml b/android/java/res/layout/activity_select_purchase_method.xml
new file mode 100644
index 000000000000..1b87841bb46d
--- /dev/null
+++ b/android/java/res/layout/activity_select_purchase_method.xml
@@ -0,0 +1,217 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/browser/ui/android/strings/android_brave_strings.grd b/browser/ui/android/strings/android_brave_strings.grd
index c17652ccbdfe..f09321608504 100644
--- a/browser/ui/android/strings/android_brave_strings.grd
+++ b/browser/ui/android/strings/android_brave_strings.grd
@@ -2041,7 +2041,7 @@ Are you sure you want to do this?
Balance:
- Continue to Ramp
+ Select Purchase Method
Brave Wallet
@@ -2091,6 +2091,30 @@ Are you sure you want to do this?
Yes
+
+ Select one of the following options
+
+
+ Buy crypto with Visa or Mastercard.
+
+
+ Instant buy with your bank account. Lower fees.
+
+
+ Ramp.Network
+
+
+ Ramp
+
+
+ Sardine
+
+
+ Transak
+
+
+ Buy with %1$s
+
Clear transaction & nonce info
diff --git a/components/brave_wallet/browser/blockchain_registry.cc b/components/brave_wallet/browser/blockchain_registry.cc
index 3b6ace7c324a..cd745880f5ba 100644
--- a/components/brave_wallet/browser/blockchain_registry.cc
+++ b/components/brave_wallet/browser/blockchain_registry.cc
@@ -7,6 +7,7 @@
#include
+#include "base/containers/flat_set.h"
#include "base/ranges/algorithm.h"
#include "base/strings/stringprintf.h"
#include "brave/components/brave_wallet/browser/brave_wallet_constants.h"
@@ -100,51 +101,62 @@ void BlockchainRegistry::GetAllTokens(const std::string& chain_id,
GetAllTokensCallback callback) {
const auto key = GetTokenListKey(coin, chain_id);
if (!token_list_map_.contains(key)) {
- std::move(callback).Run(
- std::vector());
+ std::move(callback).Run(std::vector());
return;
}
const auto& tokens = token_list_map_[key];
- std::vector tokens_copy(
- tokens.size());
+ std::vector tokens_copy(tokens.size());
std::transform(
tokens.begin(), tokens.end(), tokens_copy.begin(),
- [](const brave_wallet::mojom::BlockchainTokenPtr& current_token)
- -> brave_wallet::mojom::BlockchainTokenPtr {
- return current_token.Clone();
- });
+ [](const mojom::BlockchainTokenPtr& current_token)
+ -> mojom::BlockchainTokenPtr { return current_token.Clone(); });
std::move(callback).Run(std::move(tokens_copy));
}
-void BlockchainRegistry::GetBuyTokens(mojom::OnRampProvider provider,
- const std::string& chain_id,
- GetBuyTokensCallback callback) {
- std::vector blockchain_buy_tokens;
- const std::vector* buy_tokens = nullptr;
- if (provider == mojom::OnRampProvider::kWyre)
- buy_tokens = &GetWyreBuyTokens();
- else if (provider == mojom::OnRampProvider::kRamp)
- buy_tokens = &GetRampBuyTokens();
- else if (provider == mojom::OnRampProvider::kSardine)
- buy_tokens = &GetSardineBuyTokens();
- else if (provider == mojom::OnRampProvider::kTransak)
- buy_tokens = &GetTransakBuyTokens();
-
- if (buy_tokens == nullptr) {
- std::move(callback).Run(std::move(blockchain_buy_tokens));
- return;
- }
-
- for (const auto& token : *buy_tokens) {
- if (token.chain_id != chain_id) {
+std::vector BlockchainRegistry::GetBuyTokens(
+ const std::vector& providers,
+ const std::string& chain_id) {
+ std::vector blockchain_buy_tokens;
+ base::flat_set provider_set(providers.begin(),
+ providers.end());
+
+ for (const auto& provider : provider_set) {
+ const std::vector* buy_tokens = nullptr;
+ if (provider == mojom::OnRampProvider::kWyre) {
+ buy_tokens = &GetWyreBuyTokens();
+ } else if (provider == mojom::OnRampProvider::kRamp) {
+ buy_tokens = &GetRampBuyTokens();
+ } else if (provider == mojom::OnRampProvider::kSardine) {
+ buy_tokens = &GetSardineBuyTokens();
+ } else if (provider == mojom::OnRampProvider::kTransak) {
+ buy_tokens = &GetTransakBuyTokens();
+ } else {
continue;
}
- blockchain_buy_tokens.push_back(
- brave_wallet::mojom::BlockchainToken::New(token));
+ for (const auto& token : *buy_tokens) {
+ if (token.chain_id == chain_id) {
+ blockchain_buy_tokens.push_back(mojom::BlockchainToken::New(token));
+ }
+ }
}
- std::move(callback).Run(std::move(blockchain_buy_tokens));
+
+ return blockchain_buy_tokens;
}
+
+void BlockchainRegistry::GetBuyTokens(mojom::OnRampProvider provider,
+ const std::string& chain_id,
+ GetBuyTokensCallback callback) {
+ std::move(callback).Run(GetBuyTokens({provider}, chain_id));
+}
+
+void BlockchainRegistry::GetProvidersBuyTokens(
+ const std::vector& providers,
+ const std::string& chain_id,
+ GetProvidersBuyTokensCallback callback) {
+ std::move(callback).Run(GetBuyTokens(providers, chain_id));
+}
+
// TODO(muliswilliam) - Remove this function when iOS and Android no longer
// depend on it https://github.com/brave/brave-browser/issues/23503
void BlockchainRegistry::GetBuyUrl(mojom::OnRampProvider provider,
@@ -183,12 +195,12 @@ void BlockchainRegistry::GetBuyUrl(mojom::OnRampProvider provider,
void BlockchainRegistry::GetOnRampCurrencies(
GetOnRampCurrenciesCallback callback) {
- std::vector currencies;
+ std::vector currencies;
const std::vector* onRampCurrencies =
&GetOnRampCurrenciesList();
for (const auto& currency : *onRampCurrencies) {
- currencies.push_back(brave_wallet::mojom::OnRampCurrency::New(currency));
+ currencies.push_back(mojom::OnRampCurrency::New(currency));
}
std::move(callback).Run(std::move(currencies));
}
diff --git a/components/brave_wallet/browser/blockchain_registry.h b/components/brave_wallet/browser/blockchain_registry.h
index 75363223bdb0..07f376554f1f 100644
--- a/components/brave_wallet/browser/blockchain_registry.h
+++ b/components/brave_wallet/browser/blockchain_registry.h
@@ -54,6 +54,10 @@ class BlockchainRegistry : public mojom::BlockchainRegistry {
void GetBuyTokens(mojom::OnRampProvider provider,
const std::string& chain_id,
GetBuyTokensCallback callback) override;
+ void GetProvidersBuyTokens(
+ const std::vector& providers,
+ const std::string& chain_id,
+ GetProvidersBuyTokensCallback callback) override;
void GetBuyUrl(mojom::OnRampProvider provider,
const std::string& chain_id,
const std::string& address,
@@ -76,6 +80,9 @@ class BlockchainRegistry : public mojom::BlockchainRegistry {
private:
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 d4d4dfeb57ec..167d3cfad101 100644
--- a/components/brave_wallet/browser/blockchain_registry_unittest.cc
+++ b/components/brave_wallet/browser/blockchain_registry_unittest.cc
@@ -14,6 +14,8 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "brave/components/brave_wallet/browser/brave_wallet_constants.h"
+
using testing::ElementsAreArray;
namespace brave_wallet {
@@ -456,6 +458,46 @@ TEST(BlockchainRegistryUnitTest, GetBuyTokens) {
run_loop4.Run();
}
+TEST(BlockchainRegistryUnitTest, GetProvidersBuyTokens) {
+ base::test::TaskEnvironment task_environment;
+ auto* registry = BlockchainRegistry::GetInstance();
+
+ std::vector buy_tokens;
+ for (const auto& v : {GetWyreBuyTokens(), GetRampBuyTokens(),
+ GetSardineBuyTokens(), GetTransakBuyTokens()}) {
+ buy_tokens.insert(buy_tokens.end(), v.begin(), v.end());
+ }
+
+ const char* chains[] = {
+ mojom::kMainnetChainId, mojom::kPolygonMainnetChainId,
+ mojom::kAvalancheMainnetChainId, mojom::kBinanceSmartChainMainnetChainId,
+ mojom::kSolanaMainnet, mojom::kCeloMainnetChainId,
+ mojom::kArbitrumMainnetChainId};
+
+ for (auto* chain : chains) {
+ std::vector expected_tokens;
+ for (const auto& token : buy_tokens) {
+ if (token.chain_id == chain) {
+ expected_tokens.push_back(mojom::BlockchainToken::New(token));
+ }
+ }
+
+ base::RunLoop run_loop;
+ registry->GetProvidersBuyTokens(
+ {mojom::OnRampProvider::kWyre, mojom::OnRampProvider::kRamp,
+ mojom::OnRampProvider::kSardine, mojom::OnRampProvider::kTransak,
+ mojom::OnRampProvider::kTransak /* test duplicate provider */},
+ chain,
+ base::BindLambdaForTesting(
+ [&](std::vector token_list) {
+ EXPECT_EQ(expected_tokens, token_list) << chain;
+
+ run_loop.Quit();
+ }));
+ run_loop.Run();
+ }
+}
+
TEST(BlockchainRegistryUnitTest, GetBuyUrlWyre) {
base::test::TaskEnvironment task_environment;
auto* registry = BlockchainRegistry::GetInstance();
diff --git a/components/brave_wallet/common/brave_wallet.mojom b/components/brave_wallet/common/brave_wallet.mojom
index ced671391d60..bf4665f391b8 100644
--- a/components/brave_wallet/common/brave_wallet.mojom
+++ b/components/brave_wallet/common/brave_wallet.mojom
@@ -670,8 +670,10 @@ interface BlockchainRegistry {
GetAllTokens(string chain_id, CoinType coin) => (array tokens);
// Below APIs are Ethereum only for the moment.
- // Obtains all tokens for the Buy UI
+ // Obtains all tokens for a single provider for the Buy UI
GetBuyTokens(OnRampProvider provider, string chain_id) => (array tokens);
+ // Obtains all tokens for multiple providers for the Buy UI
+ GetProvidersBuyTokens(array providers, string chain_id) => (array tokens);
// Obtains the URL used for buying assets.
GetBuyUrl(OnRampProvider provider, string chain_id, string address, string symbol, string amount) => (string url, string? error);