From eebf518969bb12b95cd0ae935e7b0bde7625c65f Mon Sep 17 00:00:00 2001 From: Simone Arpe Date: Tue, 13 Jun 2023 16:57:17 +0200 Subject: [PATCH 01/17] Enable Filecoin support flag --- .../brave_wallet/keyring_service_unittest.cc | 43 ------------------- components/brave_wallet/common/features.cc | 7 +-- 2 files changed, 1 insertion(+), 49 deletions(-) diff --git a/browser/brave_wallet/keyring_service_unittest.cc b/browser/brave_wallet/keyring_service_unittest.cc index 66f76ab80b71..ff33bff275b9 100644 --- a/browser/brave_wallet/keyring_service_unittest.cc +++ b/browser/brave_wallet/keyring_service_unittest.cc @@ -2516,7 +2516,6 @@ TEST_F(KeyringServiceUnitTest, SelectAddedFilecoinAccount) { KeyringService service(json_rpc_service(), GetPrefs(), GetLocalState()); ASSERT_TRUE(CreateWallet(&service, "brave")); -#if !BUILDFLAG(IS_ANDROID) AddAccount(&service, mojom::CoinType::FIL, mojom::kFilecoinKeyringId, "fil acc 1"); AddAccount(&service, mojom::CoinType::FIL, mojom::kFilecoinKeyringId, @@ -2544,12 +2543,6 @@ TEST_F(KeyringServiceUnitTest, SelectAddedFilecoinAccount) { ASSERT_EQ(GetFilecoinSelectedAccount(&service, mojom::kFilecoinTestnet), keyring_info->account_infos[2]->address); })); -#else - ASSERT_FALSE(AddAccount(&service, mojom::CoinType::FIL, - mojom::kFilecoinKeyringId, "fil acc 1")); - ASSERT_FALSE(AddAccount(&service, mojom::CoinType::FIL, - mojom::kFilecoinTestnetKeyringId, "fil acc 3")); -#endif } TEST_F(KeyringServiceUnitTest, SelectImportedFilecoinAccount) { @@ -2557,7 +2550,6 @@ TEST_F(KeyringServiceUnitTest, SelectImportedFilecoinAccount) { ASSERT_TRUE(CreateWallet(&service, "brave")); ASSERT_FALSE(service.IsLocked(mojom::kDefaultKeyringId)); -#if !BUILDFLAG(IS_ANDROID) ImportFilecoinAccount(&service, "fil m acc 1", "7b2254797065223a22736563703235366b31222c22507269766174" "654b6579223a224169776f6a344469323155316844776835735348" @@ -2594,22 +2586,6 @@ TEST_F(KeyringServiceUnitTest, SelectImportedFilecoinAccount) { ASSERT_EQ(GetFilecoinSelectedAccount(&service, mojom::kFilecoinTestnet), keyring_info->account_infos[1]->address); })); -#else - ASSERT_EQ(absl::nullopt, - ImportFilecoinAccount( - &service, "fil m acc 1", - "7b2254797065223a22736563703235366b31222c22507269766174" - "654b6579223a224169776f6a344469323155316844776835735348" - "434d7a37342b346c45303472376e5349454d706d6258493d227d", - mojom::kFilecoinMainnet)); - ASSERT_EQ(absl::nullopt, - ImportFilecoinAccount( - &service, "fil t acc 2", - "7b2254797065223a22736563703235366b31222c22507269766174" - "654b6579223a224169776f6a344469323155316844776835735348" - "434d7a37342b346c45303472376e5349454d706d6258493d227d", - mojom::kFilecoinTestnet)); -#endif // !BUILDFLAG(IS_ANDROID) } TEST_F(KeyringServiceUnitTest, SelectImportedAccount) { @@ -3237,13 +3213,8 @@ TEST_F(KeyringServiceUnitTest, AddFilecoinAccounts) { KeyringService service(json_rpc_service(), GetPrefs(), GetLocalState()); { ASSERT_TRUE(CreateWallet(&service, "brave")); -#if BUILDFLAG(IS_ANDROID) - ASSERT_FALSE(AddAccount(&service, mojom::CoinType::FIL, - mojom::kFilecoinTestnetKeyringId, "FIL account1")); -#else ASSERT_TRUE(AddAccount(&service, mojom::CoinType::FIL, mojom::kFilecoinTestnetKeyringId, "FIL account1")); -#endif service.Reset(); } @@ -3572,11 +3543,7 @@ TEST_F(KeyringServiceUnitTest, PreCreateEncryptors) { ASSERT_TRUE(CreateWallet(&service, "brave")); EXPECT_NE(service.encryptors_.at(mojom::kDefaultKeyringId), nullptr); EXPECT_NE(service.encryptors_.at(mojom::kSolanaKeyringId), nullptr); -#if BUILDFLAG(IS_ANDROID) - EXPECT_FALSE(service.encryptors_.contains(mojom::kFilecoinKeyringId)); -#else EXPECT_NE(service.encryptors_.at(mojom::kFilecoinKeyringId), nullptr); -#endif } { // Create wallet with enabled filecoin & solana @@ -3597,11 +3564,7 @@ TEST_F(KeyringServiceUnitTest, PreCreateEncryptors) { ASSERT_TRUE(CreateWallet(&service, "brave")); EXPECT_NE(service.encryptors_.at(mojom::kDefaultKeyringId), nullptr); EXPECT_NE(service.encryptors_.at(mojom::kSolanaKeyringId), nullptr); -#if BUILDFLAG(IS_ANDROID) - EXPECT_FALSE(service.encryptors_.contains(mojom::kFilecoinKeyringId)); -#else EXPECT_NE(service.encryptors_.at(mojom::kFilecoinKeyringId), nullptr); -#endif service.Lock(); base::test::ScopedFeatureList local_feature_list; local_feature_list.InitWithFeatures({features::kBraveWalletFilecoinFeature, @@ -3625,11 +3588,7 @@ TEST_F(KeyringServiceUnitTest, PreCreateEncryptors) { RestoreWallet(&service, *mnemonic_to_be_restored, "brave", false)); EXPECT_NE(service.encryptors_.at(mojom::kDefaultKeyringId), nullptr); EXPECT_NE(service.encryptors_.at(mojom::kSolanaKeyringId), nullptr); -#if BUILDFLAG(IS_ANDROID) - EXPECT_FALSE(service.encryptors_.contains(mojom::kFilecoinKeyringId)); -#else EXPECT_NE(service.encryptors_.at(mojom::kFilecoinKeyringId), nullptr); -#endif base::test::ScopedFeatureList local_feature_list; base::FieldTrialParams local_parameters; @@ -4071,7 +4030,6 @@ TEST_F(KeyringServiceAccountDiscoveryUnitTest, SolAccountDiscovery) { EXPECT_THAT(requested_addresses, ElementsAreArray(&saved_addresses()[1], 30)); } -#if !BUILDFLAG(IS_ANDROID) TEST_F(KeyringServiceAccountDiscoveryUnitTest, FilAccountDiscovery) { PrepareAccounts(mojom::CoinType::FIL, mojom::kFilecoinKeyringId); @@ -4107,7 +4065,6 @@ TEST_F(KeyringServiceAccountDiscoveryUnitTest, FilAccountDiscovery) { // 20 attempts more after Account 10 is added. EXPECT_THAT(requested_addresses, ElementsAreArray(&saved_addresses()[0], 30)); } -#endif // !BUILDFLAG(IS_ANDROID) TEST_F(KeyringServiceAccountDiscoveryUnitTest, StopsOnError) { PrepareAccounts(mojom::CoinType::ETH, mojom::kDefaultKeyringId); diff --git a/components/brave_wallet/common/features.cc b/components/brave_wallet/common/features.cc index 3ef0a2025fc7..b469cc4d6673 100644 --- a/components/brave_wallet/common/features.cc +++ b/components/brave_wallet/common/features.cc @@ -19,12 +19,7 @@ const base::FeatureParam kShowToolbarTxStatus{ BASE_FEATURE(kBraveWalletFilecoinFeature, "BraveWalletFilecoin", -#if BUILDFLAG(IS_ANDROID) - base::FEATURE_DISABLED_BY_DEFAULT -#else - base::FEATURE_ENABLED_BY_DEFAULT -#endif -); + base::FEATURE_ENABLED_BY_DEFAULT); BASE_FEATURE(kBraveWalletSolanaFeature, "BraveWalletSolana", From dc80a7fab9b4f0361a144c8ae8433e15a4921a06 Mon Sep 17 00:00:00 2001 From: Simone Arpe Date: Wed, 14 Jun 2023 15:19:46 +0200 Subject: [PATCH 02/17] Expose util method and add Javadoc --- .../browser/crypto_wallet/util/Utils.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/util/Utils.java b/android/java/org/chromium/chrome/browser/crypto_wallet/util/Utils.java index 7673ad4f4a30..d8a15b87d85f 100644 --- a/android/java/org/chromium/chrome/browser/crypto_wallet/util/Utils.java +++ b/android/java/org/chromium/chrome/browser/crypto_wallet/util/Utils.java @@ -424,8 +424,20 @@ public static double fromHexGWeiToGWEI(String number) { return 0; } - // Supposedly toWei shall always end up with an integer - private static BigInteger toWeiInternal(String number, int decimals) throws ParseException { + /** + * Converts a given string to a big integer and multiplies the value of the object by + * ten raised to the power of decimals. + * + * @param number Number to be multiplied, represented as a string. + * @param decimals Number of decimal to multiply by + * @return The result of multiplying the number by ten raised to the power of decimals, + * expressed as a BigInteger. + * @throws ParseException If the input string cannot be parsed as a BigDecimal. + * + * Note:: Supposedly, when converting to Wei the result shall always end up with an + * integer. + */ + public static BigInteger multiplyByDecimals(String number, int decimals) throws ParseException { NumberFormat nf = NumberFormat.getInstance(Locale.getDefault()); ParsePosition parsePosition = new ParsePosition(0); BigDecimal parsed = null; @@ -466,7 +478,7 @@ public static String toHexWei(String number, int decimals) { } try { - return "0x" + toWeiInternal(number, decimals).toString(16); + return "0x" + multiplyByDecimals(number, decimals).toString(16); } catch (ParseException ex) { return "0x0"; } From 308e694f2d6665f6a0a1ac46ebe6ee4878d0f503 Mon Sep 17 00:00:00 2001 From: Simone Arpe Date: Thu, 15 Jun 2023 12:03:26 +0200 Subject: [PATCH 03/17] Implement send logic for Filecoin --- .../chrome/browser/app/domain/SendModel.java | 50 +++++++++++++++++++ .../activities/BuySendSwapActivity.java | 11 +++- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/android/java/org/chromium/chrome/browser/app/domain/SendModel.java b/android/java/org/chromium/chrome/browser/app/domain/SendModel.java index 413d7e1b6350..8d18534b49c3 100644 --- a/android/java/org/chromium/chrome/browser/app/domain/SendModel.java +++ b/android/java/org/chromium/chrome/browser/app/domain/SendModel.java @@ -7,19 +7,30 @@ import android.text.TextUtils; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.chromium.base.Log; import org.chromium.brave_wallet.mojom.BlockchainRegistry; import org.chromium.brave_wallet.mojom.BlockchainToken; import org.chromium.brave_wallet.mojom.BraveWalletService; import org.chromium.brave_wallet.mojom.CoinType; import org.chromium.brave_wallet.mojom.EthTxManagerProxy; +import org.chromium.brave_wallet.mojom.FilTxData; import org.chromium.brave_wallet.mojom.JsonRpcService; import org.chromium.brave_wallet.mojom.KeyringService; import org.chromium.brave_wallet.mojom.SolanaTxManagerProxy; +import org.chromium.brave_wallet.mojom.TxDataUnion; import org.chromium.brave_wallet.mojom.TxService; +import org.chromium.chrome.browser.crypto_wallet.util.Utils; import org.chromium.chrome.browser.crypto_wallet.util.WalletUtils; import org.chromium.mojo.bindings.Callbacks; +import java.text.ParseException; + public class SendModel { + private static final String TAG = "SendModel"; + private TxService mTxService; private KeyringService mKeyringService; private BlockchainRegistry mBlockchainRegistry; @@ -94,4 +105,43 @@ public void sendSolanaToken(String chainId, BlockchainToken token, String fromAd }); } } + + public void sendFilecoinToken(@NonNull final String chainId, + @Nullable final BlockchainToken token, @NonNull final String fromAddress, + @NonNull final String toAddress, @NonNull final String amount, + @NonNull final Callbacks.Callback3 callback) { + if (token == null) { + Log.e(TAG, "Token cannot be null."); + return; + } + if (TextUtils.isEmpty(token.contractAddress)) { + Log.e(TAG, "Contract address cannot be null or empty."); + return; + } + if (TextUtils.isEmpty(amount)) { + Log.e(TAG, "Amount to send cannot be null or empty."); + return; + } + + if (token.coin != CoinType.FIL) { + throw new IllegalStateException(); + } + + final FilTxData filTxData = new FilTxData(); + filTxData.to = toAddress; + filTxData.from = fromAddress; + try { + filTxData.value = Utils.multiplyByDecimals(amount, token.decimals).toString(); + } catch (ParseException parseException) { + Log.e(TAG, "Error while parsing Filecoin amount to send.", parseException); + return; + } + final TxDataUnion txDataUnion = new TxDataUnion(); + txDataUnion.setFilTxData(filTxData); + + mTxService.addUnapprovedTransaction( + txDataUnion, fromAddress, null, null, (success, txMetaId, errorMessage) -> { + callback.call(success, txMetaId, errorMessage); + }); + } } 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 e9a5f8f061ab..24c22071eeac 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 @@ -549,8 +549,15 @@ public void onAssetClick(WalletListItemModel asset) { setSendToFromValueValidationResult(errorMessage, false, true); }); } else if (mSelectedAccount.accountId.coin == CoinType.FIL) { - // TODO: implement Filecoin send action. - // https://github.com/brave/brave-browser/issues/30402 + mSendModel.sendFilecoinToken(mSelectedNetwork.chainId, mCurrentBlockchainToken, + mSelectedAccount.address, to, amount, + (success, txMetaId, errorMessage) -> { + // Do nothing here when success as we will receive an + // unapproved transaction in TxServiceObserver. + // When we have error, let the user know, + // error_message is localized, do not disable send button + setSendToFromValueValidationResult(errorMessage, false, true); + }); } } else if (mActivityType == ActivityType.BUY) { Intent selectPurchaseMethodIntent = SelectPurchaseMethodActivity.getIntent(this, From 6cb3ccb103fd0be2ddffdc7576d40f0ae0ba3c45 Mon Sep 17 00:00:00 2001 From: Simone Arpe Date: Thu, 15 Jun 2023 13:33:07 +0200 Subject: [PATCH 04/17] Fix contract address --- .../java/org/chromium/chrome/browser/app/domain/SendModel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/java/org/chromium/chrome/browser/app/domain/SendModel.java b/android/java/org/chromium/chrome/browser/app/domain/SendModel.java index 8d18534b49c3..9cff7f476f07 100644 --- a/android/java/org/chromium/chrome/browser/app/domain/SendModel.java +++ b/android/java/org/chromium/chrome/browser/app/domain/SendModel.java @@ -114,7 +114,7 @@ public void sendFilecoinToken(@NonNull final String chainId, Log.e(TAG, "Token cannot be null."); return; } - if (TextUtils.isEmpty(token.contractAddress)) { + if (TextUtils.isEmpty(fromAddress)) { Log.e(TAG, "Contract address cannot be null or empty."); return; } From 833f6e3b3127ac2e9e19aae90f79fb881b1bb454 Mon Sep 17 00:00:00 2001 From: Simone Arpe Date: Thu, 15 Jun 2023 13:47:43 +0200 Subject: [PATCH 05/17] Prepopulate FilTxData --- .../org/chromium/chrome/browser/app/domain/SendModel.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/android/java/org/chromium/chrome/browser/app/domain/SendModel.java b/android/java/org/chromium/chrome/browser/app/domain/SendModel.java index 9cff7f476f07..c074d7cb645f 100644 --- a/android/java/org/chromium/chrome/browser/app/domain/SendModel.java +++ b/android/java/org/chromium/chrome/browser/app/domain/SendModel.java @@ -136,6 +136,12 @@ public void sendFilecoinToken(@NonNull final String chainId, Log.e(TAG, "Error while parsing Filecoin amount to send.", parseException); return; } + filTxData.nonce = ""; + filTxData.gasPremium = ""; + filTxData.gasFeeCap = ""; + filTxData.gasLimit = ""; + filTxData.maxFee = "0"; + final TxDataUnion txDataUnion = new TxDataUnion(); txDataUnion.setFilTxData(filTxData); From 0f57e0275110d17493112f8303389fa90f3c06a4 Mon Sep 17 00:00:00 2001 From: Simone Arpe Date: Fri, 16 Jun 2023 15:56:23 +0200 Subject: [PATCH 06/17] Calculate gas fee for Filecoins Calculates gas fee for Filecoins and adjust amount. --- .../crypto_wallet/util/ParsedTransaction.java | 2 +- .../util/ParsedTransactionFees.java | 73 +++++++++++++++---- 2 files changed, 59 insertions(+), 16 deletions(-) diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/util/ParsedTransaction.java b/android/java/org/chromium/chrome/browser/crypto_wallet/util/ParsedTransaction.java index 39eba95ac812..4e96e35701ad 100644 --- a/android/java/org/chromium/chrome/browser/crypto_wallet/util/ParsedTransaction.java +++ b/android/java/org/chromium/chrome/browser/crypto_wallet/util/ParsedTransaction.java @@ -159,7 +159,7 @@ public static ParsedTransaction parseTransaction(TransactionInfo txInfo, final String value = isSPLTransaction ? solTxData != null ? String.valueOf(solTxData.amount) : "" : isSolTransaction ? solTxData != null ? String.valueOf(solTxData.lamports) : "" - : isFilTransaction ? filTxData.value != null ? filTxData.value : "" + : isFilTransaction ? filTxData.value != null ? Utils.toHex(filTxData.value) : "" : txData != null ? txData.baseData.value : ""; diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/util/ParsedTransactionFees.java b/android/java/org/chromium/chrome/browser/crypto_wallet/util/ParsedTransactionFees.java index 25ebd89fadd7..ec9062c6ea5f 100644 --- a/android/java/org/chromium/chrome/browser/crypto_wallet/util/ParsedTransactionFees.java +++ b/android/java/org/chromium/chrome/browser/crypto_wallet/util/ParsedTransactionFees.java @@ -7,15 +7,19 @@ import static org.chromium.chrome.browser.crypto_wallet.util.WalletConstants.SOLANA_TRANSACTION_TYPES; +import androidx.annotation.NonNull; + import org.chromium.brave_wallet.mojom.FilTxData; import org.chromium.brave_wallet.mojom.NetworkInfo; import org.chromium.brave_wallet.mojom.TransactionInfo; import org.chromium.brave_wallet.mojom.TxData1559; import org.chromium.brave_wallet.mojom.TxDataUnion; +import java.math.BigInteger; + /* - * Transaction fees parser. Java version of - * components/brave_wallet_ui/common/hooks/transaction-parser.ts. + * Transaction fees parser. Java version adapted from + * components/brave_wallet_ui/utils/tx-utils.ts. */ public class ParsedTransactionFees { // Strings are initialized to empty string instead of null, as the default value from mojo @@ -104,11 +108,27 @@ public static ParsedTransactionFees parseTransactionFees(TransactionInfo txInfo, final int networkDecimals = selectedNetwork.decimals; final boolean isSolTransaction = SOLANA_TRANSACTION_TYPES.contains(txInfo.txType); final boolean isFilTransaction = filTxData != null; - final String gasLimit = isFilTransaction ? filTxData.gasLimit - : (txData != null ? txData.baseData.gasLimit : ""); - final String gasPrice = txData != null ? txData.baseData.gasPrice : ""; - final String maxFeePerGas = txData != null ? txData.maxFeePerGas : ""; - final String maxPriorityFeePerGas = txData != null ? txData.maxPriorityFeePerGas : ""; + + final String gasLimit; + final String gasPrice; + final String maxFeePerGas; + final String maxPriorityFeePerGas; + if (isFilTransaction) { + final String maxFeePerGasDecimal = + filTxData.gasFeeCap != null ? filTxData.gasFeeCap : ""; + final String maxPriorityFeePerGasDecimal = + filTxData.gasPremium != null ? filTxData.gasPremium : ""; + + gasLimit = filTxData.gasLimit != null ? Utils.toHex(filTxData.gasLimit) : ""; + maxFeePerGas = Utils.toHex(maxFeePerGasDecimal); + maxPriorityFeePerGas = Utils.toHex(maxPriorityFeePerGasDecimal); + gasPrice = calculateFilGasPrice(maxFeePerGasDecimal, maxPriorityFeePerGasDecimal); + } else { + gasLimit = txData != null ? txData.baseData.gasLimit : ""; + gasPrice = txData != null ? txData.baseData.gasPrice : ""; + maxFeePerGas = txData != null ? txData.maxFeePerGas : ""; + maxPriorityFeePerGas = txData != null ? txData.maxPriorityFeePerGas : ""; + } final boolean isEIP1559Transaction = !maxPriorityFeePerGas.isEmpty() && !maxFeePerGas.isEmpty(); final double[] gasFeeArr = @@ -130,20 +150,43 @@ public static ParsedTransactionFees parseTransactionFees(TransactionInfo txInfo, return parsedTransactionFees; } + @NonNull + private static String calculateFilGasPrice(@NonNull final String maxFeePerGasDecimal, + @NonNull final String maxPriorityFeePerGasDecimal) { + if (maxFeePerGasDecimal.isEmpty() || maxPriorityFeePerGasDecimal.isEmpty()) { + return ""; + } else { + String result; + try { + BigInteger minued = new BigInteger(maxFeePerGasDecimal); + BigInteger subtrahend = new BigInteger(maxPriorityFeePerGasDecimal); + BigInteger gasPrice = minued.subtract(subtrahend); + result = "0x" + gasPrice.toString(16); + } catch (NumberFormatException nfe) { + result = ""; + } + return result; + } + } + // Desktop doesn't seem to have slow/avg/fast MaxFeePerGas options, // so extracting this part as a separate function public static double[] calcGasFee(NetworkInfo selectedNetwork, double networkSpotPrice, boolean isEIP1559Transaction, String gasLimit, String gasPrice, String maxFeePerGas, boolean isSolTransaction, long solFeeEstimatesFee) { final int networkDecimals = selectedNetwork.decimals; - final double gasFee = isSolTransaction - ? solFeeEstimatesFee != 0 - ? Utils.fromWei(Long.toString(solFeeEstimatesFee), networkDecimals) - : 0.0d - : Utils.fromHexWei(isEIP1559Transaction - ? Utils.multiplyHexBN(maxFeePerGas, gasLimit) - : Utils.multiplyHexBN(gasPrice, gasLimit), - networkDecimals); + double gasFee; + if (isSolTransaction) { + gasFee = solFeeEstimatesFee != 0 + ? Utils.fromWei(Long.toString(solFeeEstimatesFee), networkDecimals) + : 0.0d; + } else { + // In common for Eth and Fil as calculations are the same. + gasFee = Utils.fromHexWei(isEIP1559Transaction + ? Utils.multiplyHexBN(maxFeePerGas, gasLimit) + : Utils.multiplyHexBN(gasPrice, gasLimit), + networkDecimals); + } final double gasFeeFiat = gasFee * networkSpotPrice; From c9adf52f1373c96a63354ca6dfa8d3b7504775d2 Mon Sep 17 00:00:00 2001 From: Simone Arpe Date: Mon, 19 Jun 2023 13:58:49 +0200 Subject: [PATCH 07/17] Add Filecoin address validation when sending --- .../crypto_wallet/util/Validations.java | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/util/Validations.java b/android/java/org/chromium/chrome/browser/crypto_wallet/util/Validations.java index f62e4478b55f..e585cc3f2e93 100644 --- a/android/java/org/chromium/chrome/browser/crypto_wallet/util/Validations.java +++ b/android/java/org/chromium/chrome/browser/crypto_wallet/util/Validations.java @@ -7,9 +7,12 @@ import android.content.res.Resources; +import androidx.annotation.NonNull; + import org.chromium.base.ContextUtils; import org.chromium.brave_wallet.mojom.BlockchainRegistry; import org.chromium.brave_wallet.mojom.BlockchainToken; +import org.chromium.brave_wallet.mojom.BraveWalletConstants; import org.chromium.brave_wallet.mojom.BraveWalletService; import org.chromium.brave_wallet.mojom.CoinType; import org.chromium.brave_wallet.mojom.KeyringService; @@ -32,11 +35,9 @@ public void validate(NetworkInfo selectedNetwork, KeyringService keyringService, BlockchainRegistry blockchainRegistry, BraveWalletService braveWalletService, String senderAccountAddress, String receiverAccountAddress, Callbacks.Callback2 callback) { - String senderAccountAddressLower = - senderAccountAddress.toLowerCase(Locale.getDefault()); + String senderAccountAddressLower = senderAccountAddress.toLowerCase(Locale.ENGLISH); - String receiverAccountAddressLower = - receiverAccountAddress.toLowerCase(Locale.getDefault()); + String receiverAccountAddressLower = receiverAccountAddress.toLowerCase(Locale.ENGLISH); Resources resources = ContextUtils.getApplicationContext().getResources(); @@ -55,6 +56,15 @@ public void validate(NetworkInfo selectedNetwork, KeyringService keyringService, } callback.call(responseMsg, success); }); + } else if (coinType == CoinType.FIL) { + if (receiverAccountAddress.isEmpty()) { + return; + } + final boolean showErrorMessage = !isValidFilAddress(receiverAccountAddress); + final String errorMessage = showErrorMessage + ? resources.getString(R.string.invalid_fil_address) + : ""; + callback.call(errorMessage, showErrorMessage); } else { // Steps to validate: // 1. Valid hex string @@ -71,10 +81,8 @@ public void validate(NetworkInfo selectedNetwork, KeyringService keyringService, if (!receiverAccountAddress.isEmpty() && bytesReceiverAccountAddress.length != VALID_ACCOUNT_ADDRESS_BYTE_LENGTH) { - int invalidAddressId = coinType == CoinType.FIL - ? R.string.invalid_fil_address - : R.string.wallet_not_valid_eth_address; - callback.call(resources.getString(invalidAddressId), true); + callback.call( + resources.getString(R.string.wallet_not_valid_eth_address), true); return; } @@ -120,6 +128,20 @@ public void validate(NetworkInfo selectedNetwork, KeyringService keyringService, }); } + /** + * Checks if the Filecoin contract address provided is valid. + * @param address Lowercase Filecoin contract address. + * @return {@code true} if the Filecoin contract address is valid, {@code false} otherwise. + */ + private boolean isValidFilAddress(@NonNull final String address) { + if (!address.startsWith(BraveWalletConstants.FILECOIN_MAINNET) + && !address.startsWith(BraveWalletConstants.FILECOIN_TESTNET)) { + return false; + } + // Secp256k have 41 address length and BLS keys have 86. + return (address.length() == 41 || address.length() == 86); + } + private void checkForKnowContracts(String receiverAccountAddressLower, Callbacks.Callback2 callback, Resources resources) { assert mKnownContractAddresses != null; From 8b316ae4b44885e2095acfe33f4f47b37586953c Mon Sep 17 00:00:00 2001 From: Simone Arpe Date: Mon, 19 Jun 2023 15:55:48 +0200 Subject: [PATCH 08/17] Fix Filecoin transaction URL --- .../org/chromium/chrome/browser/crypto_wallet/util/Utils.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/util/Utils.java b/android/java/org/chromium/chrome/browser/crypto_wallet/util/Utils.java index d8a15b87d85f..61a6b156d6c2 100644 --- a/android/java/org/chromium/chrome/browser/crypto_wallet/util/Utils.java +++ b/android/java/org/chromium/chrome/browser/crypto_wallet/util/Utils.java @@ -1075,6 +1075,8 @@ public static void openTransaction(TransactionInfo txInfo, AppCompatActivity act BraveWalletConstants.FILECOIN_ETHEREUM_TESTNET_CHAIN_ID); if (isFileCoinEvmNet) { openAddress("/" + txInfo.txHash, activity, coinType, networkInfo); + } else if (coinType == CoinType.FIL) { + openAddress("?cid=" + txInfo.txHash, activity, coinType, networkInfo); } else { openAddress("/tx/" + txInfo.txHash, activity, coinType, networkInfo); } @@ -1087,7 +1089,7 @@ public static void openAddress( if (blockExplorerUrl.length() > 2) { blockExplorerUrl = blockExplorerUrl.substring(1, blockExplorerUrl.length() - 1); } - if (coinType == CoinType.ETH) { + if (coinType == CoinType.ETH || coinType == CoinType.FIL) { blockExplorerUrl += toAppend; } else if (coinType == CoinType.SOL) { int iPos = blockExplorerUrl.indexOf("?cluster="); From 5678d9436d6e8be61abb0ba4d6f4d3c918e81ff9 Mon Sep 17 00:00:00 2001 From: Simone Arpe Date: Mon, 19 Jun 2023 16:03:47 +0200 Subject: [PATCH 09/17] Improve Javadoc --- .../chromium/chrome/browser/crypto_wallet/util/Utils.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/util/Utils.java b/android/java/org/chromium/chrome/browser/crypto_wallet/util/Utils.java index 61a6b156d6c2..e368ceb5f78c 100644 --- a/android/java/org/chromium/chrome/browser/crypto_wallet/util/Utils.java +++ b/android/java/org/chromium/chrome/browser/crypto_wallet/util/Utils.java @@ -429,10 +429,10 @@ public static double fromHexGWeiToGWEI(String number) { * ten raised to the power of decimals. * * @param number Number to be multiplied, represented as a string. - * @param decimals Number of decimal to multiply by + * @param decimals Number of decimals to multiply by. * @return The result of multiplying the number by ten raised to the power of decimals, - * expressed as a BigInteger. - * @throws ParseException If the input string cannot be parsed as a BigDecimal. + * expressed as a {@code BigInteger}. + * @throws ParseException If the input string cannot be parsed as a {@code BigDecimal}. * * Note:: Supposedly, when converting to Wei the result shall always end up with an * integer. From af8d1fff3d543d4f5485f3ea0f8503d6a6d729b4 Mon Sep 17 00:00:00 2001 From: Simone Arpe Date: Mon, 19 Jun 2023 16:49:02 +0200 Subject: [PATCH 10/17] Perform small refactoring --- .../chrome/browser/app/domain/SendModel.java | 19 +++++++++++++++---- .../activities/BuySendSwapActivity.java | 5 ++--- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/android/java/org/chromium/chrome/browser/app/domain/SendModel.java b/android/java/org/chromium/chrome/browser/app/domain/SendModel.java index c074d7cb645f..8f7f49438844 100644 --- a/android/java/org/chromium/chrome/browser/app/domain/SendModel.java +++ b/android/java/org/chromium/chrome/browser/app/domain/SendModel.java @@ -106,9 +106,20 @@ public void sendSolanaToken(String chainId, BlockchainToken token, String fromAd } } - public void sendFilecoinToken(@NonNull final String chainId, - @Nullable final BlockchainToken token, @NonNull final String fromAddress, - @NonNull final String toAddress, @NonNull final String amount, + /** + * Sends a given amount of Filecoins to a destination address and creates an unapproved + * transaction or notifies the callback with an error message. Coin type of blockchain token + * must be {@code CoinType.FIL}. + * @param token Filecoin blockchain token containing extra data required to create a + * transaction. + * @param fromAddress Source address that sends Filecoins. + * @param toAddress Destinations address that receives Filecoins. + * @param amount Amount of Filecoins to send. + * @param callback Callback used to notify if the transaction has been created correctly. + */ + public void sendFilecoinToken(@Nullable final BlockchainToken token, + @Nullable final String fromAddress, @NonNull final String toAddress, + @Nullable final String amount, @NonNull final Callbacks.Callback3 callback) { if (token == null) { Log.e(TAG, "Token cannot be null."); @@ -124,7 +135,7 @@ public void sendFilecoinToken(@NonNull final String chainId, } if (token.coin != CoinType.FIL) { - throw new IllegalStateException(); + throw new IllegalStateException("Coin type must be `CoinType.FIL`."); } final FilTxData filTxData = new FilTxData(); 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 24c22071eeac..9a01064bffa4 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 @@ -549,9 +549,8 @@ public void onAssetClick(WalletListItemModel asset) { setSendToFromValueValidationResult(errorMessage, false, true); }); } else if (mSelectedAccount.accountId.coin == CoinType.FIL) { - mSendModel.sendFilecoinToken(mSelectedNetwork.chainId, mCurrentBlockchainToken, - mSelectedAccount.address, to, amount, - (success, txMetaId, errorMessage) -> { + mSendModel.sendFilecoinToken(mCurrentBlockchainToken, mSelectedAccount.address, + to, amount, (success, txMetaId, errorMessage) -> { // Do nothing here when success as we will receive an // unapproved transaction in TxServiceObserver. // When we have error, let the user know, From 30eae48f9f696e84dee45f8bf32fe0436d254ab0 Mon Sep 17 00:00:00 2001 From: Simone Arpe Date: Tue, 20 Jun 2023 10:29:49 +0200 Subject: [PATCH 11/17] Add null check for parsed transactions --- .../chromium/chrome/browser/app/domain/TransactionsModel.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/java/org/chromium/chrome/browser/app/domain/TransactionsModel.java b/android/java/org/chromium/chrome/browser/app/domain/TransactionsModel.java index a687a9dfa104..e23d23b240cb 100644 --- a/android/java/org/chromium/chrome/browser/app/domain/TransactionsModel.java +++ b/android/java/org/chromium/chrome/browser/app/domain/TransactionsModel.java @@ -244,7 +244,7 @@ public void onTransactionStatusChanged(TransactionInfo txInfo) { private void updateTx( TransactionInfo txInfo, WeakReference mActivityRef) { List items = _mParsedTransactions.getValue(); - if (txInfo.txStatus == TransactionStatus.REJECTED + if (items != null && txInfo.txStatus == TransactionStatus.REJECTED && items.stream().anyMatch( walletItem -> walletItem.getTransactionInfo().id.equals(txInfo.id))) { // Remove rejected transaction @@ -253,7 +253,7 @@ private void updateTx( -> !walletItem.getTransactionInfo().id.equals(txInfo.id)) .collect(Collectors.toList()); _mParsedTransactions.postValue(items); - } else if (txInfo.txStatus != TransactionStatus.UNAPPROVED) { + } else if (items != null && txInfo.txStatus != TransactionStatus.UNAPPROVED) { // Update status WalletListItemModel walletItemTx = items.stream() From a17ff1a8d347fb895674b2bbf7bba288c6e5776a Mon Sep 17 00:00:00 2001 From: Simone Arpe Date: Tue, 20 Jun 2023 10:47:58 +0200 Subject: [PATCH 12/17] Improve comment --- .../browser/crypto_wallet/util/ParsedTransactionFees.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/util/ParsedTransactionFees.java b/android/java/org/chromium/chrome/browser/crypto_wallet/util/ParsedTransactionFees.java index ec9062c6ea5f..cfbb691956e8 100644 --- a/android/java/org/chromium/chrome/browser/crypto_wallet/util/ParsedTransactionFees.java +++ b/android/java/org/chromium/chrome/browser/crypto_wallet/util/ParsedTransactionFees.java @@ -181,7 +181,7 @@ public static double[] calcGasFee(NetworkInfo selectedNetwork, double networkSpo ? Utils.fromWei(Long.toString(solFeeEstimatesFee), networkDecimals) : 0.0d; } else { - // In common for Eth and Fil as calculations are the same. + // Gas fee calculations for Eth and Fil use the same logic. gasFee = Utils.fromHexWei(isEIP1559Transaction ? Utils.multiplyHexBN(maxFeePerGas, gasLimit) : Utils.multiplyHexBN(gasPrice, gasLimit), From e5fb736e1f7d6fdf33039c105c78de56e5b6fa1c Mon Sep 17 00:00:00 2001 From: Simone Arpe Date: Tue, 20 Jun 2023 12:02:47 +0200 Subject: [PATCH 13/17] Fix typo --- .../browser/crypto_wallet/util/ParsedTransactionFees.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/util/ParsedTransactionFees.java b/android/java/org/chromium/chrome/browser/crypto_wallet/util/ParsedTransactionFees.java index cfbb691956e8..79f7c3d86221 100644 --- a/android/java/org/chromium/chrome/browser/crypto_wallet/util/ParsedTransactionFees.java +++ b/android/java/org/chromium/chrome/browser/crypto_wallet/util/ParsedTransactionFees.java @@ -158,9 +158,9 @@ private static String calculateFilGasPrice(@NonNull final String maxFeePerGasDec } else { String result; try { - BigInteger minued = new BigInteger(maxFeePerGasDecimal); + BigInteger minuend = new BigInteger(maxFeePerGasDecimal); BigInteger subtrahend = new BigInteger(maxPriorityFeePerGasDecimal); - BigInteger gasPrice = minued.subtract(subtrahend); + BigInteger gasPrice = minuend.subtract(subtrahend); result = "0x" + gasPrice.toString(16); } catch (NumberFormatException nfe) { result = ""; From fae7ffc45480512e37d4f514a5e7a07e6e06946b Mon Sep 17 00:00:00 2001 From: Simone Arpe Date: Tue, 20 Jun 2023 13:44:37 +0200 Subject: [PATCH 14/17] Remove duplicate method --- .../browser/app/domain/KeyringModel.java | 21 +------------------ 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/android/java/org/chromium/chrome/browser/app/domain/KeyringModel.java b/android/java/org/chromium/chrome/browser/app/domain/KeyringModel.java index 333948766f2f..8edf2128203d 100644 --- a/android/java/org/chromium/chrome/browser/app/domain/KeyringModel.java +++ b/android/java/org/chromium/chrome/browser/app/domain/KeyringModel.java @@ -287,29 +287,10 @@ private void addAccountInternal(@CoinType.EnumType int coinType, result -> { handleAddAccountResult(result, callback); }); } - private static @KeyringId.EnumType int getTargetKeyringId( - @CoinType.EnumType int coinType, String chainId) { - if (coinType == CoinType.ETH) { - return KeyringId.DEFAULT; - } else if (coinType == CoinType.SOL) { - return KeyringId.SOLANA; - } else if (coinType == CoinType.FIL) { - assert chainId != null; - if (chainId.equals(BraveWalletConstants.FILECOIN_MAINNET)) { - return KeyringId.FILECOIN; - } else if (chainId.equals(BraveWalletConstants.FILECOIN_TESTNET)) { - return KeyringId.FILECOIN_TESTNET; - } - } - - assert false; - return KeyringId.DEFAULT; - } - public void addAccount(@CoinType.EnumType int coinType, String chainId, String accountName, Callbacks.Callback1 callback) { @KeyringId.EnumType - int keyringId = getTargetKeyringId(coinType, chainId); + int keyringId = AssetUtils.getKeyring(coinType, chainId); if (accountName == null) { LiveDataUtil.observeOnce(mAccountInfos, accounts -> { addAccountInternal(coinType, keyringId, From 4b8c587d7b663a58106633af062d588582aa54e2 Mon Sep 17 00:00:00 2001 From: Simone Arpe Date: Tue, 20 Jun 2023 13:45:09 +0200 Subject: [PATCH 15/17] Improve network check for Filecoins --- .../chromium/chrome/browser/app/domain/NetworkModel.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/android/java/org/chromium/chrome/browser/app/domain/NetworkModel.java b/android/java/org/chromium/chrome/browser/app/domain/NetworkModel.java index e223fbc8b182..8bff1ceb8402 100644 --- a/android/java/org/chromium/chrome/browser/app/domain/NetworkModel.java +++ b/android/java/org/chromium/chrome/browser/app/domain/NetworkModel.java @@ -26,6 +26,7 @@ import org.chromium.brave_wallet.mojom.NetworkInfo; import org.chromium.brave_wallet.mojom.OriginInfo; import org.chromium.chrome.browser.crypto_wallet.activities.BuySendSwapActivity; +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.NetworkResponsesCollector; import org.chromium.chrome.browser.crypto_wallet.util.NetworkUtils; @@ -403,7 +404,11 @@ boolean hasAccountOfNetworkType(NetworkInfo networkToBeSetAsSelected) { List accountInfos = mSharedData.getAccounts().getValue(); for (AccountInfo accountInfo : accountInfos) { if (accountInfo.accountId.coin == networkToBeSetAsSelected.coin) { - return true; + if (accountInfo.accountId.coin != CoinType.FIL + || AssetUtils.getKeyring(CoinType.FIL, networkToBeSetAsSelected.chainId) + == accountInfo.accountId.keyringId) { + return true; + } } } return false; From 9bdf4742cbb0f93c1b7bf1fe4db6307743d21edd Mon Sep 17 00:00:00 2001 From: Simone Arpe Date: Tue, 20 Jun 2023 13:59:34 +0200 Subject: [PATCH 16/17] Improve method signature --- .../org/chromium/chrome/browser/crypto_wallet/util/Utils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/util/Utils.java b/android/java/org/chromium/chrome/browser/crypto_wallet/util/Utils.java index e368ceb5f78c..db8253a83ec9 100644 --- a/android/java/org/chromium/chrome/browser/crypto_wallet/util/Utils.java +++ b/android/java/org/chromium/chrome/browser/crypto_wallet/util/Utils.java @@ -437,7 +437,7 @@ public static double fromHexGWeiToGWEI(String number) { * Note:: Supposedly, when converting to Wei the result shall always end up with an * integer. */ - public static BigInteger multiplyByDecimals(String number, int decimals) throws ParseException { + public static BigInteger multiplyByDecimals(@NonNull final String number, final int decimals) throws ParseException { NumberFormat nf = NumberFormat.getInstance(Locale.getDefault()); ParsePosition parsePosition = new ParsePosition(0); BigDecimal parsed = null; From 56105c9796e2e71845f50e13ad36c22717cfe763 Mon Sep 17 00:00:00 2001 From: Simone Arpe Date: Tue, 20 Jun 2023 14:00:25 +0200 Subject: [PATCH 17/17] Apply code formatting --- .../org/chromium/chrome/browser/crypto_wallet/util/Utils.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/android/java/org/chromium/chrome/browser/crypto_wallet/util/Utils.java b/android/java/org/chromium/chrome/browser/crypto_wallet/util/Utils.java index db8253a83ec9..5a1de9a981a2 100644 --- a/android/java/org/chromium/chrome/browser/crypto_wallet/util/Utils.java +++ b/android/java/org/chromium/chrome/browser/crypto_wallet/util/Utils.java @@ -437,7 +437,8 @@ public static double fromHexGWeiToGWEI(String number) { * Note:: Supposedly, when converting to Wei the result shall always end up with an * integer. */ - public static BigInteger multiplyByDecimals(@NonNull final String number, final int decimals) throws ParseException { + public static BigInteger multiplyByDecimals(@NonNull final String number, final int decimals) + throws ParseException { NumberFormat nf = NumberFormat.getInstance(Locale.getDefault()); ParsePosition parsePosition = new ParsePosition(0); BigDecimal parsed = null;