Skip to content

Commit

Permalink
feat(wallet): implement ramp network support (uplift to 1.47.x) (#16598)
Browse files Browse the repository at this point in the history
  • Loading branch information
Pavneet Singh authored Jan 18, 2023
1 parent 952fb55 commit 969062e
Show file tree
Hide file tree
Showing 10 changed files with 187 additions and 53 deletions.
1 change: 1 addition & 0 deletions android/brave_java_sources.gni
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ brave_java_sources = [
"../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/util/BalanceHelper.java",
"../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/util/Blockies.java",
"../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/util/ItemOffsetDecoration.java",
"../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/util/JavaUtils.java",
"../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/util/KeystoreHelper.java",
"../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/util/NavigationItem.java",
"../../brave/android/java/org/chromium/chrome/browser/crypto_wallet/util/NetworkResponsesCollector.java",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ public void resetService(KeyringService keyringService, BraveWalletService brave
}
}

public List<AccountInfo> stripNoBuySwapAccounts(List<AccountInfo> accountInfos) {
public List<AccountInfo> stripNoSwapSupportedAccounts(List<AccountInfo> accountInfos) {
List<AccountInfo> accountInfosFiltered = new ArrayList<>();
for (AccountInfo accountInfo : accountInfos) {
if (accountInfo.coin != CoinType.SOL) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@
import org.chromium.chrome.browser.crypto_wallet.fragments.EditVisibleAssetsBottomSheetDialogFragment;
import org.chromium.chrome.browser.crypto_wallet.observers.ApprovedTxObserver;
import org.chromium.chrome.browser.crypto_wallet.util.AddressUtils;
import org.chromium.chrome.browser.crypto_wallet.util.AssetUtils;
import org.chromium.chrome.browser.crypto_wallet.util.JavaUtils;
import org.chromium.chrome.browser.crypto_wallet.util.TokenUtils;
import org.chromium.chrome.browser.crypto_wallet.util.Utils;
import org.chromium.chrome.browser.crypto_wallet.util.Validations;
Expand Down Expand Up @@ -114,6 +116,7 @@ public class BuySendSwapActivity extends BraveWalletBaseActivity
private boolean mInitialLayoutInflationComplete;

private int radioSlippageToleranceCheckedId;
private List<AccountInfo> mAllAccountInfos;
private List<AccountInfo> mAccountInfos;
private SendModel mSendModel;
private NetworkInfo[] mNetworks;
Expand Down Expand Up @@ -911,7 +914,7 @@ public void onTextChanged(CharSequence s, int start, int before, int count) {
String from = mCustomAccountAdapter.getAccountAddressAtPosition(
mAccountSpinner.getSelectedItemPosition());
// TODO(sergz): Some kind of validation that we have enough balance
String value = mFromValueText.getText().toString();
String amount = mFromValueText.getText().toString();
if (mActivityType == ActivityType.SEND) {
String to = mSendToAddrText.getText().toString();
if (to.isEmpty()) {
Expand All @@ -921,22 +924,22 @@ public void onTextChanged(CharSequence s, int start, int before, int count) {
if (mCurrentBlockchainToken == null
|| mCurrentBlockchainToken.contractAddress.isEmpty()) {
TxData data = Utils.getTxData("", "", "", to,
Utils.toHexWei(value, Utils.ETH_DEFAULT_DECIMALS), new byte[0]);
Utils.toHexWei(amount, Utils.ETH_DEFAULT_DECIMALS), new byte[0]);
sendTransaction(data, from, "", "");
} else if (mCurrentBlockchainToken.isErc20) {
addUnapprovedTransactionERC20(to,
Utils.toHexWei(value, mCurrentBlockchainToken.decimals), from,
Utils.toHexWei(amount, mCurrentBlockchainToken.decimals), from,
mCurrentBlockchainToken.contractAddress);
} else if (mCurrentBlockchainToken.isErc721) {
addUnapprovedTransactionERC721(from, to, mCurrentBlockchainToken.tokenId,
mCurrentBlockchainToken.contractAddress);
}
} else if (mSelectedAccount.coin == CoinType.SOL) {
if (mCurrentBlockchainToken.isNft) {
value = "1";
amount = "1";
}
mSendModel.sendSolanaToken(mCurrentBlockchainToken, mSelectedAccount.address,
to, Utils.toDecimalLamport(value, mCurrentBlockchainToken.decimals),
to, Utils.toDecimalLamport(amount, mCurrentBlockchainToken.decimals),
(success, txMetaId, errorMessage) -> {
// Do nothing here when success as we will receive an
// unapproved transaction in TxServiceObserver.
Expand All @@ -945,23 +948,19 @@ public void onTextChanged(CharSequence s, int start, int before, int count) {
setSendToFromValueValidationResult(errorMessage, false, true);
});
}
} else if (mActivityType == ActivityType.BUY
&& mSelectedNetwork.chainId.equals(BraveWalletConstants.MAINNET_CHAIN_ID)) {
// TODO(pav): Un-comment to reuse, while adding support for other providers
// assert mBlockchainRegistry != null;
// String asset = mFromAssetText.getText().toString();
// mBlockchainRegistry.getBuyUrl(,
// BraveWalletConstants.MAINNET_CHAIN_ID, from, asset, value,
// (url, error) -> {
// if (error != null && !error.isEmpty() &&
// Utils.isDebuggable(this)) {
// Log.e(TAG, "Could not get buy URL: " + error);
// return;
// }
//
// TabUtils.openUrlInNewTab(false, url);
// TabUtils.bringChromeTabbedActivityToTheTop(this);
// });
} 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;
}

TabUtils.openUrlInNewTab(false, url);
TabUtils.bringChromeTabbedActivityToTheTop(this);
});
} else if (mActivityType == ActivityType.SWAP) {
if (mCurrentBlockchainToken != null) {
String btnText = mBtnBuySendSwap.getText().toString();
Expand Down Expand Up @@ -1493,14 +1492,16 @@ private void initAccountsUI() {
if (mSelectedAccount == null || mSelectedNetwork == null) {
return;
}
if (mActivityType != ActivityType.SEND) {
mAccountInfos = mWalletModel.getKeyringModel().stripNoBuySwapAccounts(mAccountInfos);
if (mActivityType == ActivityType.SWAP) {
mAccountInfos =
mWalletModel.getKeyringModel().stripNoSwapSupportedAccounts(mAllAccountInfos);
} else if (mActivityType == ActivityType.BUY) {
mAccountInfos =
JavaUtils.filter(mAllAccountInfos, item -> item.coin == mSelectedNetwork.coin);
}

mCustomAccountAdapter.setAccounts(mAccountInfos);
if (mAccountInfos.size() > 0) {
String selectedAccountAddress = mSelectedAccount != null ? mSelectedAccount.address
: mAccountInfos.get(0).address;
mAccountSpinner.setSelection(
WalletUtils.getSelectedAccountIndex(mSelectedAccount, mAccountInfos));
}
Expand Down Expand Up @@ -1549,6 +1550,7 @@ mSelectedNetwork.symbol, getResources().getDisplayMetrics().density,
});
mWalletModel.getKeyringModel().mAccountAllAccountsPair.observe(
this, accountInfoListPair -> {
mAllAccountInfos = accountInfoListPair.second;
mSelectedAccount = accountInfoListPair.first;
mAccountInfos = accountInfoListPair.second;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,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.ERC20,
TokenUtils.TokenType.ALL,
tokens -> { setUpAssetsList(view, tokens, new BlockchainToken[0]); });
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,18 @@

import android.text.TextUtils;

import androidx.annotation.NonNull;

import org.chromium.brave_wallet.mojom.BlockchainToken;
import org.chromium.brave_wallet.mojom.BraveWalletConstants;
import org.chromium.brave_wallet.mojom.CoinType;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

public class AssetUtils {
public static String AURORA_SUPPORTED_CONTRACT_ADDRESSES[] = {
"0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9", // AAVE
Expand Down Expand Up @@ -94,4 +103,72 @@ public static String getKeyringForCoinType(int coinType) {
}
return coin;
}

public static String mapToRampNetworkSymbol(@NonNull BlockchainToken asset) {
String assetChainId = asset.chainId;
if (asset.symbol.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)) {
// AVAX native token has no prefix
return asset.symbol;
} else {
String rampNetworkPrefix = getRampNetworkPrefix(asset.chainId);
return TextUtils.isEmpty(rampNetworkPrefix)
? asset.symbol.toUpperCase(Locale.ENGLISH)
: rampNetworkPrefix + "_" + asset.symbol.toUpperCase(Locale.ENGLISH);
}
}

public static String getRampNetworkPrefix(String chainId) {
switch (chainId) {
case BraveWalletConstants.AVALANCHE_MAINNET_CHAIN_ID:
return "AVAXC";
case BraveWalletConstants.BINANCE_SMART_CHAIN_MAINNET_CHAIN_ID:
return "BSC";
case BraveWalletConstants.POLYGON_MAINNET_CHAIN_ID:
return "MATIC";
case BraveWalletConstants.SOLANA_MAINNET:
return "SOLANA";
case BraveWalletConstants.OPTIMISM_MAINNET_CHAIN_ID:
return "OPTIMISM";
// case BraveWalletConstants.FILECOIN_MAINNET: return "FILECOIN"; /*not
// supported yet*/
case BraveWalletConstants.MAINNET_CHAIN_ID:
case BraveWalletConstants.CELO_MAINNET_CHAIN_ID:
default:
return "";
}
}

public static boolean isNativeToken(BlockchainToken token) {
List<String> nativeTokens = NATIVE_TOKENS_PER_CHAIN.get(token.chainId);
if (nativeTokens != null) {
return nativeTokens.contains(token.symbol.toLowerCase(Locale.ENGLISH));
}
return false;
}

public static boolean isBatToken(BlockchainToken token) {
String symbol = token.symbol;
return symbol.equalsIgnoreCase("bat") || symbol.equalsIgnoreCase("wbat")
|| symbol.equalsIgnoreCase("bat.e");
}

private static final Map<String, List<String>> NATIVE_TOKENS_PER_CHAIN = new HashMap<>() {
{
put(BraveWalletConstants.MAINNET_CHAIN_ID, Arrays.asList("eth"));
put(BraveWalletConstants.OPTIMISM_MAINNET_CHAIN_ID, Arrays.asList("eth"));
put(BraveWalletConstants.AURORA_MAINNET_CHAIN_ID, Arrays.asList("eth"));
put(BraveWalletConstants.POLYGON_MAINNET_CHAIN_ID, Arrays.asList("matic"));
put(BraveWalletConstants.FANTOM_MAINNET_CHAIN_ID, Arrays.asList("ftm"));
put(BraveWalletConstants.CELO_MAINNET_CHAIN_ID, Arrays.asList("celo"));
put(BraveWalletConstants.BINANCE_SMART_CHAIN_MAINNET_CHAIN_ID, Arrays.asList("bnb"));
put(BraveWalletConstants.SOLANA_MAINNET, Arrays.asList("sol"));
put(BraveWalletConstants.FILECOIN_MAINNET, Arrays.asList("fil"));
put(BraveWalletConstants.AVALANCHE_MAINNET_CHAIN_ID, Arrays.asList("avax", "avaxc"));
}
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/* Copyright (c) 2022 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.Predicate;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class JavaUtils {
public static <T> List<T> safeVal(List<T> list) {
if (list == null) return Collections.emptyList();
return list;
}

@SuppressWarnings("unchecked")
public static <T> T[] safeVal(T[] arr) {
if (arr == null) return (T[]) new Object[0];
return arr;
}

public static <T> T safeVal(T object, T defValue) {
if (object == null) return defValue;
return object;
}

public static <T> List<T> filter(List<T> list, Predicate<T> filter) {
List<T> filteredList = new ArrayList<>();
for (T item : list) {
if (filter.test(item)) {
filteredList.add(item);
}
}
return filteredList;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;

Expand Down Expand Up @@ -121,13 +122,12 @@ public static void getUserOrAllTokensFiltered(BraveWalletService braveWalletServ
public static void getBuyTokensFiltered(BlockchainRegistry blockchainRegistry,
NetworkInfo selectedNetwork, TokenType tokenType,
Callbacks.Callback1<BlockchainToken[]> callback) {
callback.call(new BlockchainToken[0]);
// TODO(pav): Un-comment to reuse, while adding support for other providers
// blockchainRegistry.getBuyTokens(, selectedNetwork.chainId, tokens -> {
// BlockchainToken[] filteredTokens =
// filterTokens(selectedNetwork, tokens, tokenType, false);
// callback.call(filteredTokens);
// });
blockchainRegistry.getBuyTokens(OnRampProvider.RAMP, selectedNetwork.chainId, tokens -> {
BlockchainToken[] filteredTokens =
filterTokens(selectedNetwork, tokens, tokenType, false);
Arrays.sort(filteredTokens, blockchainTokenComparatorPerGasOrBatType);
callback.call(filteredTokens);
});
}

public static void isCustomToken(BlockchainRegistry blockchainRegistry,
Expand Down Expand Up @@ -200,4 +200,22 @@ public static void getExactUserAsset(BraveWalletService braveWalletService,
callback.onResult(resultToken);
});
}

private static Comparator<BlockchainToken> blockchainTokenComparatorPerGasOrBatType =
(token1, token2) -> {
boolean isNativeToken1 = AssetUtils.isNativeToken(token1);
boolean isNativeToken2 = AssetUtils.isNativeToken(token2);
boolean isBatToken1 = AssetUtils.isBatToken(token1);
boolean isBatToken2 = AssetUtils.isBatToken(token2);
if (isNativeToken1 && !isNativeToken2)
return -1;
else if (!isNativeToken1 && isNativeToken2)
return 1;
else if (isBatToken1 && !isBatToken2)
return -1;
else if (!isBatToken1 && isBatToken2)
return 1;
else
return token1.symbol.compareTo(token2.symbol);
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -1674,7 +1674,8 @@ public static double getBalanceForCoinType(int coinType, int decimals, String ba
}

public static boolean allowBuy(String chainId) {
return WalletConstants.BUY_SUPPORTED_NETWORKS.contains(chainId);
return !WalletConstants.KNOWN_TEST_CHAIN_IDS.contains(chainId)
&& WalletConstants.BUY_SUPPORTED_ONRAMP_NETWORKS.contains(chainId);
}

public static boolean allowSwap(String chainId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,22 +50,17 @@ public final class WalletConstants {
// To clear prefs while resetting wallet
public static final String[] BRAVE_WALLET_PREFS = {PREF_SHOW_BRIDGE_INFO_DIALOG};

// TODO(pav): We should maintain separate list of supported network list per provider so rename
// this accordingly. e.g. BUY_SUPPORTED_PROVIDER_NAME_NETWORKS
public static List<String> BUY_SUPPORTED_NETWORKS = Collections.emptyList();
// Arrays.asList(BraveWalletConstants.MAINNET_CHAIN_ID);
// BraveWalletConstants.RINKEBY_CHAIN_ID,
// BraveWalletConstants.ROPSTEN_CHAIN_ID, BraveWalletConstants.GOERLI_CHAIN_ID,
// BraveWalletConstants.KOVAN_CHAIN_ID, BraveWalletConstants.LOCALHOST_CHAIN_ID
// BraveWalletConstants.POLYGON_MAINNET_CHAIN_ID, /* not yet supported */
// BraveWalletConstants.AVALANCHE_MAINNET_CHAIN_ID, /* not yet supported */
// BraveWalletConstants.AURORA_MAINNET_CHAIN_ID, /* not yet supported */
// BraveWalletConstants.OPTIMISM_MAINNET_CHAIN_ID, /* disabled until Ramp support */
// BraveWalletConstants.CELO_MAINNET_CHAIN_ID, /* disabled until Ramp support */
// BraveWalletConstants.BINANCE_SMART_CHAIN_MAINNET_CHAIN_ID, /* disabled until Ramp
// support */ BraveWalletConstants.FANTOM_MAINNET_CHAIN_ID, /* not yet supported */
// BraveWalletConstants.SOLANA_MAINNET, /* not yet supported */
// BraveWalletConstants.FILECOIN_MAINNET); , /* not yet supported */
public static List<String> BUY_SUPPORTED_ONRAMP_NETWORKS = Arrays.asList(
BraveWalletConstants.SOLANA_MAINNET, BraveWalletConstants.MAINNET_CHAIN_ID,
BraveWalletConstants.POLYGON_MAINNET_CHAIN_ID,
BraveWalletConstants.BINANCE_SMART_CHAIN_MAINNET_CHAIN_ID,
BraveWalletConstants.CELO_MAINNET_CHAIN_ID,
BraveWalletConstants.AVALANCHE_MAINNET_CHAIN_ID,
// BraveWalletConstants.FANTOM_MAINNET_CHAIN_ID, /* not yet supported */
// BraveWalletConstants.AURORA_MAINNET_CHAIN_ID, /* not yet supported */
// BraveWalletConstants.FILECOIN_MAINNET, /* not yet supported */
BraveWalletConstants.OPTIMISM_MAINNET_CHAIN_ID,
BraveWalletConstants.ARBITRUM_MAINNET_CHAIN_ID);

public static List<String> SWAP_SUPPORTED_NETWORKS = Arrays.asList(
BraveWalletConstants.MAINNET_CHAIN_ID, BraveWalletConstants.GOERLI_CHAIN_ID);
Expand Down
Loading

0 comments on commit 969062e

Please sign in to comment.